tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 12a2dc344ebb8ac6ffeb0ab047cbd90c504741ec
parent c4ae6453e2300a204c917ff2d119900dd51d3e94
Author: agoloman <agoloman@mozilla.com>
Date:   Thu, 23 Oct 2025 01:45:00 +0300

Revert "Bug 1995837 - Fix Moz2D for Skia m142 update. r=aosmond" for causing build bustages @SkTemplates.h.

This reverts commit c9704533beb0bcf71ab2ce7cae798cc954632a4f.

Revert "Bug 1995837 - Update mozbuilds for Skia m142. r=aosmond"

This reverts commit a63a6f3c84a4b0c0eef59caf83bff5034de1a798.

Revert "Bug 1995837 - Update Skia source files for Skia m142. r=aosmond"

This reverts commit 3610af8e547a433f7d6503632c7400cc806e3586.

Diffstat:
Mdom/canvas/DrawTargetWebgl.cpp | 2+-
Mgfx/2d/2D.h | 6+++---
Mgfx/2d/DWriteSettings.cpp | 4+---
Mgfx/2d/DrawTargetSkia.cpp | 23+++++------------------
Mgfx/2d/Factory.cpp | 8+++-----
Mgfx/2d/HelpersSkia.h | 2+-
Mgfx/2d/ScaledFontBase.cpp | 2+-
Mgfx/2d/ScaledFontDWrite.cpp | 2+-
Mgfx/2d/ScaledFontFontconfig.cpp | 9++++++++-
Mgfx/2d/Types.h | 8--------
Mgfx/skia/generate_mozbuild.py | 12+++---------
Mgfx/skia/moz.build | 10++--------
Mgfx/skia/skia/include/codec/SkCodec.h | 25++-----------------------
Dgfx/skia/skia/include/codec/SkPngRustDecoder.h | 30------------------------------
Dgfx/skia/skia/include/config/MODULE.bazel | 5-----
Mgfx/skia/skia/include/config/SkUserConfig.h | 11-----------
Mgfx/skia/skia/include/config/copts.bzl | 5++---
Mgfx/skia/skia/include/core/SkBitmap.h | 2+-
Dgfx/skia/skia/include/core/SkCPUContext.h | 31-------------------------------
Dgfx/skia/skia/include/core/SkCPURecorder.h | 74--------------------------------------------------------------------------
Mgfx/skia/skia/include/core/SkCanvas.h | 72+++++++++++++-----------------------------------------------------------
Mgfx/skia/skia/include/core/SkColorSpace.h | 23++++++++++++-----------
Mgfx/skia/skia/include/core/SkContourMeasure.h | 10+++-------
Mgfx/skia/skia/include/core/SkData.h | 100+++++++++++++++++++++++++++++--------------------------------------------------
Mgfx/skia/skia/include/core/SkExecutor.h | 18------------------
Mgfx/skia/skia/include/core/SkFont.h | 202+++++++++++++++++++++++++++++++++----------------------------------------------
Mgfx/skia/skia/include/core/SkImage.h | 89++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------
Mgfx/skia/skia/include/core/SkImageGenerator.h | 8+++++---
Mgfx/skia/skia/include/core/SkMatrix.h | 420++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Mgfx/skia/skia/include/core/SkMilestone.h | 2+-
Mgfx/skia/skia/include/core/SkPath.h | 734++++++++++++++++++++++++++++++++++---------------------------------------------
Mgfx/skia/skia/include/core/SkPathBuilder.h | 807+++++--------------------------------------------------------------------------
Mgfx/skia/skia/include/core/SkPathEffect.h | 17+++++------------
Dgfx/skia/skia/include/core/SkPathIter.h | 90-------------------------------------------------------------------------------
Mgfx/skia/skia/include/core/SkPathMeasure.h | 6+-----
Mgfx/skia/skia/include/core/SkPathTypes.h | 16++++------------
Mgfx/skia/skia/include/core/SkPathUtils.h | 33+++++++++++++--------------------
Mgfx/skia/skia/include/core/SkPictureRecorder.h | 12++++++------
Mgfx/skia/skia/include/core/SkRRect.h | 18+++++++++++-------
Dgfx/skia/skia/include/core/SkRecorder.h | 47-----------------------------------------------
Mgfx/skia/skia/include/core/SkRect.h | 125++++++++++++++++++++++---------------------------------------------------------
Mgfx/skia/skia/include/core/SkRegion.h | 19++++---------------
Mgfx/skia/skia/include/core/SkScalar.h | 12++++++++++++
Mgfx/skia/skia/include/core/SkShader.h | 27+++++++--------------------
Mgfx/skia/skia/include/core/SkStrokeRec.h | 3+--
Mgfx/skia/skia/include/core/SkSurface.h | 9+--------
Mgfx/skia/skia/include/core/SkTextBlob.h | 56++++++--------------------------------------------------
Mgfx/skia/skia/include/core/SkTraceMemoryDump.h | 7-------
Mgfx/skia/skia/include/core/SkTypeface.h | 97+++++++++++++++++++++++++++++--------------------------------------------------
Mgfx/skia/skia/include/core/SkTypes.h | 10----------
Mgfx/skia/skia/include/effects/SkDashPathEffect.h | 12+++---------
Mgfx/skia/skia/include/effects/SkRuntimeEffect.h | 2+-
Mgfx/skia/skia/include/encode/SkEncoder.h | 2+-
Mgfx/skia/skia/include/encode/SkICC.h | 2+-
Mgfx/skia/skia/include/encode/SkJpegEncoder.h | 15++++++++++-----
Mgfx/skia/skia/include/encode/SkPngEncoder.h | 15+++++++--------
Dgfx/skia/skia/include/encode/SkPngRustEncoder.h | 88-------------------------------------------------------------------------------
Mgfx/skia/skia/include/encode/SkWebpEncoder.h | 15++++++++++-----
Mgfx/skia/skia/include/gpu/ganesh/GrDirectContext.h | 23-----------------------
Mgfx/skia/skia/include/gpu/ganesh/GrRecordingContext.h | 12------------
Mgfx/skia/skia/include/gpu/graphite/BackendSemaphore.h | 7++-----
Mgfx/skia/skia/include/gpu/graphite/BackendTexture.h | 7++-----
Mgfx/skia/skia/include/gpu/graphite/Context.h | 57++++++++++-----------------------------------------------
Mgfx/skia/skia/include/gpu/graphite/ContextOptions.h | 52++++------------------------------------------------
Mgfx/skia/skia/include/gpu/graphite/GraphiteTypes.h | 98+++----------------------------------------------------------------------------
Mgfx/skia/skia/include/gpu/graphite/PrecompileContext.h | 18++----------------
Mgfx/skia/skia/include/gpu/graphite/Recorder.h | 59+++++++++++++++--------------------------------------------
Mgfx/skia/skia/include/gpu/graphite/Recording.h | 13++++++-------
Mgfx/skia/skia/include/gpu/graphite/TextureInfo.h | 6+++---
Mgfx/skia/skia/include/gpu/graphite/mtl/MtlBackendContext.h | 2--
Mgfx/skia/skia/include/gpu/graphite/precompile/PaintOptions.h | 25+++++++------------------
Mgfx/skia/skia/include/gpu/graphite/precompile/PrecompileBase.h | 12++++++++++--
Mgfx/skia/skia/include/gpu/graphite/precompile/PrecompileImageFilter.h | 7++++++-
Mgfx/skia/skia/include/gpu/graphite/precompile/PrecompileMaskFilter.h | 6+++++-
Mgfx/skia/skia/include/gpu/graphite/precompile/PrecompileShader.h | 121+++++++++++--------------------------------------------------------------------
Dgfx/skia/skia/include/gpu/graphite/vk/precompile/VulkanPrecompileShader.h | 39---------------------------------------
Mgfx/skia/skia/include/gpu/vk/VulkanBackendContext.h | 2+-
Dgfx/skia/skia/include/gpu/vk/VulkanPreferredFeatures.h | 217-------------------------------------------------------------------------------
Mgfx/skia/skia/include/pathops/SkPathOps.h | 69++++++++++++++++++++-------------------------------------------------
Mgfx/skia/skia/include/ports/SkFontMgr_FontConfigInterface.h | 3+--
Mgfx/skia/skia/include/ports/SkFontMgr_android.h | 4++++
Mgfx/skia/skia/include/ports/SkFontMgr_android_ndk.h | 6++----
Mgfx/skia/skia/include/ports/SkFontMgr_fontconfig.h | 2+-
Mgfx/skia/skia/include/ports/SkTypeface_cairo.h | 4+++-
Mgfx/skia/skia/include/private/SkEncodedInfo.h | 127++++++++++++++++++++++++++++++++++---------------------------------------------
Mgfx/skia/skia/include/private/SkGainmapShader.h | 14++++----------
Dgfx/skia/skia/include/private/SkHdrMetadata.h | 173-------------------------------------------------------------------------------
Mgfx/skia/skia/include/private/SkPathRef.h | 265+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Mgfx/skia/skia/include/private/base/SkAlign.h | 11-----------
Mgfx/skia/skia/include/private/base/SkTemplates.h | 106++++++++++++++++++++++++-------------------------------------------------------
Mgfx/skia/skia/include/private/gpu/ganesh/GrTypesPriv.h | 2+-
Mgfx/skia/skia/include/utils/SkParsePath.h | 10+---------
Mgfx/skia/skia/modules/skcms/README.chromium | 1+
Mgfx/skia/skia/modules/skcms/skcms.cc | 240+++++++++++--------------------------------------------------------------------
Dgfx/skia/skia/modules/skcms/skcms.gni | 43-------------------------------------------
Mgfx/skia/skia/modules/skcms/src/Transform_inl.h | 60+++++++-----------------------------------------------------
Mgfx/skia/skia/modules/skcms/src/skcms_Transform.h | 2--
Mgfx/skia/skia/modules/skcms/src/skcms_internals.h | 20++------------------
Mgfx/skia/skia/modules/skcms/src/skcms_public.h | 49++++++++++---------------------------------------
Mgfx/skia/skia/modules/skcms/version.sha1 | 2+-
Mgfx/skia/skia/src/base/SkArenaAlloc.h | 1-
Mgfx/skia/skia/src/base/SkCubics.cpp | 2+-
Mgfx/skia/skia/src/base/SkSafeMath.h | 9---------
Mgfx/skia/skia/src/base/SkTLazy.h | 8--------
Mgfx/skia/skia/src/base/SkTime.cpp | 2+-
Mgfx/skia/skia/src/base/SkVx.h | 1-
Mgfx/skia/skia/src/codec/SkCodec.cpp | 51+++++++++++++++++++++------------------------------
Mgfx/skia/skia/src/codec/SkImageGenerator_FromEncoded.cpp | 2+-
Mgfx/skia/skia/src/core/SkAAClip.cpp | 6++----
Mgfx/skia/skia/src/core/SkATrace.h | 2+-
Mgfx/skia/skia/src/core/SkAnalyticEdge.cpp | 418++++++++++++++-----------------------------------------------------------------
Mgfx/skia/skia/src/core/SkAnalyticEdge.h | 38+++++++++++---------------------------
Mgfx/skia/skia/src/core/SkAutoBlitterChoose.h | 30+++++++++++++-----------------
Mgfx/skia/skia/src/core/SkBitmap.cpp | 4++--
Mgfx/skia/skia/src/core/SkBitmapDevice.cpp | 128++++++++++++++++++++++++-------------------------------------------------------
Mgfx/skia/skia/src/core/SkBitmapDevice.h | 38+++++++++++++-------------------------
Mgfx/skia/skia/src/core/SkBitmapProcState.cpp | 39+++++++++++++++++++--------------------
Mgfx/skia/skia/src/core/SkBitmapProcState.h | 73+++++++++++++++++++++++++++++++++++++++++--------------------------------
Mgfx/skia/skia/src/core/SkBitmapProcState_matrixProcs.cpp | 85+++++++++++++++++++++++++++++++++----------------------------------------------
Mgfx/skia/skia/src/core/SkBlitter.cpp | 11++++++-----
Mgfx/skia/skia/src/core/SkBlitter.h | 15++++-----------
Mgfx/skia/skia/src/core/SkBlitter_A8.cpp | 24+++++++-----------------
Mgfx/skia/skia/src/core/SkBlitter_A8.h | 15+++------------
Mgfx/skia/skia/src/core/SkBlitter_ARGB32.cpp | 6------
Mgfx/skia/skia/src/core/SkBlurMaskFilterImpl.cpp | 120++++++++++++++++++++++++++++++++-----------------------------------------------
Mgfx/skia/skia/src/core/SkBlurMaskFilterImpl.h | 15+++++----------
Dgfx/skia/skia/src/core/SkCPUContext.cpp | 32--------------------------------
Dgfx/skia/skia/src/core/SkCPUContextImpl.h | 23-----------------------
Dgfx/skia/skia/src/core/SkCPURecorder.cpp | 21---------------------
Dgfx/skia/skia/src/core/SkCPURecorderImpl.h | 32--------------------------------
Mgfx/skia/skia/src/core/SkCanvas.cpp | 178++++++++++++++++++++++++++-----------------------------------------------------
Mgfx/skia/skia/src/core/SkCanvasPriv.cpp | 18+++++++-----------
Mgfx/skia/skia/src/core/SkClipStack.cpp | 71++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
Mgfx/skia/skia/src/core/SkClipStack.h | 9++++++---
Mgfx/skia/skia/src/core/SkClipStackDevice.cpp | 13+++++++------
Mgfx/skia/skia/src/core/SkColorSpace.cpp | 292+++++++++++++++++++++++++++++++------------------------------------------------
Mgfx/skia/skia/src/core/SkColorSpaceXformSteps.cpp | 168+++++++++----------------------------------------------------------------------
Mgfx/skia/skia/src/core/SkColorSpaceXformSteps.h | 8--------
Mgfx/skia/skia/src/core/SkCompressedDataUtils.cpp | 2+-
Mgfx/skia/skia/src/core/SkContourMeasure.cpp | 25++++++-------------------
Mgfx/skia/skia/src/core/SkCoreBlitters.h | 5+----
Mgfx/skia/skia/src/core/SkData.cpp | 97+++++++++++++++++++++++++++++++++++++------------------------------------------
Mgfx/skia/skia/src/core/SkDevice.cpp | 66+++++++++++++++++++++++++++++++++---------------------------------
Mgfx/skia/skia/src/core/SkDevice.h | 13++++++-------
Mgfx/skia/skia/src/core/SkDraw.cpp | 941++++++-------------------------------------------------------------------------
Mgfx/skia/skia/src/core/SkDraw.h | 242++++++++++---------------------------------------------------------------------
Agfx/skia/skia/src/core/SkDrawBase.cpp | 772+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agfx/skia/skia/src/core/SkDrawBase.h | 167+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mgfx/skia/skia/src/core/SkDrawProcs.h | 13+++++--------
Mgfx/skia/skia/src/core/SkDrawShadowInfo.cpp | 13++++++++-----
Mgfx/skia/skia/src/core/SkDraw_atlas.cpp | 73++++++++++++++++++++++++++++++++-----------------------------------------
Mgfx/skia/skia/src/core/SkDraw_text.cpp | 16+++++++---------
Mgfx/skia/skia/src/core/SkDraw_vertices.cpp | 56++++++++++++++++++++++++++++----------------------------
Mgfx/skia/skia/src/core/SkEdge.cpp | 534+++++++++++++++++++++++++++++++++++--------------------------------------------
Mgfx/skia/skia/src/core/SkEdge.h | 162+++++++++++++++++++++++--------------------------------------------------------
Mgfx/skia/skia/src/core/SkEdgeBuilder.cpp | 111+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
Mgfx/skia/skia/src/core/SkEdgeBuilder.h | 29+++++++++++------------------
Mgfx/skia/skia/src/core/SkEdgeClipper.cpp | 60+++++++++++++++++++++++++++++++-----------------------------
Mgfx/skia/skia/src/core/SkEdgeClipper.h | 19++++++++-----------
Mgfx/skia/skia/src/core/SkEffectPriv.h | 5-----
Mgfx/skia/skia/src/core/SkExecutor.cpp | 94+++++++++++++------------------------------------------------------------------
Mgfx/skia/skia/src/core/SkFDot6.h | 13++++++-------
Mgfx/skia/skia/src/core/SkFont.cpp | 119+++++++++++++++++++++++++++++++++++++------------------------------------------
Mgfx/skia/skia/src/core/SkFontDescriptor.cpp | 7+------
Mgfx/skia/skia/src/core/SkFontDescriptor.h | 1-
Mgfx/skia/skia/src/core/SkFontMgr.cpp | 4++--
Mgfx/skia/skia/src/core/SkFontPriv.h | 27++++++++++++++-------------
Mgfx/skia/skia/src/core/SkFontStream.cpp | 11++++++-----
Mgfx/skia/skia/src/core/SkFontStream.h | 3+--
Mgfx/skia/skia/src/core/SkGeometry.cpp | 31+++++++++++--------------------
Mgfx/skia/skia/src/core/SkGeometry.h | 24++++++++++++------------
Mgfx/skia/skia/src/core/SkGlyph.cpp | 71+++++++++++++++++++++++++++++++++++++++--------------------------------
Mgfx/skia/skia/src/core/SkGlyphRunPainter.cpp | 29+++++++++++++----------------
Mgfx/skia/skia/src/core/SkGlyphRunPainter.h | 41++++++++++++++++++++++++++---------------
Mgfx/skia/skia/src/core/SkGraphics.cpp | 21---------------------
Mgfx/skia/skia/src/core/SkImageFilter.cpp | 2+-
Mgfx/skia/skia/src/core/SkImageFilterCache.cpp | 9+++++----
Mgfx/skia/skia/src/core/SkImageFilterTypes.cpp | 127+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Mgfx/skia/skia/src/core/SkImageFilterTypes.h | 27+++++++++++----------------
Mgfx/skia/skia/src/core/SkLRUCache.h | 30+++++++++++++-----------------
Mgfx/skia/skia/src/core/SkLineClipper.cpp | 2+-
Mgfx/skia/skia/src/core/SkLocalMatrixImageFilter.cpp | 8+++++---
Mgfx/skia/skia/src/core/SkMaskFilterBase.cpp | 98++++++++++++++++++++++++++-----------------------------------------------------
Mgfx/skia/skia/src/core/SkMaskFilterBase.h | 57++++++++++++++-------------------------------------------
Mgfx/skia/skia/src/core/SkMatrix.cpp | 373+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
Mgfx/skia/skia/src/core/SkMatrixPriv.h | 26+++++++++++++-------------
Mgfx/skia/skia/src/core/SkMesh.cpp | 2+-
Mgfx/skia/skia/src/core/SkOverdrawCanvas.cpp | 11+++++------
Mgfx/skia/skia/src/core/SkPath.cpp | 1459++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Mgfx/skia/skia/src/core/SkPathBuilder.cpp | 649+++++++++++++++++++++++++++----------------------------------------------------
Mgfx/skia/skia/src/core/SkPathEffect.cpp | 41+++++++++++++++++------------------------
Mgfx/skia/skia/src/core/SkPathEffectBase.h | 29++++++++++++++++++-----------
Mgfx/skia/skia/src/core/SkPathEnums.h | 72+++---------------------------------------------------------------------
Dgfx/skia/skia/src/core/SkPathIter.cpp | 79-------------------------------------------------------------------------------
Mgfx/skia/skia/src/core/SkPathMeasure.cpp | 16+---------------
Dgfx/skia/skia/src/core/SkPathPriv.cpp | 256-------------------------------------------------------------------------------
Mgfx/skia/skia/src/core/SkPathPriv.h | 320++++++++++++++++++++++++++++++++++++-------------------------------------------
Dgfx/skia/skia/src/core/SkPathRaw.cpp | 37-------------------------------------
Dgfx/skia/skia/src/core/SkPathRaw.h | 55-------------------------------------------------------
Dgfx/skia/skia/src/core/SkPathRawShapes.cpp | 202-------------------------------------------------------------------------------
Dgfx/skia/skia/src/core/SkPathRawShapes.h | 58----------------------------------------------------------
Mgfx/skia/skia/src/core/SkPathRef.cpp | 328+++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
Mgfx/skia/skia/src/core/SkPathUtils.cpp | 72++++++++++++++++++++++++++++++++++++++----------------------------------
Mgfx/skia/skia/src/core/SkPath_serial.cpp | 82++++++++++++++++++++++++++-----------------------------------------------------
Mgfx/skia/skia/src/core/SkPictureData.cpp | 5++---
Mgfx/skia/skia/src/core/SkPicturePlayback.cpp | 8++------
Mgfx/skia/skia/src/core/SkPicturePriv.h | 5+----
Mgfx/skia/skia/src/core/SkPictureRecorder.cpp | 4++--
Mgfx/skia/skia/src/core/SkPixmapDraw.cpp | 3+--
Mgfx/skia/skia/src/core/SkRRect.cpp | 49-------------------------------------------------
Mgfx/skia/skia/src/core/SkRSXform.cpp | 2+-
Mgfx/skia/skia/src/core/SkRasterClip.cpp | 5+++--
Mgfx/skia/skia/src/core/SkRasterPipeline.cpp | 2+-
Mgfx/skia/skia/src/core/SkRasterPipelineBlitter.cpp | 96+++++++++----------------------------------------------------------------------
Mgfx/skia/skia/src/core/SkRasterPipelineOpContexts.h | 4++--
Mgfx/skia/skia/src/core/SkRasterPipelineOpList.h | 3+--
Mgfx/skia/skia/src/core/SkReadBuffer.cpp | 46+++++++++++++++++++---------------------------
Mgfx/skia/skia/src/core/SkReadBuffer.h | 14++++++--------
Mgfx/skia/skia/src/core/SkRecord.h | 5++---
Dgfx/skia/skia/src/core/SkRecordCanvas.cpp | 446-------------------------------------------------------------------------------
Dgfx/skia/skia/src/core/SkRecordCanvas.h | 199-------------------------------------------------------------------------------
Mgfx/skia/skia/src/core/SkRecordDraw.cpp | 31+++++++++++++++----------------
Mgfx/skia/skia/src/core/SkRecordOpts.cpp | 2+-
Mgfx/skia/skia/src/core/SkRecordedDrawable.h | 2+-
Agfx/skia/skia/src/core/SkRecorder.cpp | 419+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agfx/skia/skia/src/core/SkRecorder.h | 176+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mgfx/skia/skia/src/core/SkRecords.h | 2+-
Mgfx/skia/skia/src/core/SkRect.cpp | 117+++++++++++++++++++++++++++++---------------------------------------------------
Mgfx/skia/skia/src/core/SkRegion_path.cpp | 70++++++++++++++++++++++++++--------------------------------------------
Mgfx/skia/skia/src/core/SkResourceCache.cpp | 60+++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
Mgfx/skia/skia/src/core/SkResourceCache.h | 30+++++++++++++++---------------
Mgfx/skia/skia/src/core/SkRuntimeEffect.cpp | 4++--
Mgfx/skia/skia/src/core/SkRuntimeEffectPriv.h | 7+------
Mgfx/skia/skia/src/core/SkScalar.cpp | 9+++++----
Mgfx/skia/skia/src/core/SkScalerContext.cpp | 192++++++++++++++++++++++++++++++++++++++-----------------------------------------
Mgfx/skia/skia/src/core/SkScalerContext.h | 34++++++++++++++++------------------
Mgfx/skia/skia/src/core/SkScan.h | 28++++++++++++++--------------
Mgfx/skia/skia/src/core/SkScanPriv.h | 4++++
Mgfx/skia/skia/src/core/SkScan_AAAPath.cpp | 48++++++++++++++++++++++++------------------------
Mgfx/skia/skia/src/core/SkScan_AntiPath.cpp | 31+++++++++++++++++++++++--------
Mgfx/skia/skia/src/core/SkScan_Antihair.cpp | 11-----------
Mgfx/skia/skia/src/core/SkScan_Hairline.cpp | 188+++++++++++++++++++++++++++++--------------------------------------------------
Mgfx/skia/skia/src/core/SkScan_Path.cpp | 162++++++++++++++++++++++++++++++++++++++++---------------------------------------
Mgfx/skia/skia/src/core/SkSpecialImage.cpp | 32+++++++++++++++++++++++++++++---
Mgfx/skia/skia/src/core/SkStreamPriv.h | 26+-------------------------
Mgfx/skia/skia/src/core/SkStrike.cpp | 3+++
Mgfx/skia/skia/src/core/SkStrikeSpec.cpp | 6+++---
Mgfx/skia/skia/src/core/SkStroke.cpp | 190+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Mgfx/skia/skia/src/core/SkStroke.h | 5++---
Mgfx/skia/skia/src/core/SkStrokeRec.cpp | 4++--
Mgfx/skia/skia/src/core/SkStrokerPriv.cpp | 51++++++++++++++++++++++++---------------------------
Mgfx/skia/skia/src/core/SkStrokerPriv.h | 8++++----
Dgfx/skia/skia/src/core/SkSynchronizedResourceCache.cpp | 81-------------------------------------------------------------------------------
Dgfx/skia/skia/src/core/SkSynchronizedResourceCache.h | 49-------------------------------------------------
Mgfx/skia/skia/src/core/SkTMultiMap.h | 22+++++++---------------
Mgfx/skia/skia/src/core/SkTaskGroup.cpp | 16+++-------------
Mgfx/skia/skia/src/core/SkTaskGroup.h | 3---
Mgfx/skia/skia/src/core/SkTextBlob.cpp | 60++++++++++++++++++++++++++++++------------------------------
Mgfx/skia/skia/src/core/SkTraceEvent.h | 2+-
Mgfx/skia/skia/src/core/SkTraceEventCommon.h | 39---------------------------------------
Mgfx/skia/skia/src/core/SkTypeface.cpp | 82++++++++++++++++++++++++++++++++++++++++---------------------------------------
Mgfx/skia/skia/src/core/SkTypefaceCache.cpp | 2--
Mgfx/skia/skia/src/core/SkTypeface_remote.cpp | 5+++--
Mgfx/skia/skia/src/core/SkTypeface_remote.h | 17+++++++++--------
Mgfx/skia/skia/src/core/SkVertices.cpp | 3+--
Mgfx/skia/skia/src/core/SkWriteBuffer.cpp | 35++++++++++++++++++-----------------
Mgfx/skia/skia/src/core/SkWriteBuffer.h | 21++++++++++-----------
Mgfx/skia/skia/src/effects/Sk1DPathEffect.cpp | 88+++++++++++++++++++++++++++++++++++++------------------------------------------
Mgfx/skia/skia/src/effects/Sk2DPathEffect.cpp | 37+++++++++++++++++++------------------
Mgfx/skia/skia/src/effects/SkCornerPathEffect.cpp | 47+++++++++++++++++++++++++----------------------
Mgfx/skia/skia/src/effects/SkDashImpl.h | 20+++++++++-----------
Mgfx/skia/skia/src/effects/SkDashPathEffect.cpp | 67++++++++++++++++++++++++++++++++++++++++---------------------------
Mgfx/skia/skia/src/effects/SkDiscretePathEffect.cpp | 5++---
Mgfx/skia/skia/src/effects/SkEmbossMaskFilter.cpp | 84++-----------------------------------------------------------------------------
Mgfx/skia/skia/src/effects/SkEmbossMaskFilter.h | 30+-----------------------------
Mgfx/skia/skia/src/effects/SkShaderMaskFilterImpl.cpp | 8--------
Mgfx/skia/skia/src/effects/SkShaderMaskFilterImpl.h | 4----
Mgfx/skia/skia/src/effects/SkTableMaskFilter.cpp | 10+++-------
Mgfx/skia/skia/src/effects/SkTrimPE.h | 2+-
Mgfx/skia/skia/src/effects/SkTrimPathEffect.cpp | 5++---
Mgfx/skia/skia/src/effects/colorfilters/SkColorFilterBase.cpp | 8+-------
Mgfx/skia/skia/src/effects/colorfilters/SkMatrixColorFilter.cpp | 4++--
Mgfx/skia/skia/src/effects/colorfilters/SkWorkingFormatColorFilter.cpp | 11+++++------
Mgfx/skia/skia/src/effects/imagefilters/SkBlurImageFilter.cpp | 84+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
Mgfx/skia/skia/src/effects/imagefilters/SkCropImageFilter.cpp | 2+-
Mgfx/skia/skia/src/effects/imagefilters/SkDisplacementMapImageFilter.cpp | 2+-
Mgfx/skia/skia/src/effects/imagefilters/SkImageImageFilter.cpp | 2+-
Mgfx/skia/skia/src/effects/imagefilters/SkLightingImageFilter.cpp | 19++-----------------
Mgfx/skia/skia/src/effects/imagefilters/SkMatrixConvolutionImageFilter.cpp | 6+++---
Mgfx/skia/skia/src/effects/imagefilters/SkRuntimeImageFilter.cpp | 2+-
Mgfx/skia/skia/src/encode/SkICC.cpp | 22+++++++++-------------
Mgfx/skia/skia/src/encode/SkImageEncoderFns.h | 192+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Mgfx/skia/skia/src/encode/SkJpegEncoderImpl.cpp | 14+++++++-------
Mgfx/skia/skia/src/encode/SkPngEncoderBase.cpp | 268+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Mgfx/skia/skia/src/encode/SkPngEncoderBase.h | 21++++++++++-----------
Mgfx/skia/skia/src/encode/SkPngEncoderImpl.cpp | 196++++++++++++++++++++++++++++++-------------------------------------------------
Dgfx/skia/skia/src/encode/SkPngRustEncoder.cpp | 28----------------------------
Dgfx/skia/skia/src/encode/SkPngRustEncoderImpl.cpp | 273-------------------------------------------------------------------------------
Dgfx/skia/skia/src/encode/SkPngRustEncoderImpl.h | 80-------------------------------------------------------------------------------
Mgfx/skia/skia/src/encode/SkWebpEncoderImpl.cpp | 14+++++++-------
Mgfx/skia/skia/src/image/SkImage.cpp | 15+--------------
Mgfx/skia/skia/src/image/SkImage_Base.cpp | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Mgfx/skia/skia/src/image/SkImage_Base.h | 48+++++++++++++++++++++++++++++++++++-------------
Mgfx/skia/skia/src/image/SkImage_Lazy.cpp | 45+++++++++++++++++++++++----------------------
Mgfx/skia/skia/src/image/SkImage_Lazy.h | 23++++++++++++-----------
Mgfx/skia/skia/src/image/SkImage_Picture.cpp | 19+++++++++++++------
Mgfx/skia/skia/src/image/SkImage_Picture.h | 8++++++--
Mgfx/skia/skia/src/image/SkImage_Raster.cpp | 40+++++++++++++++++++++-------------------
Mgfx/skia/skia/src/image/SkImage_Raster.h | 30+++++++++++++-----------------
Mgfx/skia/skia/src/image/SkPictureImageGenerator.cpp | 4++--
Mgfx/skia/skia/src/image/SkPictureImageGenerator.h | 9+++++----
Mgfx/skia/skia/src/image/SkSurface.cpp | 2--
Mgfx/skia/skia/src/image/SkSurface_Base.cpp | 3+--
Mgfx/skia/skia/src/image/SkSurface_Base.h | 20+++-----------------
Mgfx/skia/skia/src/image/SkSurface_Raster.cpp | 77+++++++++++------------------------------------------------------------------
Mgfx/skia/skia/src/image/SkSurface_Raster.h | 25+++++--------------------
Mgfx/skia/skia/src/opts/SkBlitMask_opts.h | 36+++++++++++++++---------------------
Mgfx/skia/skia/src/opts/SkRasterPipeline_opts.h | 208+++++++++++++++++++-------------------------------------------------------------
Mgfx/skia/skia/src/pathops/SkOpAngle.cpp | 2+-
Mgfx/skia/skia/src/pathops/SkOpBuilder.cpp | 52++++++++++++++++++++++++++++------------------------
Mgfx/skia/skia/src/pathops/SkOpEdgeBuilder.h | 10++++++++++
Mgfx/skia/skia/src/pathops/SkOpSegment.h | 2+-
Mgfx/skia/skia/src/pathops/SkPathOpsAsWinding.cpp | 114+++++++++++++++++++++++++++++++++++++++----------------------------------------
Mgfx/skia/skia/src/pathops/SkPathOpsCommon.h | 6+-----
Mgfx/skia/skia/src/pathops/SkPathOpsCubic.cpp | 6+++---
Mgfx/skia/skia/src/pathops/SkPathOpsOp.cpp | 50+++++++++++++++++++++++++++-----------------------
Mgfx/skia/skia/src/pathops/SkPathOpsQuad.cpp | 2+-
Mgfx/skia/skia/src/pathops/SkPathOpsSimplify.cpp | 66++++++++++++++++++++++++++++++++++++++----------------------------
Mgfx/skia/skia/src/pathops/SkPathWriter.cpp | 28++++++++++++++--------------
Mgfx/skia/skia/src/pathops/SkPathWriter.h | 7+++----
Mgfx/skia/skia/src/pdf/SkKeyedImage.cpp | 2+-
Mgfx/skia/skia/src/pdf/SkPDFBitmap.cpp | 79++++++++++++++++++++++++++++++++-----------------------------------------------
Mgfx/skia/skia/src/pdf/SkPDFBitmap.h | 4+---
Mgfx/skia/skia/src/pdf/SkPDFDevice.cpp | 247+++++++++++++++++++++++++++++++++++--------------------------------------------
Mgfx/skia/skia/src/pdf/SkPDFDevice.h | 15+++++----------
Mgfx/skia/skia/src/pdf/SkPDFDocument.cpp | 4++--
Mgfx/skia/skia/src/pdf/SkPDFDocumentPriv.h | 1-
Mgfx/skia/skia/src/pdf/SkPDFFont.cpp | 28++++++++++++++++------------
Mgfx/skia/skia/src/pdf/SkPDFGradientShader.cpp | 226+++++++++++++++++++++++++++++++------------------------------------------------
Mgfx/skia/skia/src/pdf/SkPDFGradientShader.h | 2+-
Mgfx/skia/skia/src/pdf/SkPDFGraphicStackState.cpp | 14+++++++-------
Mgfx/skia/skia/src/pdf/SkPDFMakeToUnicodeCmap.cpp | 7+++----
Mgfx/skia/skia/src/pdf/SkPDFTag.cpp | 18++++--------------
Mgfx/skia/skia/src/pdf/SkPDFTypes.cpp | 4----
Mgfx/skia/skia/src/pdf/SkPDFTypes.h | 1-
Mgfx/skia/skia/src/pdf/SkPDFUtils.cpp | 51++++++++++++++++++++++++++++-----------------------
Mgfx/skia/skia/src/pdf/SkPDFUtils.h | 4++--
Mgfx/skia/skia/src/ports/SkFontConfigInterface_direct.cpp | 16++++------------
Mgfx/skia/skia/src/ports/SkFontHost_FreeType.cpp | 151++++++++++++++++++++++++++++++++-----------------------------------------------
Mgfx/skia/skia/src/ports/SkFontHost_FreeType_common.cpp | 132+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Mgfx/skia/skia/src/ports/SkFontHost_FreeType_common.h | 5++---
Mgfx/skia/skia/src/ports/SkFontHost_cairo.cpp | 78++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
Mgfx/skia/skia/src/ports/SkFontHost_win.cpp | 116++++++++++++++++++++++++++++++++++++-------------------------------------------
Mgfx/skia/skia/src/ports/SkFontMgr_FontConfigInterface.cpp | 17+++++++++++------
Mgfx/skia/skia/src/ports/SkFontMgr_android.cpp | 80+++++++++++++++++++++++++++++++++----------------------------------------------
Mgfx/skia/skia/src/ports/SkFontMgr_android_ndk.cpp | 1013++++++++++++-------------------------------------------------------------------
Mgfx/skia/skia/src/ports/SkFontMgr_android_parser.cpp | 87+++++++++++++++++++++++++++++++++++--------------------------------------------
Mgfx/skia/skia/src/ports/SkFontMgr_android_parser.h | 8++------
Mgfx/skia/skia/src/ports/SkFontMgr_fontconfig.cpp | 14++++++++++----
Mgfx/skia/skia/src/ports/SkFontMgr_fuchsia.cpp | 4+---
Mgfx/skia/skia/src/ports/SkFontScanner_fontations.cpp | 3++-
Mgfx/skia/skia/src/ports/SkImageEncoder_NDK.cpp | 33+++++++++++++++------------------
Mgfx/skia/skia/src/ports/SkScalerContext_mac_ct.cpp | 16+++++++++-------
Mgfx/skia/skia/src/ports/SkScalerContext_mac_ct.h | 2+-
Mgfx/skia/skia/src/ports/SkScalerContext_win_dw.cpp | 79++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Mgfx/skia/skia/src/ports/SkScalerContext_win_dw.h | 2+-
Mgfx/skia/skia/src/ports/SkTypeface_FreeType.h | 17+++++++++--------
Mgfx/skia/skia/src/ports/SkTypeface_fontations.cpp | 361+++++++++++++++++++++++++++++++++++++------------------------------------------
Mgfx/skia/skia/src/ports/SkTypeface_fontations_priv.h | 13+++++++------
Mgfx/skia/skia/src/ports/SkTypeface_mac_ct.cpp | 48+++++++++++++++++++++++-------------------------
Mgfx/skia/skia/src/ports/SkTypeface_mac_ct.h | 13+++++++------
Mgfx/skia/skia/src/ports/SkTypeface_proxy.cpp | 32+++++++++++++++-----------------
Mgfx/skia/skia/src/ports/SkTypeface_proxy.h | 20+++++++++++---------
Mgfx/skia/skia/src/ports/SkTypeface_win_dw.cpp | 29+++++++++++++----------------
Mgfx/skia/skia/src/ports/SkTypeface_win_dw.h | 13+++++++------
Mgfx/skia/skia/src/shaders/SkBitmapProcShader.cpp | 7++++---
Mgfx/skia/skia/src/shaders/SkColorShader.h | 7+------
Mgfx/skia/skia/src/shaders/SkGainmapShader.cpp | 42+++++++++++++++++++++++-------------------
Mgfx/skia/skia/src/shaders/SkImageShader.cpp | 40+++++++++-------------------------------
Mgfx/skia/skia/src/shaders/SkImageShader.h | 4++--
Mgfx/skia/skia/src/shaders/SkLocalMatrixShader.cpp | 8++++----
Mgfx/skia/skia/src/shaders/SkLocalMatrixShader.h | 4++--
Mgfx/skia/skia/src/shaders/SkPictureShader.cpp | 5++---
Mgfx/skia/skia/src/shaders/SkRuntimeShader.cpp | 6+++---
Mgfx/skia/skia/src/shaders/SkShader.cpp | 7+++----
Mgfx/skia/skia/src/shaders/SkShaderBase.cpp | 37+++++++++++--------------------------
Mgfx/skia/skia/src/shaders/SkShaderBase.h | 24++++++++++--------------
Mgfx/skia/skia/src/shaders/SkWorkingColorSpaceShader.cpp | 99+++++++++++++------------------------------------------------------------------
Mgfx/skia/skia/src/shaders/SkWorkingColorSpaceShader.h | 41++++++++++++++---------------------------
Mgfx/skia/skia/src/shaders/gradients/SkConicalGradient.cpp | 18+++++++++---------
Mgfx/skia/skia/src/shaders/gradients/SkConicalGradient.h | 7+++----
Mgfx/skia/skia/src/shaders/gradients/SkGradientBaseShader.cpp | 22++++++++++------------
Mgfx/skia/skia/src/sksl/SkSLCompiler.cpp | 4+++-
Mgfx/skia/skia/src/sksl/SkSLGraphiteModules.cpp | 6++++++
Mgfx/skia/skia/src/sksl/SkSLGraphiteModules.h | 2++
Mgfx/skia/skia/src/sksl/SkSLInliner.cpp | 20+++++++-------------
Mgfx/skia/skia/src/sksl/SkSLInliner.h | 2--
Mgfx/skia/skia/src/sksl/SkSLModule.h | 4+++-
Mgfx/skia/skia/src/sksl/SkSLModuleDataDefault.cpp | 7+++++++
Mgfx/skia/skia/src/sksl/SkSLModuleLoader.cpp | 28+++++++++++++++++++++++++++-
Mgfx/skia/skia/src/sksl/SkSLModuleLoader.h | 2++
Mgfx/skia/skia/src/sksl/SkSLParser.cpp | 9++++-----
Mgfx/skia/skia/src/sksl/SkSLProgramKind.h | 2++
Mgfx/skia/skia/src/sksl/SkSLProgramSettings.h | 10++++++----
Mgfx/skia/skia/src/sksl/SkSLUtil.h | 19++++++-------------
Mgfx/skia/skia/src/sksl/analysis/SkSLFinalizationChecks.cpp | 4++--
Mgfx/skia/skia/src/sksl/analysis/SkSLIsConstantExpression.cpp | 2+-
Mgfx/skia/skia/src/sksl/codegen/SkSLCodeGenTypes.h | 27---------------------------
Mgfx/skia/skia/src/sksl/codegen/SkSLGLSLCodeGenerator.cpp | 12+++++-------
Mgfx/skia/skia/src/sksl/codegen/SkSLGLSLCodeGenerator.h | 5+++--
Mgfx/skia/skia/src/sksl/codegen/SkSLHLSLCodeGenerator.cpp | 2+-
Mgfx/skia/skia/src/sksl/codegen/SkSLHLSLCodeGenerator.h | 11++++-------
Mgfx/skia/skia/src/sksl/codegen/SkSLMetalCodeGenerator.cpp | 54+++---------------------------------------------------
Mgfx/skia/skia/src/sksl/codegen/SkSLMetalCodeGenerator.h | 5+++--
Dgfx/skia/skia/src/sksl/codegen/SkSLNativeShader.h | 28----------------------------
Mgfx/skia/skia/src/sksl/codegen/SkSLSPIRVCodeGenerator.cpp | 642++++++++++++++++++++++++++++++++++---------------------------------------------
Mgfx/skia/skia/src/sksl/codegen/SkSLSPIRVCodeGenerator.h | 21+++++++--------------
Mgfx/skia/skia/src/sksl/codegen/SkSLSPIRVValidator.cpp | 19++++++++++---------
Mgfx/skia/skia/src/sksl/codegen/SkSLSPIRVValidator.h | 8+++-----
Mgfx/skia/skia/src/sksl/codegen/SkSLSPIRVtoHLSL.cpp | 5+++--
Mgfx/skia/skia/src/sksl/codegen/SkSLSPIRVtoHLSL.h | 5+----
Mgfx/skia/skia/src/sksl/codegen/SkSLWGSLCodeGenerator.cpp | 64+++++++++++++++++++++-------------------------------------------
Mgfx/skia/skia/src/sksl/codegen/SkSLWGSLCodeGenerator.h | 3+--
Mgfx/skia/skia/src/sksl/codegen/SkSLWGSLValidator.cpp | 2+-
Mgfx/skia/skia/src/sksl/generated/sksl_graphite_frag.minified.sksl | 364+++++++++++++++++++++++++++++++++++++++----------------------------------------
Mgfx/skia/skia/src/sksl/generated/sksl_graphite_frag.unoptimized.sksl | 383+++++++++++++++++++++++++++++++++++++++----------------------------------------
Agfx/skia/skia/src/sksl/generated/sksl_graphite_frag_es2.minified.sksl | 2++
Agfx/skia/skia/src/sksl/generated/sksl_graphite_frag_es2.unoptimized.sksl | 2++
Mgfx/skia/skia/src/sksl/generated/sksl_graphite_vert.minified.sksl | 193+++++++++++++++++++++++++++++++++++++++----------------------------------------
Mgfx/skia/skia/src/sksl/generated/sksl_graphite_vert.unoptimized.sksl | 273+++++++++++++++++++++++++++++++++++++++----------------------------------------
Agfx/skia/skia/src/sksl/generated/sksl_graphite_vert_es2.minified.sksl | 2++
Agfx/skia/skia/src/sksl/generated/sksl_graphite_vert_es2.unoptimized.sksl | 2++
Mgfx/skia/skia/src/sksl/generated/sksl_rt_shader.minified.sksl | 28+++++++++++++---------------
Mgfx/skia/skia/src/sksl/generated/sksl_rt_shader.unoptimized.sksl | 13+++++--------
Mgfx/skia/skia/src/sksl/ir/SkSLFunctionDeclaration.cpp | 4+++-
Mgfx/skia/skia/src/sksl/ir/SkSLSwitchStatement.cpp | 2+-
Mgfx/skia/skia/src/sksl/ir/SkSLType.h | 6------
Mgfx/skia/skia/src/sksl/sksl_graphite_frag.sksl | 150++++++++++++++++++++++++++++---------------------------------------------------
Agfx/skia/skia/src/sksl/sksl_graphite_frag_es2.sksl | 1+
Mgfx/skia/skia/src/sksl/sksl_graphite_vert.sksl | 101++++++++++++++++++++++++++++---------------------------------------------------
Agfx/skia/skia/src/sksl/sksl_graphite_vert_es2.sksl | 1+
Mgfx/skia/skia/src/sksl/sksl_rt_shader.sksl | 11+++--------
Mgfx/skia/skia/src/sksl/sksl_shared.sksl | 4++--
Mgfx/skia/skia/src/text/GlyphRun.cpp | 5+++--
Mgfx/skia/skia/src/text/gpu/GlyphVector.h | 1-
Mgfx/skia/skia/src/text/gpu/SubRunContainer.cpp | 483+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
Mgfx/skia/skia/src/text/gpu/SubRunContainer.h | 145+++++++++++++++++++++++++++++++++++--------------------------------------------
Mgfx/skia/skia/src/text/gpu/VertexFiller.cpp | 251++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Mgfx/skia/skia/src/text/gpu/VertexFiller.h | 22++++++++++++++--------
Mgfx/skia/skia/src/utils/SkClipStackUtils.cpp | 15++++++---------
Mgfx/skia/skia/src/utils/SkClipStackUtils.h | 2+-
Mgfx/skia/skia/src/utils/SkCustomTypeface.cpp | 97+++++++++++++++++++++++++++++++++++--------------------------------------------
Mgfx/skia/skia/src/utils/SkDashPath.cpp | 124++++++++++++++++++++++++++++++++++++++++---------------------------------------
Mgfx/skia/skia/src/utils/SkDashPathPriv.h | 40+++++++++++++++++++---------------------
Mgfx/skia/skia/src/utils/SkFloatUtils.h | 11-----------
Mgfx/skia/skia/src/utils/SkNWayCanvas.cpp | 9++-------
Mgfx/skia/skia/src/utils/SkOrderedFontMgr.cpp | 16+++++-----------
Mgfx/skia/skia/src/utils/SkPaintFilterCanvas.cpp | 1-
Mgfx/skia/skia/src/utils/SkParsePath.cpp | 70++++++++++++++++++++++++++++++++++++----------------------------------
Mgfx/skia/skia/src/utils/SkPatchUtils.cpp | 8++++----
Mgfx/skia/skia/src/utils/SkPatchUtils.h | 2+-
Mgfx/skia/skia/src/utils/SkPolyUtils.cpp | 2+-
Mgfx/skia/skia/src/utils/SkShaderUtils.cpp | 21---------------------
Mgfx/skia/skia/src/utils/SkShaderUtils.h | 4----
Mgfx/skia/skia/src/utils/SkShadowTessellator.cpp | 76++++++++++++++++++++++++++++++++++++++--------------------------------------
Mgfx/skia/skia/src/utils/SkShadowUtils.cpp | 45+++++++++++++++++++--------------------------
Mgfx/skia/skia/src/utils/SkTextUtils.cpp | 13++++++-------
Mgfx/skia/skia/src/utils/win/SkDWriteGeometrySink.cpp | 28++++++++++++++--------------
Mgfx/skia/skia/src/utils/win/SkDWriteGeometrySink.h | 12+++++++-----
Mgfx/skia/skia/src/utils/win/SkHRESULT.cpp | 2+-
Mgfx/thebes/gfxFcPlatformFontList.cpp | 25-------------------------
471 files changed, 11832 insertions(+), 17675 deletions(-)

diff --git a/dom/canvas/DrawTargetWebgl.cpp b/dom/canvas/DrawTargetWebgl.cpp @@ -5498,7 +5498,7 @@ void DrawTargetWebgl::Stroke(const Path* aPath, const Pattern& aPattern, bool allowStrokeAlpha = false; if (numVerbs >= 2 && numVerbs <= 3) { uint8_t verbs[3]; - skiaPath.getVerbs({verbs, numVerbs}); + skiaPath.getVerbs(verbs, numVerbs); if (verbs[0] == SkPath::kMove_Verb && verbs[1] == SkPath::kLine_Verb && (numVerbs < 3 || verbs[2] == SkPath::kClose_Verb)) { bool closed = numVerbs >= 3; diff --git a/gfx/2d/2D.h b/gfx/2d/2D.h @@ -2317,11 +2317,11 @@ class GFX2D_API Factory { static bool DoesBackendSupportDataDrawtarget(BackendType aType); - static void SetSubpixelOrder(SubpixelOrder aOrder); - static SubpixelOrder GetSubpixelOrder(); + static void SetBGRSubpixelOrder(bool aBGR); + static bool GetBGRSubpixelOrder(); private: - static SubpixelOrder mSubpixelOrder; + static bool mBGRSubpixelOrder; public: static already_AddRefed<DrawTarget> CreateDrawTargetWithSkCanvas( diff --git a/gfx/2d/DWriteSettings.cpp b/gfx/2d/DWriteSettings.cpp @@ -68,9 +68,7 @@ static void GDIGammaVarUpdated() { static void UpdatePixelGeometry() { sPixelGeometry = static_cast<DWRITE_PIXEL_GEOMETRY>(gfxVars::SystemTextPixelGeometry()); - Factory::SetSubpixelOrder(sPixelGeometry == DWRITE_PIXEL_GEOMETRY_BGR - ? SubpixelOrder::BGR - : SubpixelOrder::RGB); + Factory::SetBGRSubpixelOrder(sPixelGeometry == DWRITE_PIXEL_GEOMETRY_BGR); } static void PixelGeometryVarUpdated() { UpdatePixelGeometry(); diff --git a/gfx/2d/DrawTargetSkia.cpp b/gfx/2d/DrawTargetSkia.cpp @@ -420,7 +420,7 @@ static sk_sp<SkImage> ExtractSubset(sk_sp<SkImage> aImage, return SkImages::RasterFromPixmap(subsetPixmap, ReleaseImage, aImage.release()); } - return aImage->makeSubset(nullptr, subsetRect, SkImage::RequiredProperties()); + return aImage->makeSubset(nullptr, subsetRect); } static void FreeAlphaPixels(void* aBuf, void*) { sk_free(aBuf); } @@ -1329,10 +1329,7 @@ class GlyphMaskShader : public SkEmptyShader { } bool isOpaque() const override { return true; } - bool isConstant(SkColor4f* color) const override { - if (color) *color = SkColor4f{1, 1, 1, 1}; - return true; - } + bool isConstant() const override { return true; } void flatten(SkWriteBuffer& buffer) const override { buffer.writeColor4f(mColor); @@ -1415,8 +1412,7 @@ Maybe<Rect> DrawTargetSkia::GetGlyphLocalBounds( for (uint32_t i = 0; i < batchSize; i++) { glyphs[i] = aBuffer.mGlyphs[offset + i].mIndex; } - font.getBounds({glyphs.begin(), batchSize}, {rects.begin(), batchSize}, - nullptr); + font.getBounds(glyphs.begin(), batchSize, rects.begin(), nullptr); for (uint32_t i = 0; i < batchSize; i++) { bounds = bounds.Union(SkRectToRect(rects[i]) + aBuffer.mGlyphs[offset + i].mPosition); @@ -1782,17 +1778,8 @@ void DrawTargetSkia::CopySurface(SourceSurface* aSurface, } static inline SkPixelGeometry GetSkPixelGeometry() { - switch (Factory::GetSubpixelOrder()) { - case SubpixelOrder::BGR: - return kBGR_H_SkPixelGeometry; - case SubpixelOrder::VBGR: - return kBGR_V_SkPixelGeometry; - case SubpixelOrder::VRGB: - return kRGB_V_SkPixelGeometry; - case SubpixelOrder::RGB: - default: - return kRGB_H_SkPixelGeometry; - } + return Factory::GetBGRSubpixelOrder() ? kBGR_H_SkPixelGeometry + : kRGB_H_SkPixelGeometry; } template <typename T> diff --git a/gfx/2d/Factory.cpp b/gfx/2d/Factory.cpp @@ -240,7 +240,7 @@ StaticMutex Factory::mDeviceLock; StaticMutex Factory::mDTDependencyLock; #endif -SubpixelOrder Factory::mSubpixelOrder = SubpixelOrder::UNKNOWN; +bool Factory::mBGRSubpixelOrder = false; mozilla::gfx::Config* Factory::sConfig = nullptr; @@ -627,11 +627,9 @@ already_AddRefed<ScaledFont> Factory::CreateScaledFontForFreeTypeFont( } #endif -void Factory::SetSubpixelOrder(SubpixelOrder aOrder) { - mSubpixelOrder = aOrder; -} +void Factory::SetBGRSubpixelOrder(bool aBGR) { mBGRSubpixelOrder = aBGR; } -SubpixelOrder Factory::GetSubpixelOrder() { return mSubpixelOrder; } +bool Factory::GetBGRSubpixelOrder() { return mBGRSubpixelOrder; } #ifdef MOZ_ENABLE_FREETYPE SharedFTFace::SharedFTFace(FT_Face aFace, SharedFTFaceData* aData) diff --git a/gfx/2d/HelpersSkia.h b/gfx/2d/HelpersSkia.h @@ -153,7 +153,7 @@ static inline bool StrokeOptionsToPaint(SkPaint& aPaint, SkFloatToScalar(aOptions.mDashPattern[i % aOptions.mDashLength]); } - auto dash = SkDashPathEffect::Make({&pattern.front(), dashCount}, + auto dash = SkDashPathEffect::Make(&pattern.front(), dashCount, SkFloatToScalar(aOptions.mDashOffset)); aPaint.setPathEffect(dash); } diff --git a/gfx/2d/ScaledFontBase.cpp b/gfx/2d/ScaledFontBase.cpp @@ -103,7 +103,7 @@ SkPath ScaledFontBase::GetSkiaPathForGlyphs(const GlyphBuffer& aBuffer) { } ctx = {aBuffer.mGlyphs}; font.getPaths( - {indices.data(), indices.size()}, + indices.data(), indices.size(), [](const SkPath* glyphPath, const SkMatrix& scaleMatrix, void* ctxPtr) { Context& ctx = *reinterpret_cast<Context*>(ctxPtr); if (glyphPath) { diff --git a/gfx/2d/ScaledFontDWrite.cpp b/gfx/2d/ScaledFontDWrite.cpp @@ -496,7 +496,7 @@ bool ScaledFontDWrite::GetWRFontInstanceOptions( default: break; } - if (Factory::GetSubpixelOrder() == SubpixelOrder::BGR) { + if (Factory::GetBGRSubpixelOrder()) { options.flags |= wr::FontInstanceFlags::SUBPIXEL_BGR; } options.synthetic_italics = diff --git a/gfx/2d/ScaledFontFontconfig.cpp b/gfx/2d/ScaledFontFontconfig.cpp @@ -48,7 +48,14 @@ bool ScaledFontFontconfig::UseSubpixelPosition() const { } SkTypeface* ScaledFontFontconfig::CreateSkTypeface() { - return SkCreateTypefaceFromCairoFTFont(mFace->GetFace(), mFace.get(), + SkPixelGeometry geo = mInstanceData.mFlags & InstanceData::SUBPIXEL_BGR + ? (mInstanceData.mFlags & InstanceData::LCD_VERTICAL + ? kBGR_V_SkPixelGeometry + : kBGR_H_SkPixelGeometry) + : (mInstanceData.mFlags & InstanceData::LCD_VERTICAL + ? kRGB_V_SkPixelGeometry + : kRGB_H_SkPixelGeometry); + return SkCreateTypefaceFromCairoFTFont(mFace->GetFace(), mFace.get(), geo, mInstanceData.mLcdFilter); } diff --git a/gfx/2d/Types.h b/gfx/2d/Types.h @@ -122,14 +122,6 @@ enum class SurfaceFormat : int8_t { OS_RGBX = X8R8G8B8_UINT32 }; -enum class SubpixelOrder : uint8_t { - UNKNOWN, - RGB, - BGR, - VRGB, - VBGR, -}; - struct SurfaceFormatInfo { bool hasColor; bool hasAlpha; diff --git a/gfx/skia/generate_mozbuild.py b/gfx/skia/generate_mozbuild.py @@ -160,16 +160,11 @@ def generate_platform_sources(): platform_args = { 'win' : 'win_vc="C:/" win_sdk_version="00.0.00000.0" win_toolchain_version="00.00.00000"' } - source_sets = [':core', ':skia', ':clipstack_utils', ':pathops'] for plat in platforms: args = platform_args.get(plat, '') - subprocess.check_output('cd skia && bin/gn gen out/{0} --args=\'target_os="{0}" {1}\' > /dev/null'.format(plat, args), shell=True) - output = b'' - for source_set in source_sets: - set_output = subprocess.check_output('cd skia && bin/gn desc out/{0} {1} sources'.format(plat, source_set), shell=True) - if set_output: - output += set_output - sources[plat] = parse_sources(output) + output = subprocess.check_output('cd skia && bin/gn gen out/{0} --args=\'target_os="{0}" {1}\' > /dev/null && bin/gn desc out/{0} :skia sources'.format(plat, args), shell=True) + if output: + sources[plat] = parse_sources(output) plat_deps = { ':fontmgr_win' : 'win', @@ -206,7 +201,6 @@ def generate_separated_sources(platform_sources): 'third_party', 'SkAnimCodecPlayer', 'SkCamera', - 'SkCapture', 'SkCanvasStack', 'SkCanvasStateUtils', 'SkMultiPictureDocument', diff --git a/gfx/skia/moz.build b/gfx/skia/moz.build @@ -83,8 +83,6 @@ UNIFIED_SOURCES += [ 'skia/src/core/SkCompressedDataUtils.cpp', 'skia/src/core/SkContourMeasure.cpp', 'skia/src/core/SkConvertPixels.cpp', - 'skia/src/core/SkCPUContext.cpp', - 'skia/src/core/SkCPURecorder.cpp', 'skia/src/core/SkCubicClipper.cpp', 'skia/src/core/SkCubicMap.cpp', 'skia/src/core/SkData.cpp', @@ -98,6 +96,7 @@ UNIFIED_SOURCES += [ 'skia/src/core/SkDraw_text.cpp', 'skia/src/core/SkDraw_vertices.cpp', 'skia/src/core/SkDrawable.cpp', + 'skia/src/core/SkDrawBase.cpp', 'skia/src/core/SkDrawShadowInfo.cpp', 'skia/src/core/SkEdge.cpp', 'skia/src/core/SkEdgeBuilder.cpp', @@ -148,11 +147,7 @@ UNIFIED_SOURCES += [ 'skia/src/core/SkPath_serial.cpp', 'skia/src/core/SkPathBuilder.cpp', 'skia/src/core/SkPathEffect.cpp', - 'skia/src/core/SkPathIter.cpp', 'skia/src/core/SkPathMeasure.cpp', - 'skia/src/core/SkPathPriv.cpp', - 'skia/src/core/SkPathRaw.cpp', - 'skia/src/core/SkPathRawShapes.cpp', 'skia/src/core/SkPathRef.cpp', 'skia/src/core/SkPathUtils.cpp', 'skia/src/core/SkPicture.cpp', @@ -173,7 +168,6 @@ UNIFIED_SOURCES += [ 'skia/src/core/SkReadBuffer.cpp', 'skia/src/core/SkReadPixelsRec.cpp', 'skia/src/core/SkRecord.cpp', - 'skia/src/core/SkRecordCanvas.cpp', 'skia/src/core/SkRecordDraw.cpp', 'skia/src/core/SkRecordedDrawable.cpp', 'skia/src/core/SkRecordOpts.cpp', @@ -204,7 +198,6 @@ UNIFIED_SOURCES += [ 'skia/src/core/SkStrokeRec.cpp', 'skia/src/core/SkStrokerPriv.cpp', 'skia/src/core/SkSwizzle.cpp', - 'skia/src/core/SkSynchronizedResourceCache.cpp', 'skia/src/core/SkTaskGroup.cpp', 'skia/src/core/SkTextBlob.cpp', 'skia/src/core/SkTypeface.cpp', @@ -472,6 +465,7 @@ SOURCES += [ 'skia/src/core/SkOpts.cpp', 'skia/src/core/SkPath.cpp', 'skia/src/core/SkPictureData.cpp', + 'skia/src/core/SkRecorder.cpp', 'skia/src/core/SkRTree.cpp', 'skia/src/core/SkScan_Antihair.cpp', 'skia/src/core/SkScan_AntiPath.cpp', diff --git a/gfx/skia/skia/include/codec/SkCodec.h b/gfx/skia/skia/include/codec/SkCodec.h @@ -18,7 +18,6 @@ #include "include/core/SkTypes.h" #include "include/core/SkYUVAPixmaps.h" #include "include/private/SkEncodedInfo.h" -#include "include/private/SkHdrMetadata.h" #include "include/private/base/SkNoncopyable.h" #include "modules/skcms/skcms.h" @@ -241,12 +240,6 @@ public: } /** - * Return the HDR metadata associated with this image. Note that even SDR images can include - * HDR metadata (e.g, indicating how to inverse tone map when displayed on an HDR display). - */ - const skhdr::Metadata& getHdrMetadata() const { return fEncodedInfo.getHdrMetadata(); } - - /** * Whether the encoded input uses 16 or more bits per component. */ bool hasHighBitDepthEncodedData() const { @@ -911,20 +904,11 @@ protected: [[nodiscard]] bool rewindIfNeeded(); /** - * Called by onRewind(), attempts to rewind fStream. - */ - bool rewindStream(); - - /** * Called by rewindIfNeeded, if the stream needed to be rewound. * - * Subclasses should call rewindStream() if they own one, and then - * do any set up needed after. + * Subclasses should do any set up needed after a rewind. */ virtual bool onRewind() { - if (!this->rewindStream()) { - return false; - } return true; } @@ -1008,12 +992,7 @@ private: }; XformTime fXformTime; XformFormat fDstXformFormat; // Based on fDstInfo. - skcms_ICCProfile fDstProfileStorage; - // This tracks either fDstProfileStorage or the ICC profile in fEncodedInfo. - // For the latter case it is important to not make a profile copy because skcms_Transform - // only performs a shallow pointer comparison to decide whether it can skip the color space - // transformation. - const skcms_ICCProfile* fDstProfile = &fDstProfileStorage; + skcms_ICCProfile fDstProfile; skcms_AlphaFormat fDstXformAlphaFormat; // Only meaningful during scanline decodes. diff --git a/gfx/skia/skia/include/codec/SkPngRustDecoder.h b/gfx/skia/skia/include/codec/SkPngRustDecoder.h @@ -1,30 +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 SkPngRustDecoder_DEFINED -#define SkPngRustDecoder_DEFINED - -#include <memory> - -#include "include/codec/SkCodec.h" -#include "include/private/base/SkAPI.h" - -class SkStream; - -namespace SkPngRustDecoder { - -/** Returns true if this data claims to be a PNG image. */ -SK_API bool IsPng(const void*, size_t); - -SK_API std::unique_ptr<SkCodec> Decode(std::unique_ptr<SkStream>, - SkCodec::Result*, - SkCodecs::DecodeContext = nullptr); - -inline constexpr SkCodecs::Decoder Decoder() { return {"png", IsPng, Decode}; } - -} // namespace SkPngRustDecoder - -#endif // SkPngRustDecoder_DEFINED diff --git a/gfx/skia/skia/include/config/MODULE.bazel b/gfx/skia/skia/include/config/MODULE.bazel @@ -1,5 +0,0 @@ -module( - name = "skia_user_config", -) - -bazel_dep(name = "rules_cc", version = "0.1.1") diff --git a/gfx/skia/skia/include/config/SkUserConfig.h b/gfx/skia/skia/include/config/SkUserConfig.h @@ -88,7 +88,6 @@ //#define SK_HISTOGRAM_BOOLEAN(name, sample) //#define SK_HISTOGRAM_ENUMERATION(name, sampleEnum, enumSize) //#define SK_HISTOGRAM_EXACT_LINEAR(name, sample, valueMax) -//#define SK_HISTOGRAM_CUSTOM_EXACT_LINEAR(name, sample, value_min, value_max, bucket_count) //#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) @@ -122,14 +121,6 @@ */ //#define SK_API __declspec(dllexport) -/* - * If using DNG support, set the version of the dng_sdk being compiled against here - * following the versioning scheme of dng_tag_valus.h - * eg, DNG 1.4 is 0x01040000, DNG 1.7.1 is 0x01070100, etc... - * If unspecified, DNG SDK 1.4 is assumed - */ -// #define SK_DNG_VERSION 0x01040000 - #define MOZ_SKIA // On all platforms we have this byte order @@ -146,8 +137,6 @@ #define SK_SUPPORT_GPU 0 -#define SK_DISABLE_LEGACY_PNG_WRITEBUFFER - #define SK_DISABLE_SLOW_DEBUG_VALIDATION 1 #define SK_DISABLE_TYPEFACE_CACHE diff --git a/gfx/skia/skia/include/config/copts.bzl b/gfx/skia/skia/include/config/copts.bzl @@ -3,7 +3,7 @@ THIS IS THE EXTERNAL-ONLY VERSION OF THIS FILE. G3 HAS ITS OWN. This file contains flags for the C++ compiler, referred to by Bazel as copts. -The copts in a cc_library do not flow to the children (dependencies) nor the parents +The copts in a cc_library to not flow to the children (dependencies) nor the parents (dependents), so we use skia_cc_library to inject them in every library along the chain. Now that we have a modular build, this file could maybe go away and folded into our toolchains. @@ -83,7 +83,6 @@ WARNINGS = [ "-Wno-return-std-move-in-c++11", "-Wno-shadow-field-in-constructor", "-Wno-shadow-uncaptured-local", - "-Wno-switch-default", # Warns even when all values are covered "-Wno-undefined-func-template", "-Wno-unused-parameter", # It is common to have unused parameters in src/ "-Wno-zero-as-null-pointer-constant", # VK_NULL_HANDLE is defined as 0 @@ -153,7 +152,7 @@ WARNINGS = [ "-Wno-unknown-warning-option", ] + select({ "@platforms//os:windows": [ - # skbug.com/40045281 + # skbug.com/14203 "-Wno-nonportable-system-include-path", "-Wno-unknown-argument", # Clang warns even when all enum values are covered. diff --git a/gfx/skia/skia/include/core/SkBitmap.h b/gfx/skia/skia/include/core/SkBitmap.h @@ -1230,7 +1230,7 @@ public: memory from the heap. This is the default SkBitmap::Allocator invoked by allocPixels(). */ - class SK_API HeapAllocator : public Allocator { + class HeapAllocator : public Allocator { public: /** Allocates the pixel memory for the bitmap, given its dimensions and diff --git a/gfx/skia/skia/include/core/SkCPUContext.h b/gfx/skia/skia/include/core/SkCPUContext.h @@ -1,31 +0,0 @@ -/* - * 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 skcpu_Context_DEFINED -#define skcpu_Context_DEFINED - -#include "include/private/base/SkAPI.h" - -#include <memory> - -namespace skcpu { -class Recorder; - -class SK_API Context { -public: - struct Options {}; - - std::unique_ptr<Recorder> makeRecorder() const; - - static std::unique_ptr<const Context> Make(const Options&); - static std::unique_ptr<const Context> Make(); - -protected: - Context() = default; -}; -} // namespace skcpu - -#endif diff --git a/gfx/skia/skia/include/core/SkCPURecorder.h b/gfx/skia/skia/include/core/SkCPURecorder.h @@ -1,74 +0,0 @@ -/* - * 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 skcpu_Recorder_DEFINED -#define skcpu_Recorder_DEFINED - -#include "include/core/SkRecorder.h" -#include "include/core/SkRefCnt.h" -#include "include/private/base/SkAPI.h" - -class SkCanvas; -class SkSurface; -class SkSurfaceProps; -struct SkImageInfo; - -#include <cstddef> - -namespace skcpu { - -class SK_API Recorder : public SkRecorder { -public: - /** Returns a non-null global context. Can be used as a means of transitioning onto - * new APIs when a skcpu::Context/Recorder has not been piped down into the code paths - */ - static Recorder* TODO(); - - SkRecorder::Type type() const final { return SkRecorder::Type::kCPU; } - skcpu::Recorder* cpuRecorder() final { return this; } - - /** Allocates a bitmap-backed SkSurface. SkCanvas returned by SkSurface draws directly into - * those allocated pixels, which are zeroed before use. Pixel memory size is imageInfo.height() - * times imageInfo.minRowBytes() or rowBytes, if provided and non-zero. - * - * Pixel memory is deleted when SkSurface is deleted. - * - * Validity constraints include: - * - info dimensions are greater than zero; - * - info contains SkColorType and SkAlphaType supported by raster surface. - * - * @param imageInfo width, height, SkColorType, SkAlphaType, SkColorSpace, - * of raster surface; width and height must be greater than zero - * @param rowBytes interval from one SkSurface row to the next. - * @param props LCD striping orientation and setting for device independent fonts; - * may be nullptr - * @return SkSurface if parameters are valid and memory was allocated, else nullptr. - */ - sk_sp<SkSurface> makeBitmapSurface(const SkImageInfo& imageInfo, - size_t rowBytes, - const SkSurfaceProps* surfaceProps); - sk_sp<SkSurface> makeBitmapSurface(const SkImageInfo& imageInfo, - const SkSurfaceProps* surfaceProps = nullptr); - -private: - // TODO (b/412351769): Implement this so we can capture from a CPU Recorder. - SkCanvas* makeCaptureCanvas(SkCanvas*) final { return nullptr; } -}; - -inline Recorder* AsRecorder(SkRecorder* recorder) { - if (!recorder) { - return nullptr; - } - if (recorder->type() != SkRecorder::Type::kCPU) { - return nullptr; - } - return static_cast<Recorder*>(recorder); -} - -} // namespace skcpu - -#endif diff --git a/gfx/skia/skia/include/core/SkCanvas.h b/gfx/skia/skia/include/core/SkCanvas.h @@ -19,7 +19,6 @@ #include "include/core/SkMatrix.h" #include "include/core/SkPaint.h" #include "include/core/SkPoint.h" -#include "include/core/SkRSXform.h" // IWYU pragma: keep (for unspanned apis) #include "include/core/SkRasterHandleAllocator.h" #include "include/core/SkRect.h" #include "include/core/SkRefCnt.h" @@ -69,7 +68,6 @@ class SkPicture; class SkPixmap; class SkRRect; class SkRegion; -class SkRecorder; class SkShader; class SkSpecialImage; class SkSurface; @@ -77,6 +75,7 @@ class SkSurface_Base; class SkTextBlob; class SkVertices; struct SkDrawShadowRec; +struct SkRSXform; template<typename E> class SkEnumBitMask; @@ -328,18 +327,13 @@ public: */ virtual GrRecordingContext* recordingContext() const; + /** Returns Recorder for the GPU surface associated with SkCanvas. @return Recorder, if available; nullptr otherwise */ virtual skgpu::graphite::Recorder* recorder() const; - /** Returns Recorder for the surface associated with SkCanvas. - - @return Recorder, should be non-null - */ - virtual SkRecorder* baseRecorder() const; - /** Sometimes a canvas is owned by a surface. If it is, getSurface() will return a bare * pointer to that surface, else this will return nullptr. */ @@ -1327,12 +1321,7 @@ public: example: https://fiddle.skia.org/c/@Canvas_drawPoints */ - void drawPoints(PointMode mode, SkSpan<const SkPoint>, const SkPaint& paint); -#ifdef SK_SUPPORT_UNSPANNED_APIS - void drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) { - this->drawPoints(mode, {pts, count}, paint); - } -#endif + void drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint); /** Draws point at (x, y) using clip, SkMatrix and SkPaint paint. @@ -1904,28 +1893,19 @@ public: SkColorFilter, and SkImageFilter; apply to text. By default, draws filled black glyphs. - @param glyphs the span of glyphIDs to draw + @param count number of glyphs to draw + @param glyphs the array of glyphIDs to draw @param positions where to draw each glyph relative to origin - @param clusters cluster information + @param clusters array of size count of cluster information + @param textByteCount size of the utf8text @param utf8text utf8text supporting information for the glyphs @param origin the origin of all the positions @param font typeface, text size and so, used to describe the text @param paint blend, color, and so on, used to draw */ - void drawGlyphs(SkSpan<const SkGlyphID> glyphs, SkSpan<const SkPoint> positions, - SkSpan<const uint32_t> clusters, SkSpan<const char> utf8text, - SkPoint origin, const SkFont& font, const SkPaint& paint); -#ifdef SK_SUPPORT_UNSPANNED_APIS void drawGlyphs(int count, const SkGlyphID glyphs[], const SkPoint positions[], const uint32_t clusters[], int textByteCount, const char utf8text[], - SkPoint origin, const SkFont& font, const SkPaint& paint) { - this->drawGlyphs({glyphs, count}, - {positions, count}, - {clusters, count}, - {utf8text, textByteCount}, - origin, font, paint); - } -#endif + SkPoint origin, const SkFont& font, const SkPaint& paint); /** Draws count glyphs, at positions relative to origin styled with font and paint. @@ -1945,14 +1925,8 @@ public: @param font typeface, text size and so, used to describe the text @param paint blend, color, and so on, used to draw */ - void drawGlyphs(SkSpan<const SkGlyphID> glyphs, SkSpan<const SkPoint> positions, - SkPoint origin, const SkFont& font, const SkPaint& paint); -#ifdef SK_SUPPORT_UNSPANNED_APIS void drawGlyphs(int count, const SkGlyphID glyphs[], const SkPoint positions[], - SkPoint origin, const SkFont& font, const SkPaint& paint) { - this->drawGlyphs({glyphs, count}, {positions, count}, origin, font, paint); - } -#endif + SkPoint origin, const SkFont& font, const SkPaint& paint); /** Draws count glyphs, at positions relative to origin styled with font and paint. @@ -1973,14 +1947,8 @@ public: @param font typeface, text size and so, used to describe the text @param paint blend, color, and so on, used to draw */ - void drawGlyphsRSXform(SkSpan<const SkGlyphID> glyphs, SkSpan<const SkRSXform> xforms, - SkPoint origin, const SkFont& font, const SkPaint& paint); -#ifdef SK_SUPPORT_UNSPANNED_APIS void drawGlyphs(int count, const SkGlyphID glyphs[], const SkRSXform xforms[], - SkPoint origin, const SkFont& font, const SkPaint& paint) { - this->drawGlyphsRSXform({glyphs, count}, {xforms, count}, origin, font, paint); - } -#endif + SkPoint origin, const SkFont& font, const SkPaint& paint); /** Draws SkTextBlob blob at (x, y), using clip, SkMatrix, and SkPaint paint. @@ -2187,9 +2155,7 @@ public: SkMaskFilter and SkPathEffect on paint are ignored. - For non-empty spans, the number of draws will be the min of - xform.size(), tex.size(), and (if not empty) colors.size(). - + xform, tex, and colors if present, must contain count entries. Optional colors are applied for each sprite using SkBlendMode mode, treating sprite as source and colors as destination. Optional cullRect is a conservative bounds of all transformed sprites. @@ -2201,25 +2167,15 @@ public: @param xform SkRSXform mappings for sprites in atlas @param tex SkRect locations of sprites in atlas @param colors one per sprite, blended with sprite using SkBlendMode; may be nullptr + @param count number of sprites to draw @param mode SkBlendMode combining colors and sprites @param sampling SkSamplingOptions used when sampling from the atlas image @param cullRect bounds of transformed sprites for efficient clipping; may be nullptr @param paint SkColorFilter, SkImageFilter, SkBlendMode, and so on; may be nullptr */ - void drawAtlas(const SkImage* atlas, SkSpan<const SkRSXform> xform, - SkSpan<const SkRect> tex, SkSpan<const SkColor> colors, SkBlendMode mode, - const SkSamplingOptions& sampling, const SkRect* cullRect, const SkPaint* paint); -#ifdef SK_SUPPORT_UNSPANNED_APIS void drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[], const SkColor colors[], int count, SkBlendMode mode, - const SkSamplingOptions& samp, const SkRect* cullRect, const SkPaint* paint) { - this->drawAtlas(atlas, - {xform, count}, - {tex, tex ? count : 0}, - {colors, colors ? count : 0}, - mode, samp, cullRect, paint); - } -#endif + const SkSamplingOptions& sampling, const SkRect* cullRect, const SkPaint* paint); /** Draws SkDrawable drawable using clip and SkMatrix, concatenated with optional matrix. @@ -2584,8 +2540,6 @@ private: void checkForDeferredSave(); void internalSetMatrix(const SkM44&); - virtual void onSurfaceDelete() {} - friend class SkAndroidFrameworkUtils; friend class SkCanvasPriv; // needs to expose android functions for testing outside android friend class AutoLayerForImageFilter; diff --git a/gfx/skia/skia/include/core/SkColorSpace.h b/gfx/skia/skia/include/core/SkColorSpace.h @@ -131,16 +131,17 @@ static constexpr skcms_TransferFunction kRec2020 = { 2.22222f, 0.909672f, 0.0903276f, 0.222222f, 0.0812429f, 0, 0}; //////////////////////////////////////////////////////////////////////////////// -// Transfer function defined by ITU-T H.273, table 3. Names are given by the -// first specification referenced in the value's row. The equations in table 3 -// "either indicates the reference [OETF] ... or indicates the inverse of the -// reference EOTF". The transfer functions provided are reference EOTFs. +// 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. This follows note 1, which reads: "In the cases -// of [...] TransferCharacteristics equal to 1, 6, 14 or 15 [...], although the -// value is defined in terms of a reference [OETF], a suggested corresponding -// reference [EOTF] has been specified in Rec. ITU-R BT.1886-0." -static constexpr skcms_TransferFunction kRec709 = {2.4f, 1.f, 0.f, 0.f, 0.f, 0.f, 0.f}; +// 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}; @@ -174,7 +175,7 @@ static constexpr skcms_TransferFunction kRec2020_12bit = kRec709; // Rec. ITU-R BT.2100-2 perceptual quantization (PQ) system, value 16. static constexpr skcms_TransferFunction kPQ = - {-5.0f, 203.f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }; + {-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 = { @@ -182,7 +183,7 @@ static constexpr skcms_TransferFunction kSMPTE_ST_428_1 = { // Rec. ITU-R BT.2100-2 hybrid log-gamma (HLG) system, value 18. static constexpr skcms_TransferFunction kHLG = - {-6.0f, 203.f, 1000.0f, 1.2f, 0.0f, 0.0f, 0.0f }; + {-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 diff --git a/gfx/skia/skia/include/core/SkContourMeasure.h b/gfx/skia/skia/include/core/SkContourMeasure.h @@ -8,12 +8,12 @@ #ifndef SkContourMeasure_DEFINED #define SkContourMeasure_DEFINED -#include "include/core/SkPathTypes.h" // IWYU pragma: keep #include "include/core/SkPoint.h" #include "include/core/SkRefCnt.h" #include "include/core/SkScalar.h" #include "include/core/SkSpan.h" -#include "include/core/SkTypes.h" +#include "include/private/base/SkAPI.h" +#include "include/private/base/SkAssert.h" #include "include/private/base/SkTDArray.h" #include <cstddef> @@ -21,7 +21,7 @@ class SkMatrix; class SkPath; -class SkPathBuilder; +enum class SkPathVerb; class SK_API SkContourMeasure : public SkRefCnt { public: @@ -54,12 +54,8 @@ public: then return false (and leave dst untouched). Begin the segment with a moveTo if startWithMoveTo is true */ - [[nodiscard]] bool getSegment(SkScalar startD, SkScalar stopD, SkPathBuilder* dst, - bool startWithMoveTo) const; -#ifdef SK_SUPPORT_MUTABLE_PATHEFFECT [[nodiscard]] bool getSegment(SkScalar startD, SkScalar stopD, SkPath* dst, bool startWithMoveTo) const; -#endif /** Return true if the contour is closed() */ diff --git a/gfx/skia/skia/include/core/SkData.h b/gfx/skia/skia/include/core/SkData.h @@ -9,84 +9,69 @@ #define SkData_DEFINED #include "include/core/SkRefCnt.h" -#include "include/core/SkSpan.h" #include "include/private/base/SkAPI.h" +#include "include/private/base/SkAssert.h" -#include <cstddef> #include <cstdint> #include <cstdio> class SkStream; /** - * SkData holds a data buffer. It can be created to allocate its own buffer - * for the contents, or to share a pointer to the client's buffer. The size and - * address of the contents never change for the lifetime of the data object. + * SkData holds an immutable data buffer. Not only is the data immutable, + * but the actual ptr that is returned (by data() or bytes()) is guaranteed + * to always be the same for the life of this instance. */ class SK_API SkData final : public SkNVRefCnt<SkData> { public: /** - * Returns true if this and rhs are the same size, and contain the same contents. - * All empty objects compare as equal. + * Returns the number of bytes stored. */ - bool operator==(const SkData& rhs) const; - bool operator!=(const SkData& rhs) const { return !(*this == rhs); } + size_t size() const { return fSize; } - /** - * Calls == operator, but first checks if other is null (in which case it returns false) - */ - bool equals(const SkData* other) const { - return (other != nullptr) && *this == *other; - } + bool isEmpty() const { return 0 == fSize; } /** - * Returns the number of bytes stored. + * Returns the ptr to the data. */ - size_t size() const { return fSpan.size(); } + const void* data() const { return fPtr; } /** - * Returns the ptr to the data. + * Like data(), returns a read-only ptr into the data, but in this case + * it is cast to uint8_t*, to make it easy to add an offset to it. */ - const void* data() const { return fSpan.data(); } - - bool empty() const { return fSpan.empty(); } - - const uint8_t* bytes() const { return reinterpret_cast<const uint8_t*>(this->data()); } - - SkSpan<const uint8_t> byteSpan() const { return {this->bytes(), this->size()}; } + const uint8_t* bytes() const { + return reinterpret_cast<const uint8_t*>(fPtr); + } /** * USE WITH CAUTION. - * Be sure other 'owners' of this object are not accessing it in aother thread. + * This call will assert that the refcnt is 1, as a precaution against modifying the + * contents when another client/thread has access to the data. */ void* writable_data() { - return fSpan.data(); + if (fSize) { + // only assert we're unique if we're not empty + SkASSERT(this->unique()); + } + return const_cast<void*>(fPtr); } - /** Attempt to create a deep copy of the original data, using the default allocator. - * - * If offset+length > this->size(), then this returns nullptr. - */ - sk_sp<SkData> copySubset(size_t offset, size_t length) const; - - /** Attempt to return a data that is a reference to a subset of the original data, - * This will never make a deep copy of the contents, but will retain a reference - * to the original data object. - * - * If offset+length > this->size(), then this returns nullptr. - */ - sk_sp<SkData> shareSubset(size_t offset, size_t length); - sk_sp<const SkData> shareSubset(size_t offset, size_t length) const; - /** * Helper to copy a range of the data into a caller-provided buffer. * Returns the actual number of bytes copied, after clamping offset and - * length to the size of this data. If buffer is NULL, it is ignored, and + * length to the size of the data. If buffer is NULL, it is ignored, and * only the computed number of bytes is returned. */ size_t copyRange(size_t offset, size_t length, void* buffer) const; /** + * Returns true if these two objects have the same length and contents, + * effectively returning 0 == memcmp(...) + */ + bool equals(const SkData* other) const; + + /** * Function that, if provided, will be called when the SkData goes out * of scope, allowing for custom allocation/freeing of the data's contents. */ @@ -126,7 +111,7 @@ public: /** * Call this when the data parameter is already const and will outlive the lifetime of the - * SkData. Suitable for globals. + * SkData. Suitable for with const globals. */ static sk_sp<SkData> MakeWithoutCopy(const void* data, size_t length) { return MakeWithProc(data, length, NoopReleaseProc, nullptr); @@ -170,17 +155,10 @@ public: static sk_sp<SkData> MakeFromStream(SkStream*, size_t size); /** - * DEPRECATED variant of src->shareSubset(offset, length) - * - * This variant checks if shaerSubset() returned null (because offset or length were out-of-range) - * and returns an empty SkData, rather than returning null. + * Create a new dataref using a subset of the data in the specified + * src dataref. */ - static sk_sp<SkData> MakeSubset(const SkData* src, size_t offset, size_t length) { - if (sk_sp<SkData> dst = const_cast<SkData*>(src)->shareSubset(offset, length)) { - return dst; - } - return SkData::MakeEmpty(); - } + static sk_sp<SkData> MakeSubset(const SkData* src, size_t offset, size_t length); /** * Returns a new empty dataref (or a reference to a shared empty dataref). @@ -188,18 +166,14 @@ public: */ static sk_sp<SkData> MakeEmpty(); - /** - * DEPRECATED -- use empty() - */ - bool isEmpty() const { return fSpan.empty(); } - private: friend class SkNVRefCnt<SkData>; - ReleaseProc fReleaseProc; - void* fReleaseProcContext; - SkSpan<std::byte> fSpan; + ReleaseProc fReleaseProc; + void* fReleaseProcContext; + const void* fPtr; + size_t fSize; - SkData(SkSpan<std::byte>, ReleaseProc, void* context); + SkData(const void* ptr, size_t size, ReleaseProc, void* context); explicit SkData(size_t size); // inplace new/delete ~SkData(); diff --git a/gfx/skia/skia/include/core/SkExecutor.h b/gfx/skia/skia/include/core/SkExecutor.h @@ -22,31 +22,13 @@ public: static std::unique_ptr<SkExecutor> MakeLIFOThreadPool(int threads = 0, bool allowBorrowing = true); - // A work list is the queue or stack to which work is added and removed. The above two - // factory functions create an executor with only one list while the following two factories - // can create executors with multiple work lists. Having multiple work lists allows for - // prioritization with work being pulled from the lower indexed work lists first - with - // work list '0' being the highest priority. - static std::unique_ptr<SkExecutor> MakeMultiListFIFOThreadPool(int numWorkLists, - int threads = 0, - bool allowBorrowing = true); - static std::unique_ptr<SkExecutor> MakeMultiListLIFOThreadPool(int numWorkLists, - int threads = 0, - bool allowBorrowing = true); - // There is always a default SkExecutor available by calling SkExecutor::GetDefault(). static SkExecutor& GetDefault(); static void SetDefault(SkExecutor*); // Does not take ownership. Not thread safe. // Add work to execute. - virtual void add(std::function<void(void)> fn, int /* workList */) { this->add(std::move(fn)); } - - // deprecated virtual void add(std::function<void(void)>) = 0; - // Returns the number of discarded work units - virtual int discardAllPendingWork() { return 0; } - // If it makes sense for this executor, use this thread to execute work for a little while. virtual void borrow() {} diff --git a/gfx/skia/skia/include/core/SkFont.h b/gfx/skia/skia/include/core/SkFont.h @@ -8,12 +8,9 @@ #ifndef SkFont_DEFINED #define SkFont_DEFINED -#include "include/core/SkPath.h" // IWYU pragma: keep (for SK_HIDE_PATH_EDIT_METHODS) -#include "include/core/SkPoint.h" // IWYU pragma: keep (for unspanned apis) #include "include/core/SkRect.h" #include "include/core/SkRefCnt.h" #include "include/core/SkScalar.h" -#include "include/core/SkSpan.h" #include "include/core/SkTypeface.h" #include "include/core/SkTypes.h" #include "include/private/base/SkTo.h" @@ -21,16 +18,16 @@ #include <cstddef> #include <cstdint> -#include <optional> +#include <type_traits> #include <vector> class SkMatrix; class SkPaint; +class SkPath; enum class SkFontHinting; enum class SkTextEncoding; struct SkFontMetrics; - -namespace skcpu { class GlyphRunListPainter; } +struct SkPoint; /** \class SkFont SkFont controls options applied when drawing and measuring text. @@ -51,10 +48,10 @@ public: */ SkFont(); - /** Constructs SkFont with default values with SkTypeface and size. + /** Constructs SkFont with default values with SkTypeface and size in points. @param typeface font and style used to draw and measure text - @param size EM size in local coordinate units + @param size typographic height of text @return initialized SkFont */ SkFont(sk_sp<SkTypeface> typeface, SkScalar size); @@ -72,7 +69,7 @@ public: and expanded fonts. Horizontal skew emulates oblique fonts. @param typeface font and style used to draw and measure text - @param size EM size in local coordinate units + @param size typographic height of text @param scaleX text horizontal scale @param skewX additional shear on x-axis relative to y-axis @return initialized SkFont @@ -199,7 +196,7 @@ public: /** Returns a font with the same attributes of this font, but with the specified size. Returns nullptr if size is less than zero, infinite, or NaN. - @param size EM size in local coordinate units + @param size typographic height of text @return initialized SkFont */ SkFont makeWithSize(SkScalar size) const; @@ -213,10 +210,9 @@ public: return fTypeface.get(); } - /** Return EM size in local coordinate units. - See https://skia.org/docs/user/coordinates/#local-coordinates . + /** Returns text size in points. - @return EM size in local coordinate units + @return typographic height of text */ SkScalar getSize() const { return fSize; } @@ -251,11 +247,10 @@ public: */ void setTypeface(sk_sp<SkTypeface> tf); - /** Sets the EM size in local coordinate units. - See https://skia.org/docs/user/coordinates/#local-coordinates . + /** Sets text size in points. Has no effect if textSize is not greater than or equal to zero. - @param textSize EM size in local coordinate units + @param textSize typographic height of text */ void setSize(SkScalar textSize); @@ -276,7 +271,7 @@ public: /** Converts text into glyph indices. Returns the number of glyph indices represented by text. SkTextEncoding specifies how text represents characters or glyphs. - glyphs may be empty, to compute the glyph count. + glyphs may be nullptr, to compute the glyph count. Does not check text for valid character codes or valid glyph indices. @@ -292,16 +287,17 @@ public: mapping from the SkTypeface and maps characters not found in the SkTypeface to zero. - If glyphs.size() is not sufficient to store all the glyphs, no glyphs are copied. + If maxGlyphCount is not sufficient to store all the glyphs, no glyphs are copied. The total glyph count is returned for subsequent buffer reallocation. @param text character storage encoded with SkTextEncoding @param byteLength length of character storage in bytes - @param glyphs storage for glyph indices; may be empty - @return number of glyphs represented by text of length byteLength + @param glyphs storage for glyph indices; may be nullptr + @param maxGlyphCount storage capacity + @return number of glyphs represented by text of length byteLength */ - size_t textToGlyphs(const void* text, size_t byteLength, SkTextEncoding encoding, - SkSpan<SkGlyphID> glyphs) const; + int textToGlyphs(const void* text, size_t byteLength, SkTextEncoding encoding, + SkGlyphID glyphs[], int maxGlyphCount) const; /** Returns glyph index for Unicode character. @@ -312,7 +308,7 @@ public: */ SkGlyphID unicharToGlyph(SkUnichar uni) const; - void unicharsToGlyphs(SkSpan<const SkUnichar> src, SkSpan<SkGlyphID> dst) const; + void unicharsToGlyphs(const SkUnichar uni[], int count, SkGlyphID glyphs[]) const; /** Returns number of glyphs represented by text. @@ -324,8 +320,8 @@ public: @param byteLength length of character storage in bytes @return number of glyphs represented by text of length byteLength */ - size_t countText(const void* text, size_t byteLength, SkTextEncoding encoding) const { - return this->textToGlyphs(text, byteLength, encoding, {}); + int countText(const void* text, size_t byteLength, SkTextEncoding encoding) const { + return this->textToGlyphs(text, byteLength, encoding, nullptr, 0); } /** Returns the advance width of text. @@ -356,103 +352,121 @@ public: SkScalar measureText(const void* text, size_t byteLength, SkTextEncoding encoding, SkRect* bounds, const SkPaint* paint) const; + /** DEPRECATED + Retrieves the advance and bounds for each glyph in glyphs. + Both widths and bounds may be nullptr. + If widths is not nullptr, widths must be an array of count entries. + if bounds is not nullptr, bounds must be an array of count entries. + + @param glyphs array of glyph indices to be measured + @param count number of glyphs + @param widths returns text advances for each glyph; may be nullptr + @param bounds returns bounds for each glyph relative to (0, 0); may be nullptr + */ + void getWidths(const SkGlyphID glyphs[], int count, SkScalar widths[], SkRect bounds[]) const { + this->getWidthsBounds(glyphs, count, widths, bounds, nullptr); + } + + // DEPRECATED + void getWidths(const SkGlyphID glyphs[], int count, SkScalar widths[], std::nullptr_t) const { + this->getWidths(glyphs, count, widths); + } + /** Retrieves the advance and bounds for each glyph in glyphs. - widths receives min(widths.size(), glyphs.size()) values. - bounds receives min(bounds.size(), glyphs.size()) values. + Both widths and bounds may be nullptr. + If widths is not nullptr, widths must be an array of count entries. + if bounds is not nullptr, bounds must be an array of count entries. @param glyphs array of glyph indices to be measured + @param count number of glyphs @param widths returns text advances for each glyph - @param bounds returns bounds for each glyph relative to (0, 0) - @param paint optional, specifies stroking, SkPathEffect and SkMaskFilter */ - void getWidthsBounds(SkSpan<const SkGlyphID> glyphs, SkSpan<SkScalar> widths, SkSpan<SkRect> bounds, - const SkPaint* paint) const; + void getWidths(const SkGlyphID glyphs[], int count, SkScalar widths[]) const { + this->getWidthsBounds(glyphs, count, widths, nullptr, nullptr); + } /** Retrieves the advance and bounds for each glyph in glyphs. - widths receives min(widths.size(), glyphs.size()) values. + Both widths and bounds may be nullptr. + If widths is not nullptr, widths must be an array of count entries. + if bounds is not nullptr, bounds must be an array of count entries. @param glyphs array of glyph indices to be measured - @param widths returns text advances for each glyph + @param count number of glyphs + @param widths returns text advances for each glyph; may be nullptr + @param bounds returns bounds for each glyph relative to (0, 0); may be nullptr + @param paint optional, specifies stroking, SkPathEffect and SkMaskFilter */ - void getWidths(SkSpan<const SkGlyphID> glyphs, SkSpan<SkScalar> widths) const { - this->getWidthsBounds(glyphs, widths, {}, nullptr); - } - SkScalar getWidth(SkGlyphID glyph) const { - SkScalar width; - this->getWidthsBounds({&glyph, 1}, {&width, 1}, {}, nullptr); - return width; - } + void getWidthsBounds(const SkGlyphID glyphs[], int count, SkScalar widths[], SkRect bounds[], + const SkPaint* paint) const; + /** Retrieves the bounds for each glyph in glyphs. - bounds receives min(bounds.size(), glyphs.size()) values. + bounds must be an array of count entries. If paint is not nullptr, its stroking, SkPathEffect, and SkMaskFilter fields are respected. @param glyphs array of glyph indices to be measured + @param count number of glyphs @param bounds returns bounds for each glyph relative to (0, 0); may be nullptr @param paint optional, specifies stroking, SkPathEffect, and SkMaskFilter */ - void getBounds(SkSpan<const SkGlyphID> glyphs, SkSpan<SkRect> bounds, + void getBounds(const SkGlyphID glyphs[], int count, SkRect bounds[], const SkPaint* paint) const { - this->getWidthsBounds(glyphs, {}, bounds, paint); - } - SkRect getBounds(SkGlyphID glyph, const SkPaint* paint) const { - SkRect bounds; - this->getBounds({&glyph, 1}, {&bounds, 1}, paint); - return bounds; + this->getWidthsBounds(glyphs, count, nullptr, bounds, paint); } - /** Retrieves the positions for each glyph, beginning at the specified origin. - pos receives min(pos.size(), glyphs.size()) values. + /** Retrieves the positions for each glyph, beginning at the specified origin. The caller + must allocated at least count number of elements in the pos[] array. @param glyphs array of glyph indices to be positioned + @param count number of glyphs @param pos returns glyphs positions @param origin location of the first glyph. Defaults to {0, 0}. */ - void getPos(SkSpan<const SkGlyphID> glyphs, SkSpan<SkPoint> pos, SkPoint origin = {0, 0}) const; + void getPos(const SkGlyphID glyphs[], int count, SkPoint pos[], SkPoint origin = {0, 0}) const; - /** Retrieves the x-positions for each glyph, beginning at the specified origin. - xpos receives min(xpos.size(), glyphs.size()) values. + /** Retrieves the x-positions for each glyph, beginning at the specified origin. The caller + must allocated at least count number of elements in the xpos[] array. @param glyphs array of glyph indices to be positioned + @param count number of glyphs @param xpos returns glyphs x-positions @param origin x-position of the first glyph. Defaults to 0. */ - void getXPos(SkSpan<const SkGlyphID> glyphs, SkSpan<SkScalar> xpos, SkScalar origin = 0) const; + void getXPos(const SkGlyphID glyphs[], int count, SkScalar xpos[], SkScalar origin = 0) const; /** Returns intervals [start, end] describing lines parallel to the advance that intersect * with the glyphs. * * @param glyphs the glyphs to intersect + * @param count the number of glyphs and positions * @param pos the position of each glyph * @param top the top of the line intersecting * @param bottom the bottom of the line intersecting @return array of pairs of x values [start, end]. May be empty. */ - std::vector<SkScalar> getIntercepts(SkSpan<const SkGlyphID> glyphs, - SkSpan<const SkPoint> pos, + std::vector<SkScalar> getIntercepts(const SkGlyphID glyphs[], int count, const SkPoint pos[], SkScalar top, SkScalar bottom, const SkPaint* = nullptr) const; - /* - * If the specified glyph can be represented as a path, return its path. - * If it is not (e.g. it is represented with a bitmap) return {}. - * - * Note: an 'empty' glyph (e.g. what a space " " character might map to) can return - * a path, but that path may have zero contours. - */ - std::optional<SkPath> getPath(SkGlyphID glyphID) const; + /** Modifies path to be the outline of the glyph. + If the glyph has an outline, modifies path to be the glyph's outline and returns true. + The glyph outline may be empty. Degenerate contours in the glyph outline will be skipped. + If glyph is described by a bitmap, returns false and ignores path parameter. -#ifndef SK_HIDE_PATH_EDIT_METHODS + @param glyphID index of glyph + @param path pointer to existing SkPath + @return true if glyphID is described by path + */ bool getPath(SkGlyphID glyphID, SkPath* path) const; -#endif /** Returns path corresponding to glyph array. @param glyphIDs array of glyph indices + @param count number of glyphs @param glyphPathProc function returning one glyph description as path @param ctx function context */ - void getPaths(SkSpan<const SkGlyphID> glyphIDs, + void getPaths(const SkGlyphID glyphIDs[], int count, void (*glyphPathProc)(const SkPath* pathOrNull, const SkMatrix& mx, void* ctx), void* ctx) const; @@ -486,56 +500,6 @@ public: using sk_is_trivially_relocatable = std::true_type; -#ifdef SK_SUPPORT_UNSPANNED_APIS - int textToGlyphs(const void* text, size_t byteLength, SkTextEncoding encoding, - SkGlyphID glyphs[], int maxGlyphCount) const { - return (int)this->textToGlyphs(text, byteLength, encoding, {glyphs, maxGlyphCount}); - } - void unicharsToGlyphs(const SkUnichar uni[], int count, SkGlyphID glyphs[]) const { - this->unicharsToGlyphs({uni, count}, {glyphs, count}); - } - - void getPos(const SkGlyphID glyphs[], int count, SkPoint pos[], SkPoint origin = {0, 0}) const { - this->getPos({glyphs, count}, {pos, count}, origin); - } - void getXPos(const SkGlyphID glyphs[], int count, SkScalar xpos[], SkScalar origin = 0) const { - this->getXPos({glyphs, count}, {xpos, count}, origin); - } - void getPaths(const SkGlyphID glyphIDs[], int count, - void (*glyphPathProc)(const SkPath* pathOrNull, const SkMatrix& mx, void* ctx), - void* ctx) const { - this->getPaths({glyphIDs, count}, glyphPathProc, ctx); - } - void getWidthsBounds(const SkGlyphID glyphs[], int count, SkScalar widths[], SkRect bounds[], - const SkPaint* paint) const { - const auto nw = widths ? count : 0; - const auto nb = bounds ? count : 0; - this->getWidthsBounds({glyphs, count}, {widths, nw}, {bounds, nb}, paint); - } - void getWidths(const SkGlyphID glyphs[], int count, SkScalar widths[], SkRect bounds[]) const { - const auto nw = widths ? count : 0; - const auto nb = bounds ? count : 0; - this->getWidthsBounds({glyphs, count}, {widths, nw}, {bounds, nb}, nullptr); - } - void getWidths(const SkGlyphID glyphs[], int count, SkScalar widths[], std::nullptr_t) const { - this->getWidthsBounds({glyphs, count}, {widths, count}, {}, nullptr); - } - void getWidths(const SkGlyphID glyphs[], int count, SkScalar widths[]) const { - this->getWidthsBounds({glyphs, count}, {widths, count}, {}, nullptr); - } - void getBounds(const SkGlyphID glyphs[], int count, SkRect bounds[], - const SkPaint* paint) const { - this->getWidthsBounds({glyphs, count}, {}, {bounds, count}, paint); - } - - std::vector<SkScalar> getIntercepts(const SkGlyphID glyphs[], int count, const SkPoint pos[], - SkScalar top, SkScalar bottom, - const SkPaint* paint = nullptr) const { - return this->getIntercepts({glyphs, count}, {pos, count}, top, bottom, paint); - } -#endif - - private: enum PrivFlags { kForceAutoHinting_PrivFlag = 1 << 0, @@ -567,7 +531,7 @@ private: bool hasSomeAntiAliasing() const; friend class SkFontPriv; - friend class skcpu::GlyphRunListPainter; + friend class SkGlyphRunListPainterCPU; friend class SkStrikeSpec; friend class SkRemoteGlyphCacheTest; }; diff --git a/gfx/skia/skia/include/core/SkImage.h b/gfx/skia/skia/include/core/SkImage.h @@ -21,6 +21,7 @@ #include <optional> class GrDirectContext; +class GrRecordingContext; class SkBitmap; class SkColorSpace; class SkData; @@ -32,7 +33,6 @@ class SkMipmap; class SkPaint; class SkPicture; class SkPixmap; -class SkRecorder; class SkShader; class SkSurfaceProps; enum SkColorType : int; @@ -42,6 +42,8 @@ enum class SkTileMode; struct SkIPoint; struct SkSamplingOptions; +namespace skgpu::graphite { class Recorder; } + namespace SkImages { /** Caller data passed to RasterReleaseProc; may be nullptr. */ @@ -432,8 +434,8 @@ public: virtual size_t textureSize() const = 0; /** Returns true if SkImage can be drawn on either raster surface or GPU surface. - If recorder is nullptr, tests if SkImage draws on raster surface; - otherwise, tests if SkImage draws on the associated GPU surface. + If context is nullptr, tests if SkImage draws on raster surface; + otherwise, tests if SkImage draws on GPU surface associated with context. SkImage backed by GPU texture may become invalid if associated context is invalid. lazy image may be invalid and may not draw to raster surface or @@ -444,7 +446,7 @@ public: example: https://fiddle.skia.org/c/@Image_isValid */ - virtual bool isValid(SkRecorder*) const = 0; + virtual bool isValid(GrRecordingContext* context) const = 0; /** \enum SkImage::CachingHint CachingHint selects whether Skia may internally cache SkBitmap generated by @@ -718,16 +720,17 @@ public: * The Recorder parameter is required if the original image was created on a graphite Recorder, * but must be nullptr if it was create in some other way (e.g. GrContext, raster, deferred). * - * return nullptr if the requested ColorInfo is not supported, its dimesions are out of range, + * return nullptr if the requested ColorInfo is not supported, its dimesions are out of range, * or if the recorder is null on a graphite Image. */ - sk_sp<SkImage> makeScaled(SkRecorder*, const SkImageInfo&, const SkSamplingOptions&) const; - sk_sp<SkImage> makeScaled(SkRecorder*, + sk_sp<SkImage> makeScaled(skgpu::graphite::Recorder*, const SkImageInfo&, - const SkSamplingOptions&, - const SkSurfaceProps&) const; + const SkSamplingOptions&) const; - sk_sp<SkImage> makeScaled(const SkImageInfo& info, const SkSamplingOptions& sampling) const; + sk_sp<SkImage> makeScaled(const SkImageInfo& info, + const SkSamplingOptions& sampling) const { + return this->makeScaled(nullptr, info, sampling); + } /** Returns encoded SkImage pixels as SkData, if SkImage was created from supported encoded stream format. Platform support for formats vary and may require building @@ -741,8 +744,29 @@ public: */ sk_sp<SkData> refEncodedData() const; + /** Returns subset of this image. + + Returns nullptr if any of the following are true: + - Subset is empty + - Subset is not contained inside the image's bounds + - Pixels in the source image could not be read or copied + - This image is texture-backed and the provided context is null or does not match + the source image's context. + + If the source image was texture-backed, the resulting image will be texture-backed also. + Otherwise, the returned image will be raster-backed. + + @param direct the GrDirectContext of the source image (nullptr is ok if the source image + is not texture-backed). + @param subset bounds of returned SkImage + @return the subsetted image, or nullptr + + example: https://fiddle.skia.org/c/@Image_makeSubset + */ + virtual sk_sp<SkImage> makeSubset(GrDirectContext* direct, const SkIRect& subset) const = 0; + struct RequiredProperties { - bool fMipmapped = false; + bool fMipmapped; bool operator==(const RequiredProperties& other) const { return fMipmapped == other.fMipmapped; @@ -773,7 +797,7 @@ public: @param RequiredProperties properties the returned SkImage must possess (e.g. mipmaps) @return the subsetted image, or nullptr */ - virtual sk_sp<SkImage> makeSubset(SkRecorder*, + virtual sk_sp<SkImage> makeSubset(skgpu::graphite::Recorder*, const SkIRect& subset, RequiredProperties) const = 0; @@ -863,6 +887,25 @@ public: Otherwise, converts pixels from SkImage SkColorSpace to target SkColorSpace. If SkImage colorSpace() returns nullptr, SkImage SkColorSpace is assumed to be sRGB. + If this image is texture-backed, the context parameter is required and must match the + context of the source image. + + @param direct The GrDirectContext in play, if it exists + @param target SkColorSpace describing color range of returned SkImage + @return created SkImage in target SkColorSpace + + example: https://fiddle.skia.org/c/@Image_makeColorSpace + */ + virtual sk_sp<SkImage> makeColorSpace(GrDirectContext* direct, + sk_sp<SkColorSpace> target) const = 0; + + /** Creates SkImage in target SkColorSpace. + Returns nullptr if SkImage could not be created. + + Returns original SkImage if it is in target SkColorSpace. + Otherwise, converts pixels from SkImage SkColorSpace to target SkColorSpace. + If SkImage colorSpace() returns nullptr, SkImage SkColorSpace is assumed to be sRGB. + If this image is graphite-backed, the recorder parameter is required. @param targetColorSpace SkColorSpace describing color range of returned SkImage @@ -870,7 +913,7 @@ public: @param RequiredProperties properties the returned SkImage must possess (e.g. mipmaps) @return created SkImage in target SkColorSpace */ - virtual sk_sp<SkImage> makeColorSpace(SkRecorder*, + virtual sk_sp<SkImage> makeColorSpace(skgpu::graphite::Recorder*, sk_sp<SkColorSpace> targetColorSpace, RequiredProperties) const = 0; @@ -880,6 +923,24 @@ public: Returns original SkImage if it is in target SkColorType and SkColorSpace. + If this image is texture-backed, the context parameter is required and must match the + context of the source image. + + @param direct The GrDirectContext in play, if it exists + @param targetColorType SkColorType of returned SkImage + @param targetColorSpace SkColorSpace of returned SkImage + @return created SkImage in target SkColorType and SkColorSpace + */ + virtual sk_sp<SkImage> makeColorTypeAndColorSpace(GrDirectContext* direct, + SkColorType targetColorType, + sk_sp<SkColorSpace> targetCS) const = 0; + + /** Experimental. + Creates SkImage in target SkColorType and SkColorSpace. + Returns nullptr if SkImage could not be created. + + Returns original SkImage if it is in target SkColorType and SkColorSpace. + If this image is graphite-backed, the recorder parameter is required. @param targetColorType SkColorType of returned SkImage @@ -888,7 +949,7 @@ public: @param RequiredProperties properties the returned SkImage must possess (e.g. mipmaps) @return created SkImage in target SkColorType and SkColorSpace */ - virtual sk_sp<SkImage> makeColorTypeAndColorSpace(SkRecorder*, + virtual sk_sp<SkImage> makeColorTypeAndColorSpace(skgpu::graphite::Recorder*, SkColorType targetColorType, sk_sp<SkColorSpace> targetColorSpace, RequiredProperties) const = 0; diff --git a/gfx/skia/skia/include/core/SkImageGenerator.h b/gfx/skia/skia/include/core/SkImageGenerator.h @@ -18,7 +18,7 @@ #include <cstddef> #include <cstdint> -class SkRecorder; +class GrRecordingContext; class SK_API SkImageGenerator { public: @@ -50,7 +50,9 @@ public: * Can this generator be used to produce images that will be drawable to the specified context * (or to CPU, if context is nullptr)? */ - bool isValid(SkRecorder* recorder) const { return this->onIsValid(recorder); } + bool isValid(GrRecordingContext* context) const { + return this->onIsValid(context); + } /** * Will this generator produce protected content @@ -121,7 +123,7 @@ protected: virtual sk_sp<SkData> onRefEncodedData() { return nullptr; } struct Options {}; virtual bool onGetPixels(const SkImageInfo&, void*, size_t, const Options&) { return false; } - virtual bool onIsValid(SkRecorder*) const { return true; } + virtual bool onIsValid(GrRecordingContext*) const { return true; } virtual bool onIsProtected() const { return false; } virtual bool onQueryYUVAInfo(const SkYUVAPixmapInfo::SupportedDataTypes&, SkYUVAPixmapInfo*) const { return false; } diff --git a/gfx/skia/skia/include/core/SkMatrix.h b/gfx/skia/skia/include/core/SkMatrix.h @@ -9,10 +9,8 @@ #define SkMatrix_DEFINED #include "include/core/SkPoint.h" -#include "include/core/SkPoint3.h" #include "include/core/SkRect.h" #include "include/core/SkScalar.h" -#include "include/core/SkSpan.h" #include "include/core/SkTypes.h" #include "include/private/base/SkFloatingPoint.h" #include "include/private/base/SkMacros.h" @@ -20,24 +18,24 @@ #include <cstdint> #include <cstring> -#include <optional> +struct SkPoint3; struct SkRSXform; struct SkSize; // Remove when clients are updated to live without this #define SK_SUPPORT_LEGACY_MATRIX_RECTTORECT -#ifndef SK_SUPPORT_LEGACY_APPLYPERSPECTIVECLIP - #define SK_SUPPORT_LEGACY_APPLYPERSPECTIVECLIP -#endif - -#ifdef SK_SUPPORT_LEGACY_APPLYPERSPECTIVECLIP +/** + * When we transform points through a matrix containing perspective (the bottom row is something + * other than 0,0,1), the bruteforce math can produce confusing results (since we might divide + * by 0, or a negative w value). By default, methods that map rects and paths will apply + * perspective clipping, but this can be changed by specifying kYes to those methods. + */ enum class SkApplyPerspectiveClip { kNo, //!< Don't pre-clip the geometry before applying the (perspective) matrix kYes, //!< Do pre-clip the geometry before applying the (perspective) matrix }; -#endif /** \class SkMatrix SkMatrix holds a 3x3 matrix for transforming coordinates. This allows mapping @@ -98,8 +96,6 @@ public: [[nodiscard]] static SkMatrix Translate(SkVector t) { return Translate(t.x(), t.y()); } [[nodiscard]] static SkMatrix Translate(SkIVector t) { return Translate(t.x(), t.y()); } - [[nodiscard]] static SkMatrix ScaleTranslate(float sx, float sy, float tx, float ty); - /** Sets SkMatrix to rotate by |deg| about a pivot point at (0, 0). @param deg rotation angle in degrees (positive rotates clockwise) @@ -144,6 +140,25 @@ public: kEnd_ScaleToFit, //!< scales and aligns to right and bottom }; + /** Returns SkMatrix set to scale and translate src to dst. ScaleToFit selects + whether mapping completely fills dst or preserves the aspect ratio, and how to + align src within dst. Returns the identity SkMatrix if src is empty. If dst is + empty, returns SkMatrix set to: + + | 0 0 0 | + | 0 0 0 | + | 0 0 1 | + + @param src SkRect to map from + @param dst SkRect to map to + @param mode How to handle the mapping + @return SkMatrix mapping src to dst + */ + [[nodiscard]] static SkMatrix RectToRect(const SkRect& src, const SkRect& dst, + ScaleToFit mode = kFill_ScaleToFit) { + return MakeRectToRect(src, dst, mode); + } + /** Sets SkMatrix to: | scaleX skewX transX | @@ -514,14 +529,14 @@ public: */ SkMatrix& setTranslateY(SkScalar v) { return this->set(kMTransY, v); } - /** Sets input x-axis perspective factor, which causes mapPoints() to vary input x-axis values + /** Sets input x-axis perspective factor, which causes mapXY() to vary input x-axis values inversely proportional to input y-axis values. @param v perspective factor */ SkMatrix& setPerspX(SkScalar v) { return this->set(kMPersp0, v); } - /** Sets input y-axis perspective factor, which causes mapPoints() to vary input y-axis values + /** Sets input y-axis perspective factor, which causes mapXY() to vary input y-axis values inversely proportional to input x-axis values. @param v perspective factor @@ -1121,73 +1136,82 @@ public: */ SkMatrix& postConcat(const SkMatrix& other); - /** If possible, return a matrix that will transform the src rect to the dst rect. - * If the src is empty, this will return {}. - * If the dst is empty, this will return the zero matrix (degenerate). - */ - static std::optional<SkMatrix> Rect2Rect(const SkRect& src, const SkRect& dst, - ScaleToFit = kFill_ScaleToFit); +#ifndef SK_SUPPORT_LEGACY_MATRIX_RECTTORECT +private: +#endif + /** Sets SkMatrix to scale and translate src SkRect to dst SkRect. stf selects whether + mapping completely fills dst or preserves the aspect ratio, and how to align + src within dst. Returns false if src is empty, and sets SkMatrix to identity. + Returns true if dst is empty, and sets SkMatrix to: - static SkMatrix RectToRectOrIdentity(const SkRect& src, const SkRect& dst, - ScaleToFit stf = kFill_ScaleToFit) { - return Rect2Rect(src, dst, stf).value_or(SkMatrix::I()); - } + | 0 0 0 | + | 0 0 0 | + | 0 0 1 | -#ifdef SK_SUPPORT_LEGACY_MATRIX_RECTTORECT - bool setRectToRect(const SkRect& src, const SkRect& dst, ScaleToFit stf) { - if (auto mx = Rect2Rect(src, dst, stf)) { - *this = *mx; - return true; - } - this->reset(); - return false; - } + @param src SkRect to map from + @param dst SkRect to map to + @return true if SkMatrix can represent SkRect mapping - static SkMatrix MakeRectToRect(const SkRect& src, const SkRect& dst, ScaleToFit stf) { - if (auto mx = Rect2Rect(src, dst, stf)) { - return *mx; - } - return SkMatrix::I(); - } + example: https://fiddle.skia.org/c/@Matrix_setRectToRect + */ + bool setRectToRect(const SkRect& src, const SkRect& dst, ScaleToFit stf); - [[nodiscard]] static SkMatrix RectToRect(const SkRect& src, const SkRect& dst, - ScaleToFit mode = kFill_ScaleToFit) { - return MakeRectToRect(src, dst, mode); + /** Returns SkMatrix set to scale and translate src SkRect to dst SkRect. stf selects + whether mapping completely fills dst or preserves the aspect ratio, and how to + align src within dst. Returns the identity SkMatrix if src is empty. If dst is + empty, returns SkMatrix set to: + + | 0 0 0 | + | 0 0 0 | + | 0 0 1 | + + @param src SkRect to map from + @param dst SkRect to map to + @return SkMatrix mapping src to dst + */ + static SkMatrix MakeRectToRect(const SkRect& src, const SkRect& dst, ScaleToFit stf) { + SkMatrix m; + m.setRectToRect(src, dst, stf); + return m; } +#ifndef SK_SUPPORT_LEGACY_MATRIX_RECTTORECT +public: #endif - /** Compute a matrix from two polygons, such that if the matrix was applied - * to the src polygon, it would produce the dst polygon. - * - * If the size of the two spans are not equal, or if they are > 4, return {}. - * If the resulting matrix is non-invertible, return {}. - * - * example: https://fiddle.skia.org/c/@Matrix_setPolyToPoly - */ - static std::optional<SkMatrix> PolyToPoly(SkSpan<const SkPoint> src, SkSpan<const SkPoint> dst); + /** Sets SkMatrix to map src to dst. count must be zero or greater, and four or less. - bool setPolyToPoly(SkSpan<const SkPoint> src, SkSpan<const SkPoint> dst) { - if (auto mx = PolyToPoly(src, dst)) { - *this = *mx; - return true; - } - return false; - } + If count is zero, sets SkMatrix to identity and returns true. + If count is one, sets SkMatrix to translate and returns true. + If count is two or more, sets SkMatrix to map SkPoint if possible; returns false + if SkMatrix cannot be constructed. If count is four, SkMatrix may include + perspective. + + @param src SkPoint to map from + @param dst SkPoint to map to + @param count number of SkPoint in src and dst + @return true if SkMatrix was constructed successfully - /* - * If this matrix is invertible, return its inverse, else return {}. + example: https://fiddle.skia.org/c/@Matrix_setPolyToPoly */ - std::optional<SkMatrix> invert() const; + bool setPolyToPoly(const SkPoint src[], const SkPoint dst[], int count); - // deprecated + /** Sets inverse to reciprocal matrix, returning true if SkMatrix can be inverted. + Geometrically, if SkMatrix maps from source to destination, inverse SkMatrix + maps from destination to source. If SkMatrix can not be inverted, inverse is + unchanged. + + @param inverse storage for inverted SkMatrix; may be nullptr + @return true if SkMatrix can be inverted + */ [[nodiscard]] bool invert(SkMatrix* inverse) const { - if (auto inv = this->invert()) { + // Allow the trivial case to be inlined. + if (this->isIdentity()) { if (inverse) { - *inverse = *inv; + inverse->reset(); } return true; } - return false; + return this->invertNonIdentity(inverse); } /** Fills affine with identity values in column major order. @@ -1271,14 +1295,13 @@ public: src and dst may point to the same storage. - @param dst span where the transformed points are written - @param src spen where the points are read from - - Note: min(dst.size(), src.size()) is the number of points that will be written to dst. + @param dst storage for mapped SkPoint + @param src SkPoint to transform + @param count number of SkPoint to transform example: https://fiddle.skia.org/c/@Matrix_mapPoints */ - void mapPoints(SkSpan<SkPoint> dst, SkSpan<const SkPoint> src) const; + void mapPoints(SkPoint dst[], const SkPoint src[], int count) const; /** Maps pts SkPoint array of length count in place. SkPoint are mapped by multiplying each SkPoint by SkMatrix. Given: @@ -1300,10 +1323,11 @@ public: Matrix * pt = |D E F| |y| = |Ax+By+C Dx+Ey+F Gx+Hy+I| = ------- , ------- |G H I| |1| Gx+Hy+I Gx+Hy+I - @param pts span of points to be transformed in-place + @param pts storage for mapped SkPoint + @param count number of SkPoint to transform */ - void mapPoints(SkSpan<SkPoint> pts) const { - this->mapPoints(pts, pts); + void mapPoints(SkPoint pts[], int count) const { + this->mapPoints(pts, pts, count); } /** Maps src SkPoint3 array of length count to dst SkPoint3 array, which must of length count or @@ -1319,34 +1343,18 @@ public: Matrix * src = |D E F| |y| = |Ax+By+Cz Dx+Ey+Fz Gx+Hy+Iz| |G H I| |z| - @param dst span where the transformed points are written - @param src spen where the points are read from - - Note: min(dst.size(), src.size()) is the number of points that will be written to dst. + @param dst storage for mapped SkPoint3 array + @param src SkPoint3 array to transform + @param count items in SkPoint3 array to transform example: https://fiddle.skia.org/c/@Matrix_mapHomogeneousPoints */ - void mapHomogeneousPoints(SkSpan<SkPoint3> dst, SkSpan<const SkPoint3> src) const; - - SkPoint3 mapHomogeneousPoint(SkPoint3 src) const { - SkPoint3 dst; - this->mapHomogeneousPoints({&dst, 1}, {&src, 1}); - return dst; - } + void mapHomogeneousPoints(SkPoint3 dst[], const SkPoint3 src[], int count) const; /** * Returns homogeneous points, starting with 2D src points (with implied w = 1). - * - * Note: min(dst.size(), src.size()) is the number of points that will be written to dst. - */ - void mapPointsToHomogeneous(SkSpan<SkPoint3> dst, SkSpan<const SkPoint> src) const; - - SkPoint3 mapPointToHomogeneous(SkPoint src) const { - SkPoint3 dst; - this->mapPointsToHomogeneous({&dst, 1}, {&src, 1}); - return dst; - } + void mapHomogeneousPoints(SkPoint3 dst[], const SkPoint src[], int count) const; /** Returns SkPoint pt multiplied by SkMatrix. Given: @@ -1361,28 +1369,57 @@ public: |G H I| |1| Gx+Hy+I Gx+Hy+I @param p SkPoint to map - @return mapped SkPoint + @return mapped SkPoint */ - SkPoint mapPoint(SkPoint p) const { - if (this->hasPerspective()) { - return this->mapPointPerspective(p); - } else { - return this->mapPointAffine(p); - } + SkPoint mapPoint(SkPoint pt) const { + SkPoint result; + this->mapXY(pt.x(), pt.y(), &result); + return result; } - /* - * If the caller knows the matrix has no perspective, this will inline the - * math, making it more efficient than calling mapPoint(). - */ - SkPoint mapPointAffine(SkPoint p) const { - SkASSERT(!this->hasPerspective()); - return { - (p.fX * fMat[0] + p.fY * fMat[1]) + fMat[2], - (p.fX * fMat[3] + p.fY * fMat[4]) + fMat[5], - }; + /** Maps SkPoint (x, y) to result. SkPoint is mapped by multiplying by SkMatrix. Given: + + | A B C | | x | + Matrix = | D E F |, pt = | y | + | G H I | | 1 | + + result is computed as: + + |A B C| |x| Ax+By+C Dx+Ey+F + Matrix * pt = |D E F| |y| = |Ax+By+C Dx+Ey+F Gx+Hy+I| = ------- , ------- + |G H I| |1| Gx+Hy+I Gx+Hy+I + + @param x x-axis value of SkPoint to map + @param y y-axis value of SkPoint to map + @param result storage for mapped SkPoint + + example: https://fiddle.skia.org/c/@Matrix_mapXY + */ + void mapXY(SkScalar x, SkScalar y, SkPoint* result) const; + + /** Returns SkPoint (x, y) multiplied by SkMatrix. Given: + + | A B C | | x | + Matrix = | D E F |, pt = | y | + | G H I | | 1 | + + result is computed as: + + |A B C| |x| Ax+By+C Dx+Ey+F + Matrix * pt = |D E F| |y| = |Ax+By+C Dx+Ey+F Gx+Hy+I| = ------- , ------- + |G H I| |1| Gx+Hy+I Gx+Hy+I + + @param x x-axis value of SkPoint to map + @param y y-axis value of SkPoint to map + @return mapped SkPoint + */ + SkPoint mapXY(SkScalar x, SkScalar y) const { + SkPoint result; + this->mapXY(x,y, &result); + return result; } + /** Returns (0, 0) multiplied by SkMatrix. Given: | A B C | | 0 | @@ -1402,7 +1439,7 @@ public: y = this->getTranslateY(); if (this->hasPerspective()) { SkScalar w = fMat[kMPersp2]; - if ((bool)w) { w = 1 / w; } + if (w) { w = 1 / w; } x *= w; y *= w; } @@ -1432,14 +1469,13 @@ public: src and dst may point to the same storage. - @param dst span where the transformed vectors are written - @param src spen where the vectors are read from - - Note: min(dst.size(), src.size()) is the number of points that will be written to dst. + @param dst storage for mapped vectors + @param src vectors to transform + @param count number of vectors to transform example: https://fiddle.skia.org/c/@Matrix_mapVectors */ - void mapVectors(SkSpan<SkVector> dst, SkSpan<const SkVector> src) const; + void mapVectors(SkVector dst[], const SkVector src[], int count) const; /** Maps vecs vector array of length count in place, multiplying each vector by SkMatrix, treating SkMatrix translation as zero. Given: @@ -1462,9 +1498,32 @@ public: |G H I| |1| Gx+Hy+I Gx+Hy+I @param vecs vectors to transform, and storage for mapped vectors + @param count number of vectors to transform */ - void mapVectors(SkSpan<SkVector> vecs) const { - this->mapVectors(vecs, vecs); + void mapVectors(SkVector vecs[], int count) const { + this->mapVectors(vecs, vecs, count); + } + + /** Maps vector (dx, dy) to result. Vector is mapped by multiplying by SkMatrix, + treating SkMatrix translation as zero. Given: + + | A B 0 | | dx | + Matrix = | D E 0 |, vec = | dy | + | G H I | | 1 | + + each result vector is computed as: + + |A B 0| |dx| A*dx+B*dy D*dx+E*dy + Matrix * vec = |D E 0| |dy| = |A*dx+B*dy D*dx+E*dy G*dx+H*dy+I| = ----------- , ----------- + |G H I| | 1| G*dx+H*dy+I G*dx+*dHy+I + + @param dx x-axis value of vector to map + @param dy y-axis value of vector to map + @param result storage for mapped vector + */ + void mapVector(SkScalar dx, SkScalar dy, SkVector* result) const { + SkVector vec = { dx, dy }; + this->mapVectors(result, &vec, 1); } /** Returns vector (dx, dy) multiplied by SkMatrix, treating SkMatrix translation as zero. @@ -1484,12 +1543,10 @@ public: @param dy y-axis value of vector to map @return mapped vector */ - SkVector mapVector(SkVector vec) const { - this->mapVectors({&vec, 1}); - return vec; - } SkVector mapVector(SkScalar dx, SkScalar dy) const { - return this->mapVector({dx, dy}); + SkVector vec = { dx, dy }; + this->mapVectors(&vec, &vec, 1); + return vec; } /** Sets dst to bounds of src corners mapped by SkMatrix. @@ -1504,19 +1561,8 @@ public: example: https://fiddle.skia.org/c/@Matrix_mapRect */ - bool mapRect(SkRect* dst, const SkRect& src) const; - -#ifdef SK_SUPPORT_LEGACY_APPLYPERSPECTIVECLIP - bool mapRect(SkRect* dst, const SkRect& src, SkApplyPerspectiveClip) const { - return this->mapRect(dst, src); - } - bool mapRect(SkRect* rect, SkApplyPerspectiveClip) const { - return this->mapRect(rect, *rect); - } - SkRect mapRect(const SkRect& src, SkApplyPerspectiveClip) const { - return this->mapRect(src); - } -#endif + bool mapRect(SkRect* dst, const SkRect& src, + SkApplyPerspectiveClip pc = SkApplyPerspectiveClip::kYes) const; /** Sets rect to bounds of rect corners mapped by SkMatrix. Returns true if mapped corners are computed rect corners. @@ -1527,8 +1573,8 @@ public: @param pc whether to apply perspective clipping @return true if result is equivalent to mapped rect */ - bool mapRect(SkRect* rect) const { - return this->mapRect(rect, *rect); + bool mapRect(SkRect* rect, SkApplyPerspectiveClip pc = SkApplyPerspectiveClip::kYes) const { + return this->mapRect(rect, *rect, pc); } /** Returns bounds of src corners mapped by SkMatrix. @@ -1536,9 +1582,10 @@ public: @param src rectangle to map @return mapped bounds */ - SkRect mapRect(const SkRect& src) const { + SkRect mapRect(const SkRect& src, + SkApplyPerspectiveClip pc = SkApplyPerspectiveClip::kYes) const { SkRect dst; - (void)this->mapRect(&dst, src); + (void)this->mapRect(&dst, src, pc); return dst; } @@ -1572,7 +1619,8 @@ public: */ void mapRectToQuad(SkPoint dst[4], const SkRect& rect) const { // This could potentially be faster if we only transformed each x and y of the rect once. - this->mapPoints({dst, 4}, rect.toQuad()); + rect.toQuad(dst); + this->mapPoints(dst, 4); } /** Sets dst to bounds of src corners mapped by SkMatrix. If matrix contains @@ -1753,7 +1801,29 @@ public: @param ty vertical translation to store */ void setScaleTranslate(SkScalar sx, SkScalar sy, SkScalar tx, SkScalar ty) { - *this = SkMatrix::ScaleTranslate(sx, sy, tx, ty); + fMat[kMScaleX] = sx; + fMat[kMSkewX] = 0; + fMat[kMTransX] = tx; + + fMat[kMSkewY] = 0; + fMat[kMScaleY] = sy; + fMat[kMTransY] = ty; + + fMat[kMPersp0] = 0; + fMat[kMPersp1] = 0; + fMat[kMPersp2] = 1; + + int mask = 0; + if (sx != 1 || sy != 1) { + mask |= kScale_Mask; + } + if (tx != 0.0f || ty != 0.0f) { + mask |= kTranslate_Mask; + } + if (sx != 0 && sy != 0) { + mask |= kRectStaysRect_Mask; + } + this->setTypeMask(mask); } /** Returns true if all elements of the matrix are finite. Returns false if any @@ -1763,43 +1833,6 @@ public: */ bool isFinite() const { return SkIsFinite(fMat, 9); } -#ifdef SK_SUPPORT_UNSPANNED_APIS - bool setPolyToPoly(const SkPoint src[], const SkPoint dst[], int count) { - return this->setPolyToPoly({src, count}, {dst, count}); - } - - void mapPoints(SkPoint dst[], const SkPoint src[], int count) const { - this->mapPoints({dst, count}, {src, count}); - } - void mapPoints(SkPoint pts[], int count) const { - this->mapPoints(pts, pts, count); - } - - void mapHomogeneousPoints(SkPoint3 dst[], const SkPoint3 src[], int count) const { - this->mapHomogeneousPoints({dst, count}, {src, count}); - } - void mapHomogeneousPoints(SkPoint3 dst[], const SkPoint src[], int count) const { - this->mapPointsToHomogeneous({dst, count}, {src, count}); - } - - void mapVectors(SkVector dst[], const SkVector src[], int count) const { - this->mapVectors({dst, count}, {src, count}); - } - void mapVectors(SkVector vecs[], int count) const { - this->mapVectors({vecs, count}); - } - void mapXY(SkScalar x, SkScalar y, SkPoint* result) const { - *result = this->mapPoint({x, y}); - } - SkPoint mapXY(SkScalar x, SkScalar y) const { - return this->mapPoint({x, y}); - } - void mapVector(SkScalar dx, SkScalar dy, SkVector* result) const { - SkVector vec = { dx, dy }; - this->mapVectors({result, 1}, {&vec, 1}); - } -#endif - private: /** Set if the matrix will map a rectangle to another rectangle. This can be true if the matrix is scale-only, or rotates a multiple of @@ -1888,12 +1921,17 @@ private: } } - /* - * If the caller knows the matrix perspective, this dos the extra work to - * correctly compute the mapping. mapPoint() calls this, but only after - * checking if the matrix includes perspective. - */ - SkPoint mapPointPerspective(SkPoint pt) const; + typedef void (*MapXYProc)(const SkMatrix& mat, SkScalar x, SkScalar y, + SkPoint* result); + + static MapXYProc GetMapXYProc(TypeMask mask) { + SkASSERT((mask & ~kAllMasks) == 0); + return gMapXYProcs[mask & kAllMasks]; + } + + MapXYProc getMapXYProc() const { + return GetMapXYProc(this->getType()); + } typedef void (*MapPtsProc)(const SkMatrix& mat, SkPoint dst[], const SkPoint src[], int count); @@ -1907,10 +1945,22 @@ private: return GetMapPtsProc(this->getType()); } + [[nodiscard]] bool invertNonIdentity(SkMatrix* inverse) const; + static bool Poly2Proc(const SkPoint[], SkMatrix*); static bool Poly3Proc(const SkPoint[], SkMatrix*); static bool Poly4Proc(const SkPoint[], SkMatrix*); + static void Identity_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*); + static void Trans_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*); + static void Scale_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*); + static void ScaleTrans_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*); + static void Rot_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*); + static void RotTrans_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*); + static void Persp_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*); + + static const MapXYProc gMapXYProcs[]; + static void Identity_pts(const SkMatrix&, SkPoint[], const SkPoint[], int); static void Trans_pts(const SkMatrix&, SkPoint dst[], const SkPoint[], int); static void Scale_pts(const SkMatrix&, SkPoint dst[], const SkPoint[], int); diff --git 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 142 +#define SK_MILESTONE 136 #endif diff --git a/gfx/skia/skia/include/core/SkPath.h b/gfx/skia/skia/include/core/SkPath.h @@ -9,13 +9,11 @@ #define SkPath_DEFINED #include "include/core/SkMatrix.h" -#include "include/core/SkPathIter.h" #include "include/core/SkPathTypes.h" #include "include/core/SkPoint.h" #include "include/core/SkRect.h" #include "include/core/SkRefCnt.h" #include "include/core/SkScalar.h" -#include "include/core/SkSpan.h" #include "include/core/SkTypes.h" #include "include/private/base/SkDebug.h" #include "include/private/base/SkTo.h" @@ -24,18 +22,20 @@ #include <atomic> #include <cstddef> #include <cstdint> -#include <optional> +#include <initializer_list> #include <tuple> +#include <type_traits> +struct SkArc; class SkData; class SkPathRef; class SkRRect; class SkWStream; enum class SkPathConvexity; -struct SkPathRaw; +enum class SkPathFirstDirection; struct SkPathVerbAnalysis; -// WIP -- define this locally, and fix call-sites to use SkPathBuilder (skbug.com/40040287) +// WIP -- define this locally, and fix call-sites to use SkPathBuilder (skbug.com/9000) //#define SK_HIDE_PATH_EDIT_METHODS /** \class SkPath @@ -59,7 +59,7 @@ struct SkPathVerbAnalysis; class SK_API SkPath { public: /** - * Create a new path with the specified spans. + * Create a new path with the specified segments. * * The points and weights arrays are read in order, based on the sequence of verbs. * @@ -77,38 +77,34 @@ public: * with a Move verb, followed by 0 or more segments: Line, Quad, Conic, Cubic, followed * by an optional Close. */ - static SkPath Raw(SkSpan<const SkPoint> pts, - SkSpan<const SkPathVerb> verbs, - SkSpan<const SkScalar> conics, - SkPathFillType, bool isVolatile = false); + static SkPath Make(const SkPoint[], int pointCount, + const uint8_t[], int verbCount, + const SkScalar[], int conicWeightCount, + SkPathFillType, bool isVolatile = false); - static SkPath Rect(const SkRect&, SkPathDirection = SkPathDirection::kDefault, + static SkPath Rect(const SkRect&, SkPathDirection = SkPathDirection::kCW, unsigned startIndex = 0); - static SkPath Oval(const SkRect&, SkPathDirection = SkPathDirection::kDefault); + static SkPath Oval(const SkRect&, SkPathDirection = SkPathDirection::kCW); static SkPath Oval(const SkRect&, SkPathDirection, unsigned startIndex); static SkPath Circle(SkScalar center_x, SkScalar center_y, SkScalar radius, SkPathDirection dir = SkPathDirection::kCW); - static SkPath RRect(const SkRRect&, SkPathDirection dir = SkPathDirection::kDefault); + static SkPath RRect(const SkRRect&, SkPathDirection dir = SkPathDirection::kCW); static SkPath RRect(const SkRRect&, SkPathDirection, unsigned startIndex); static SkPath RRect(const SkRect& bounds, SkScalar rx, SkScalar ry, - SkPathDirection dir = SkPathDirection::kDefault); + SkPathDirection dir = SkPathDirection::kCW); - static SkPath Polygon(SkSpan<const SkPoint> pts, bool isClosed, - SkPathFillType fillType = SkPathFillType::kDefault, + static SkPath Polygon(const SkPoint pts[], int count, bool isClosed, + SkPathFillType = SkPathFillType::kWinding, bool isVolatile = false); - static SkPath Line(const SkPoint a, const SkPoint b) { - return Polygon({a, b}, false); + static SkPath Polygon(const std::initializer_list<SkPoint>& list, bool isClosed, + SkPathFillType fillType = SkPathFillType::kWinding, + bool isVolatile = false) { + return Polygon(list.begin(), SkToInt(list.size()), isClosed, fillType, isVolatile); } - // Deprecated: use Raw() - static SkPath Make(SkSpan<const SkPoint> pts, - SkSpan<const uint8_t> verbs, - SkSpan<const SkScalar> conics, - SkPathFillType fillType, - bool isVolatile = false) { - return Raw(pts, {reinterpret_cast<const SkPathVerb*>(verbs.data()), verbs.size()}, - conics, fillType, isVolatile); + static SkPath Line(const SkPoint a, const SkPoint b) { + return Polygon({a, b}, false); } /** Constructs an empty SkPath. By default, SkPath has no verbs, no SkPoint, and no weights. @@ -147,6 +143,13 @@ public: return *this; } + /** Returns a copy of this path in the current state, and resets the path to empty. */ + SkPath detach() { + SkPath result = *this; + this->reset(); + return result; + } + /** Constructs a copy of an existing path. SkPath assignment makes two paths identical by value. Internally, assignment shares pointer values. The underlying verb array, SkPoint array and weights @@ -206,18 +209,19 @@ public: one (this Point_Array); will work with values outside of this range. - interpolate() returns an empty SkPath if SkPoint array is not the same size - as ending SkPoint array. Call isInterpolatable() to check SkPath compatibility - prior to calling makeInterpolate(). + interpolate() returns false and leaves out unchanged if SkPoint array is not + the same size as ending SkPoint array. Call isInterpolatable() to check SkPath + compatibility prior to calling interpolate(). @param ending SkPoint array averaged with this SkPoint array @param weight contribution of this SkPoint array, and one minus contribution of ending SkPoint array - @return SkPath replaced by interpolated averages + @param out SkPath replaced by interpolated averages + @return true if SkPath contain same number of SkPoint example: https://fiddle.skia.org/c/@Path_interpolate */ - SkPath makeInterpolate(const SkPath& ending, SkScalar weight) const; + bool interpolate(const SkPath& ending, SkScalar weight, SkPath* out) const; /** Returns SkPathFillType, the rule used to fill SkPath. @@ -225,23 +229,26 @@ public: */ SkPathFillType getFillType() const { return (SkPathFillType)fFillType; } - /** Creates an SkPath with the same properties and data, and with SkPathFillType - set to newFillType. + /** Sets FillType, the rule used to fill SkPath. While there is no check + that ft is legal, values outside of FillType are not supported. */ - SkPath makeFillType(SkPathFillType newFillType) const; + void setFillType(SkPathFillType ft) { + fFillType = SkToU8(ft); + } - /** Returns if SkPathFillType describes area outside SkPath geometry. The inverse fill area + /** Returns if FillType describes area outside SkPath geometry. The inverse fill area extends indefinitely. @return true if FillType is kInverseWinding or kInverseEvenOdd */ bool isInverseFillType() const { return SkPathFillType_IsInverse(this->getFillType()); } - /** Creates an SkPath with the same properties and data, and with SkPathFillType - replaced with its inverse. The inverse of SkPathFillType describes the area unmodified - by the original FillType. + /** Replaces FillType with its inverse. The inverse of FillType describes the area + unmodified by the original FillType. */ - SkPath makeToggleInverseFillType() const; + void toggleInverseFillType() { + fFillType ^= 2; + } /** Returns true if the path is convex. If necessary, it will first compute the convexity. */ @@ -274,6 +281,39 @@ public: */ bool isRRect(SkRRect* rrect) const; + /** Returns true if path is representable as an oval arc. In other words, could this + path be drawn using SkCanvas::drawArc. + + arc receives parameters of arc + + @param arc storage for arc; may be nullptr + @return true if SkPath contains only a single arc from an oval + */ + bool isArc(SkArc* arc) const; + + /** Sets SkPath to its initial state. + Removes verb array, SkPoint array, and weights, and sets FillType to kWinding. + Internal storage associated with SkPath is released. + + @return reference to SkPath + + example: https://fiddle.skia.org/c/@Path_reset + */ + SkPath& reset(); + + /** Sets SkPath to its initial state, preserving internal storage. + Removes verb array, SkPoint array, and weights, and sets FillType to kWinding. + Internal storage associated with SkPath is retained. + + Use rewind() instead of reset() if SkPath storage will be reused and performance + is critical. + + @return reference to SkPath + + example: https://fiddle.skia.org/c/@Path_rewind + */ + SkPath& rewind(); + /** Returns if SkPath is empty. Empty SkPath may have FillType but has no SkPoint, SkPath::Verb, or conic weight. SkPath() constructs empty SkPath; reset() and rewind() make SkPath empty. @@ -311,9 +351,9 @@ public: return SkToBool(fIsVolatile); } - /** Return a copy of SkPath with isVolatile indicating whether it will be altered - or discarded by the caller after it is drawn. SkPath by default have volatile - set false, allowing Skia to attach a cache of data which speeds repeated drawing. + /** Specifies whether SkPath is volatile; whether it will be altered or discarded + by the caller after it is drawn. SkPath by default have volatile set false, allowing + Skia to attach a cache of data which speeds repeated drawing. Mark temporary paths, discarded or modified after use, as volatile to inform Skia that the path need not be cached. @@ -325,9 +365,12 @@ public: GPU surface SkPath draws are affected by volatile for some shadows and concave geometries. @param isVolatile true if caller will alter SkPath after drawing - @return SkPath + @return reference to SkPath */ - SkPath makeIsVolatile(bool isVolatile) const; + SkPath& setIsVolatile(bool isVolatile) { + fIsVolatile = isVolatile; + return *this; + } /** Tests if line between SkPoint pair is degenerate. Line with no length or that moves a very short distance is degenerate; it is @@ -407,15 +450,17 @@ public: */ SkPoint getPoint(int index) const; - /** Returns number of points in SkPath. - Copies N points from the path into the span, where N = min(#points, span capacity) + /** Returns number of points in SkPath. Up to max points are copied. + points may be nullptr; then, max must be zero. + If max is greater than number of points, excess points storage is unaltered. - @param points span to receive the points. may be empty - @return the number of points in the path + @param points storage for SkPath SkPoint array. May be nullptr + @param max maximum to copy; must be greater than or equal to zero + @return SkPath SkPoint array length example: https://fiddle.skia.org/c/@Path_getPoints */ - size_t getPoints(SkSpan<SkPoint> points) const; + int getPoints(SkPoint points[], int max) const; /** Returns the number of verbs: kMove_Verb, kLine_Verb, kQuad_Verb, kConic_Verb, kCubic_Verb, and kClose_Verb; added to SkPath. @@ -426,15 +471,16 @@ public: */ int countVerbs() const; - /** Returns number of points in SkPath. - Copies N points from the path into the span, where N = min(#points, span capacity) + /** Returns the number of verbs in the path. Up to max verbs are copied. The + verbs are copied as one byte per verb. - @param verbs span to store the verbs. may be empty. - @return the number of verbs in the path + @param verbs storage for verbs, may be nullptr + @param max maximum number to copy into verbs + @return the actual number of verbs in the path example: https://fiddle.skia.org/c/@Path_getVerbs */ - size_t getVerbs(SkSpan<uint8_t> verbs) const; + int getVerbs(uint8_t verbs[], int max) const; /** Returns the approximate byte size of the SkPath in memory. @@ -442,6 +488,20 @@ public: */ size_t approximateBytesUsed() const; + /** Exchanges the verb array, SkPoint array, weights, and SkPath::FillType with other. + Cached state is also exchanged. swap() internally exchanges pointers, so + it is lightweight and does not allocate memory. + + swap() usage has largely been replaced by operator=(const SkPath& path). + SkPath do not copy their content on assignment until they are written to, + making assignment as efficient as swap(). + + @param other SkPath exchanged by value + + example: https://fiddle.skia.org/c/@Path_swap + */ + void swap(SkPath& other); + /** Returns minimum and maximum axes values of SkPoint array. Returns (0, 0, 0, 0) if SkPath contains no points. Returned bounds width and height may be larger or smaller than area affected when SkPath is drawn. @@ -500,301 +560,40 @@ public: */ bool conservativelyContainsRect(const SkRect& rect) const; - /** \enum SkPath::ArcSize - Four oval parts with radii (rx, ry) start at last SkPath SkPoint and ends at (x, y). - ArcSize and Direction select one of the four oval parts. - */ - enum ArcSize { - kSmall_ArcSize, //!< smaller of arc pair - kLarge_ArcSize, //!< larger of arc pair - }; - - /** Approximates conic with quad array. Conic is constructed from start SkPoint p0, - control SkPoint p1, end SkPoint p2, and weight w. - Quad array is stored in pts; this storage is supplied by caller. - Maximum quad count is 2 to the pow2. - Every third point in array shares last SkPoint of previous quad and first SkPoint of - next quad. Maximum pts storage size is given by: - (1 + 2 * (1 << pow2)) * sizeof(SkPoint). - - Returns quad count used the approximation, which may be smaller - than the number requested. - - conic weight determines the amount of influence conic control point has on the curve. - w less than one represents an elliptical section. w greater than one represents - a hyperbolic section. w equal to one represents a parabolic section. - - Two quad curves are sufficient to approximate an elliptical conic with a sweep - of up to 90 degrees; in this case, set pow2 to one. - - @param p0 conic start SkPoint - @param p1 conic control SkPoint - @param p2 conic end SkPoint - @param w conic weight - @param pts storage for quad array - @param pow2 quad count, as power of two, normally 0 to 5 (1 to 32 quad curves) - @return number of quad curves written to pts - */ - static int ConvertConicToQuads(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2, - SkScalar w, SkPoint pts[], int pow2); - - /** Returns true if SkPath is equivalent to SkRect when filled. - If false: rect, isClosed, and direction are unchanged. - If true: rect, isClosed, and direction are written to if not nullptr. - - rect may be smaller than the SkPath bounds. SkPath bounds may include kMove_Verb points - that do not alter the area drawn by the returned rect. - - @param rect storage for bounds of SkRect; may be nullptr - @param isClosed storage set to true if SkPath is closed; may be nullptr - @param direction storage set to SkRect direction; may be nullptr - @return true if SkPath contains SkRect - - example: https://fiddle.skia.org/c/@Path_isRect - */ - bool isRect(SkRect* rect, bool* isClosed = nullptr, SkPathDirection* direction = nullptr) const; - - /** \enum SkPath::AddPathMode - AddPathMode chooses how addPath() appends. Adding one SkPath to another can extend - the last contour or start a new contour. - */ - enum AddPathMode { - /** Contours are appended to the destination path as new contours. - */ - kAppend_AddPathMode, - /** Extends the last contour of the destination path with the first countour - of the source path, connecting them with a line. If the last contour is - closed, a new empty contour starting at its start point is extended instead. - If the destination path is empty, the result is the source path. - The last path of the result is closed only if the last path of the source is. - */ - kExtend_AddPathMode, - }; - - /** Returns SkPath with SkPoint array offset by (dx, dy). - - @param dx offset added to SkPoint array x-axis coordinates - @param dy offset added to SkPoint array y-axis coordinates - */ - SkPath makeOffset(SkScalar dx, SkScalar dy) const { - SkPath dst; - this->offset(dx, dy, &dst); - return dst; - } - - /** Return a copy of SkPath with verb array, SkPoint array, and weight transformed - by matrix. makeTransform may change verbs and increase their number. - - @param matrix SkMatrix to apply to SkPath - @param pc whether to apply perspective clipping - @return SkPath - */ - SkPath makeTransform(const SkMatrix& matrix) const { - SkPath dst; - this->transform(matrix, &dst); - return dst; - } - - SkPath makeScale(SkScalar sx, SkScalar sy) const { - return this->makeTransform(SkMatrix::Scale(sx, sy)); - } - -#ifdef SK_SUPPORT_LEGACY_APPLYPERSPECTIVECLIP - void transform(const SkMatrix& matrix, SkPath* dst, SkApplyPerspectiveClip) const { - this->transform(matrix, dst); - } - void transform(const SkMatrix& matrix, SkApplyPerspectiveClip) { - this->transform(matrix); - } - SkPath makeTransform(const SkMatrix& m, SkApplyPerspectiveClip) const { - return this->makeTransform(m); - } -#endif - - /** Return the last point, or {} - - @return The last if the path contains one or more SkPoint, else returns {} - - example: https://fiddle.skia.org/c/@Path_getLastPt - */ - std::optional<SkPoint> getLastPt() const; - - // DEPRECATED - bool getLastPt(SkPoint* lastPt) const { - if (auto lp = this->getLastPt()) { - if (lastPt) { - *lastPt = *lp; - } - return true; - } - if (lastPt) { - *lastPt = {0, 0}; - } - return false; - } - - /** \enum SkPath::SegmentMask - SegmentMask constants correspond to each drawing Verb type in SkPath; for - instance, if SkPath only contains lines, only the kLine_SegmentMask bit is set. - */ - enum SegmentMask { - kLine_SegmentMask = kLine_SkPathSegmentMask, - kQuad_SegmentMask = kQuad_SkPathSegmentMask, - kConic_SegmentMask = kConic_SkPathSegmentMask, - kCubic_SegmentMask = kCubic_SkPathSegmentMask, - }; - - /** Returns a mask, where each set bit corresponds to a SegmentMask constant - if SkPath contains one or more verbs of that type. - Returns zero if SkPath contains no lines, or curves: quads, conics, or cubics. - - getSegmentMasks() returns a cached result; it is very fast. - - @return SegmentMask bits or zero - */ - uint32_t getSegmentMasks() const; - - /** \enum SkPath::Verb - Verb instructs SkPath how to interpret one or more SkPoint and optional conic weight; - manage contour, and terminate SkPath. - */ - enum Verb { - kMove_Verb = static_cast<int>(SkPathVerb::kMove), - kLine_Verb = static_cast<int>(SkPathVerb::kLine), - kQuad_Verb = static_cast<int>(SkPathVerb::kQuad), - kConic_Verb = static_cast<int>(SkPathVerb::kConic), - kCubic_Verb = static_cast<int>(SkPathVerb::kCubic), - kClose_Verb = static_cast<int>(SkPathVerb::kClose), - kDone_Verb = kClose_Verb + 1 - }; - - /** Specifies whether SkPath is volatile; whether it will be altered or discarded - by the caller after it is drawn. SkPath by default have volatile set false, allowing - Skia to attach a cache of data which speeds repeated drawing. - - Mark temporary paths, discarded or modified after use, as volatile - to inform Skia that the path need not be cached. - - Mark animating SkPath volatile to improve performance. - Mark unchanging SkPath non-volatile to improve repeated rendering. - - raster surface SkPath draws are affected by volatile for some shadows. - GPU surface SkPath draws are affected by volatile for some shadows and concave geometries. - - @param isVolatile true if caller will alter SkPath after drawing - @return reference to SkPath - */ - SkPath& setIsVolatile(bool isVolatile) { - fIsVolatile = isVolatile; - return *this; - } - - /** Exchanges the verb array, SkPoint array, weights, and SkPath::FillType with other. - Cached state is also exchanged. swap() internally exchanges pointers, so - it is lightweight and does not allocate memory. - - swap() usage has largely been replaced by operator=(const SkPath& path). - SkPath do not copy their content on assignment until they are written to, - making assignment as efficient as swap(). - - @param other SkPath exchanged by value - - example: https://fiddle.skia.org/c/@Path_swap - */ - void swap(SkPath& other); - - /** Interpolates between SkPath with SkPoint array of equal size. - Copy verb array and weights to out, and set out SkPoint array to a weighted - average of this SkPoint array and ending SkPoint array, using the formula: - (Path Point * weight) + ending Point * (1 - weight). - - weight is most useful when between zero (ending SkPoint array) and - one (this Point_Array); will work with values outside of this - range. - - interpolate() returns false and leaves out unchanged if SkPoint array is not - the same size as ending SkPoint array. Call isInterpolatable() to check SkPath - compatibility prior to calling interpolate(). - - @param ending SkPoint array averaged with this SkPoint array - @param weight contribution of this SkPoint array, and - one minus contribution of ending SkPoint array - @param out SkPath replaced by interpolated averages - @return true if SkPath contain same number of SkPoint - - example: https://fiddle.skia.org/c/@Path_interpolate - */ - bool interpolate(const SkPath& ending, SkScalar weight, SkPath* out) const; + /** Grows SkPath verb array, SkPoint array, and conics to contain additional space. + May improve performance and use less memory by + reducing the number and size of allocations when creating SkPath. - /** Sets SkPathFillType, the rule used to fill SkPath. While there is no - check that ft is legal, values outside of SkPathFillType are not supported. - */ - void setFillType(SkPathFillType ft) { - fFillType = SkToU8(ft); - } + @param extraPtCount number of additional SkPoint to allocate + @param extraVerbCount number of additional verbs + @param extraConicCount number of additional conics - /** Replaces SkPathFillType with its inverse. The inverse of SkPathFillType describes the area - unmodified by the original SkPathFillType. + example: https://fiddle.skia.org/c/@Path_incReserve */ - void toggleInverseFillType() { - fFillType ^= 2; - } + void incReserve(int extraPtCount, int extraVerbCount = 0, int extraConicCount = 0); #ifdef SK_HIDE_PATH_EDIT_METHODS private: #endif - /** Returns a copy of this path in the current state, and resets the path to empty. */ - SkPath detach() { - SkPath result = *this; - this->reset(); - return result; - } - - /** Sets SkPath to its initial state. - Removes verb array, SkPoint array, and weights, and sets FillType to kWinding. - Internal storage associated with SkPath is released. - - @return reference to SkPath - - example: https://fiddle.skia.org/c/@Path_reset - */ - SkPath& reset(); - - /** Sets SkPath to its initial state, preserving internal storage. - Removes verb array, SkPoint array, and weights, and sets FillType to kWinding. - Internal storage associated with SkPath is retained. - Use rewind() instead of reset() if SkPath storage will be reused and performance - is critical. + /** Adds beginning of contour at SkPoint (x, y). - @return reference to SkPath + @param x x-axis value of contour start + @param y y-axis value of contour start + @return reference to SkPath - example: https://fiddle.skia.org/c/@Path_rewind + example: https://fiddle.skia.org/c/@Path_moveTo */ - SkPath& rewind(); - - /** Grows SkPath verb array, SkPoint array, and conics to contain additional space. - May improve performance and use less memory by - reducing the number and size of allocations when creating SkPath. + SkPath& moveTo(SkScalar x, SkScalar y); - @param extraPtCount number of additional SkPoint to allocate - @param extraVerbCount number of additional verbs - @param extraConicCount number of additional conics + /** Adds beginning of contour at SkPoint p. - example: https://fiddle.skia.org/c/@Path_incReserve + @param p contour start + @return reference to SkPath */ - void incReserve(int extraPtCount, int extraVerbCount = 0, int extraConicCount = 0); - - /** Specifies the beginning of contour. If the previous verb was a "move" verb, - * then this just replaces the point value of that move, otherwise it appends a new - * "move" verb to the path using the point. - * - * Thus, each contour can only have 1 move verb in it (the last one specified). - */ - SkPath& moveTo(SkPoint p) { + SkPath& moveTo(const SkPoint& p) { return this->moveTo(p.fX, p.fY); } - SkPath& moveTo(SkScalar x, SkScalar y); /** Adds beginning of contour relative to last point. If SkPath is empty, starts contour at (dx, dy). @@ -1117,6 +916,15 @@ private: return this->arcTo(p1.fX, p1.fY, p2.fX, p2.fY, radius); } + /** \enum SkPath::ArcSize + Four oval parts with radii (rx, ry) start at last SkPath SkPoint and ends at (x, y). + ArcSize and Direction select one of the four oval parts. + */ + enum ArcSize { + kSmall_ArcSize, //!< smaller of arc pair + kLarge_ArcSize, //!< larger of arc pair + }; + /** Appends arc to SkPath. Arc is implemented by one or more conics weighted to describe part of oval with radii (rx, ry) rotated by xAxisRotate degrees. Arc curves from last SkPath SkPoint to (x, y), choosing one of four possible routes: @@ -1215,6 +1023,59 @@ private: */ SkPath& close(); +#ifdef SK_HIDE_PATH_EDIT_METHODS +public: +#endif + + /** Approximates conic with quad array. Conic is constructed from start SkPoint p0, + control SkPoint p1, end SkPoint p2, and weight w. + Quad array is stored in pts; this storage is supplied by caller. + Maximum quad count is 2 to the pow2. + Every third point in array shares last SkPoint of previous quad and first SkPoint of + next quad. Maximum pts storage size is given by: + (1 + 2 * (1 << pow2)) * sizeof(SkPoint). + + Returns quad count used the approximation, which may be smaller + than the number requested. + + conic weight determines the amount of influence conic control point has on the curve. + w less than one represents an elliptical section. w greater than one represents + a hyperbolic section. w equal to one represents a parabolic section. + + Two quad curves are sufficient to approximate an elliptical conic with a sweep + of up to 90 degrees; in this case, set pow2 to one. + + @param p0 conic start SkPoint + @param p1 conic control SkPoint + @param p2 conic end SkPoint + @param w conic weight + @param pts storage for quad array + @param pow2 quad count, as power of two, normally 0 to 5 (1 to 32 quad curves) + @return number of quad curves written to pts + */ + static int ConvertConicToQuads(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2, + SkScalar w, SkPoint pts[], int pow2); + + /** Returns true if SkPath is equivalent to SkRect when filled. + If false: rect, isClosed, and direction are unchanged. + If true: rect, isClosed, and direction are written to if not nullptr. + + rect may be smaller than the SkPath bounds. SkPath bounds may include kMove_Verb points + that do not alter the area drawn by the returned rect. + + @param rect storage for bounds of SkRect; may be nullptr + @param isClosed storage set to true if SkPath is closed; may be nullptr + @param direction storage set to SkRect direction; may be nullptr + @return true if SkPath contains SkRect + + example: https://fiddle.skia.org/c/@Path_isRect + */ + bool isRect(SkRect* rect, bool* isClosed = nullptr, SkPathDirection* direction = nullptr) const; + +#ifdef SK_HIDE_PATH_EDIT_METHODS +private: +#endif + /** Adds a new contour to the path, defined by the rect, and wound in the specified direction. The verbs added to the path will be: @@ -1341,7 +1202,7 @@ private: @param dir SkPath::Direction to wind SkRRect @return reference to SkPath */ - SkPath& addRoundRect(const SkRect& rect, SkSpan<const SkScalar> radii, + SkPath& addRoundRect(const SkRect& rect, const SkScalar radii[], SkPathDirection dir = SkPathDirection::kCW); /** Adds rrect to SkPath, creating a new closed contour. If @@ -1387,7 +1248,42 @@ private: example: https://fiddle.skia.org/c/@Path_addPoly */ - SkPath& addPoly(SkSpan<const SkPoint> pts, bool close); + SkPath& addPoly(const SkPoint pts[], int count, bool close); + + /** Adds contour created from list. Contour added starts at list[0], then adds a line + for every additional SkPoint in list. If close is true, appends kClose_Verb to SkPath, + connecting last and first SkPoint in list. + + If list is empty, append kMove_Verb to path. + + @param list array of SkPoint + @param close true to add line connecting contour end and start + @return reference to SkPath + */ + SkPath& addPoly(const std::initializer_list<SkPoint>& list, bool close) { + return this->addPoly(list.begin(), SkToInt(list.size()), close); + } + +#ifdef SK_HIDE_PATH_EDIT_METHODS +public: +#endif + + /** \enum SkPath::AddPathMode + AddPathMode chooses how addPath() appends. Adding one SkPath to another can extend + the last contour or start a new contour. + */ + enum AddPathMode { + /** Contours are appended to the destination path as new contours. + */ + kAppend_AddPathMode, + /** Extends the last contour of the destination path with the first countour + of the source path, connecting them with a line. If the last contour is + closed, a new empty contour starting at its start point is extended instead. + If the destination path is empty, the result is the source path. + The last path of the result is closed only if the last path of the source is. + */ + kExtend_AddPathMode, + }; /** Appends src to SkPath, offset by (dx, dy). @@ -1477,7 +1373,8 @@ private: example: https://fiddle.skia.org/c/@Path_transform */ - void transform(const SkMatrix& matrix, SkPath* dst) const; + void transform(const SkMatrix& matrix, SkPath* dst, + SkApplyPerspectiveClip pc = SkApplyPerspectiveClip::kYes) const; /** Transforms verb array, SkPoint array, and weight by matrix. transform may change verbs and increase their number. @@ -1486,11 +1383,33 @@ private: @param matrix SkMatrix to apply to SkPath @param pc whether to apply perspective clipping */ - SkPath& transform(const SkMatrix& matrix) { - this->transform(matrix, this); + SkPath& transform(const SkMatrix& matrix, + SkApplyPerspectiveClip pc = SkApplyPerspectiveClip::kYes) { + this->transform(matrix, this, pc); return *this; } + SkPath makeTransform(const SkMatrix& m, + SkApplyPerspectiveClip pc = SkApplyPerspectiveClip::kYes) const { + SkPath dst; + this->transform(m, &dst, pc); + return dst; + } + + SkPath makeScale(SkScalar sx, SkScalar sy) { + return this->makeTransform(SkMatrix::Scale(sx, sy), SkApplyPerspectiveClip::kNo); + } + + /** Returns last point on SkPath in lastPt. Returns false if SkPoint array is empty, + storing (0, 0) if lastPt is not nullptr. + + @param lastPt storage for final SkPoint in SkPoint array; may be nullptr + @return true if SkPoint array contains one or more SkPoint + + example: https://fiddle.skia.org/c/@Path_getLastPt + */ + bool getLastPt(SkPoint* lastPt) const; + /** Sets last point to (x, y). If SkPoint array is empty, append kMove_Verb to verb array and append (x, y) to SkPoint array. @@ -1510,50 +1429,39 @@ private: this->setLastPt(p.fX, p.fY); } -#ifdef SK_HIDE_PATH_EDIT_METHODS -public: -#endif -#ifdef SK_SUPPORT_UNSPANNED_APIS - static SkPath Make(const SkPoint points[], int pointCount, - const uint8_t verbs[], int verbCount, - const SkScalar conics[], int conicWeightCount, - SkPathFillType fillType, bool isVolatile = false) { - return Make({points, pointCount}, - {verbs, verbCount}, - {conics, conicWeightCount}, - fillType, isVolatile); - } - static SkPath Polygon(const SkPoint pts[], int count, bool isClosed, - SkPathFillType fillType = SkPathFillType::kWinding, - bool isVolatile = false) { - return Polygon({pts, count}, isClosed, fillType, isVolatile); - } - int getPoints(SkPoint points[], int max) const { - return (int)this->getPoints({points, max}); - } - int getVerbs(uint8_t verbs[], int max) const { - return (int)this->getVerbs({verbs, max}); - } - SkPath& addRoundRect(const SkRect& rect, const SkScalar radii[], - SkPathDirection dir = SkPathDirection::kCW) { - return this->addRoundRect(rect, {radii, radii ? 8 : 0}, dir); - } - SkPath& addPoly(const SkPoint pts[], int count, bool close) { - return this->addPoly({pts, count}, close); - } -#endif + /** \enum SkPath::SegmentMask + SegmentMask constants correspond to each drawing Verb type in SkPath; for + instance, if SkPath only contains lines, only the kLine_SegmentMask bit is set. + */ + enum SegmentMask { + kLine_SegmentMask = kLine_SkPathSegmentMask, + kQuad_SegmentMask = kQuad_SkPathSegmentMask, + kConic_SegmentMask = kConic_SkPathSegmentMask, + kCubic_SegmentMask = kCubic_SkPathSegmentMask, + }; - SkPathIter iter() const; + /** Returns a mask, where each set bit corresponds to a SegmentMask constant + if SkPath contains one or more verbs of that type. + Returns zero if SkPath contains no lines, or curves: quads, conics, or cubics. - struct IterRec { - SkPathVerb fVerb; - SkSpan<const SkPoint> fPoints; - float fConicWeight; + getSegmentMasks() returns a cached result; it is very fast. - float conicWeight() const { - SkASSERT(fVerb == SkPathVerb::kConic); - return fConicWeight; - } + @return SegmentMask bits or zero + */ + uint32_t getSegmentMasks() const; + + /** \enum SkPath::Verb + Verb instructs SkPath how to interpret one or more SkPoint and optional conic weight; + manage contour, and terminate SkPath. + */ + enum Verb { + kMove_Verb = static_cast<int>(SkPathVerb::kMove), + kLine_Verb = static_cast<int>(SkPathVerb::kLine), + kQuad_Verb = static_cast<int>(SkPathVerb::kQuad), + kConic_Verb = static_cast<int>(SkPathVerb::kConic), + kCubic_Verb = static_cast<int>(SkPathVerb::kCubic), + kClose_Verb = static_cast<int>(SkPathVerb::kClose), + kDone_Verb = kClose_Verb + 1 }; /** \class SkPath::Iter @@ -1609,8 +1517,6 @@ public: */ Verb next(SkPoint pts[4]); - std::optional<IterRec> next(); - /** Returns conic weight if next() returned kConic_Verb. If next() has not been called, or next() did not return kConic_Verb, @@ -1642,18 +1548,17 @@ public: bool isClosedContour() const; private: - const SkPoint* fPts; - const SkPathVerb* fVerbs; - const SkPathVerb* fVerbStop; - const SkScalar* fConicWeights; - SkPoint fMoveTo; - SkPoint fLastPt; - std::array<SkPoint, 4> fStorage; - bool fForceClose; - bool fNeedClose; - bool fCloseLine; - - SkPathVerb autoClose(SkPoint pts[2]); + const SkPoint* fPts; + const uint8_t* fVerbs; + const uint8_t* fVerbStop; + const SkScalar* fConicWeights; + SkPoint fMoveTo; + SkPoint fLastPt; + bool fForceClose; + bool fNeedClose; + bool fCloseLine; + + Verb autoClose(SkPoint pts[2]); }; private: @@ -1666,7 +1571,7 @@ private: class RangeIter { public: RangeIter() = default; - RangeIter(const SkPathVerb* verbs, const SkPoint* points, const SkScalar* weights) + RangeIter(const uint8_t* verbs, const SkPoint* points, const SkScalar* weights) : fVerb(verbs), fPoints(points), fWeights(weights) { SkDEBUGCODE(fInitialPoints = fPoints;) } @@ -1677,7 +1582,7 @@ private: return fVerb == that.fVerb; } RangeIter& operator++() { - auto verb = *fVerb++; + auto verb = static_cast<SkPathVerb>(*fVerb++); fPoints += pts_advance_after_verb(verb); if (verb == SkPathVerb::kConic) { ++fWeights; @@ -1690,7 +1595,7 @@ private: return copy; } SkPathVerb peekVerb() const { - return *fVerb; + return static_cast<SkPathVerb>(*fVerb); } std::tuple<SkPathVerb, const SkPoint*, const SkScalar*> operator*() const { SkPathVerb verb = this->peekVerb(); @@ -1724,7 +1629,7 @@ private: } SkUNREACHABLE; } - const SkPathVerb* fVerb = nullptr; + const uint8_t* fVerb = nullptr; const SkPoint* fPoints = nullptr; const SkScalar* fWeights = nullptr; SkDEBUGCODE(const SkPoint* fInitialPoints = nullptr;) @@ -1769,8 +1674,6 @@ public: */ Verb next(SkPoint[4]); - std::optional<IterRec> next(); - /** Returns next SkPath::Verb, but does not advance RawIter. @return next SkPath::Verb from verb array @@ -1857,8 +1760,8 @@ public: */ sk_sp<SkData> serialize() const; - /** Returns a SkPath from buffer of size length. If the buffer data is inconsistent, or the - length is too small, returns a nullopt. + /** Initializes SkPath from buffer of size length. Returns zero if the buffer is + data is inconsistent, or the length is too small. Reads SkPath::FillType, verb array, SkPoint array, conic weight, and additionally reads computed information like SkPath::Convexity and bounds. @@ -1866,21 +1769,15 @@ public: Used only in concert with writeToMemory(); the format used for SkPath in memory is not guaranteed. - @param buffer storage for SkPath - @param length buffer size in bytes; must be multiple of 4 - @param bytesRead if not null, the number of bytes read from buffer will be written here - @return the path read, or nullopt on failure + @param buffer storage for SkPath + @param length buffer size in bytes; must be multiple of 4 + @return number of bytes read, or zero on failure example: https://fiddle.skia.org/c/@Path_readFromMemory */ - static std::optional<SkPath> ReadFromMemory(const void* buffer, size_t length, - size_t* bytesRead = nullptr); - -#ifndef SK_HIDE_PATH_EDIT_METHODS size_t readFromMemory(const void* buffer, size_t length); -#endif - /** (See skbug.com/40032862) + /** (See Skia bug 1762.) Returns a non-zero, globally unique value. A different value is returned if verb array, SkPoint array, or conic weight changes. @@ -1906,11 +1803,13 @@ public: using sk_is_trivially_relocatable = std::true_type; private: - SkPath(sk_sp<SkPathRef>, SkPathFillType, bool isVolatile, SkPathConvexity); + SkPath(sk_sp<SkPathRef>, SkPathFillType, bool isVolatile, SkPathConvexity, + SkPathFirstDirection firstDirection); sk_sp<SkPathRef> fPathRef; int fLastMoveToIndex; mutable std::atomic<uint8_t> fConvexity; // SkPathConvexity + mutable std::atomic<uint8_t> fFirstDirection; // SkPathFirstDirection uint8_t fFillType : 2; uint8_t fIsVolatile : 1; @@ -1978,9 +1877,10 @@ private: // Bottlenecks for working with fConvexity and fFirstDirection. // Notice the setters are const... these are mutable atomic fields. - void setConvexity(SkPathConvexity) const; + void setConvexity(SkPathConvexity) const; - void addRaw(const SkPathRaw&); + void setFirstDirection(SkPathFirstDirection) const; + SkPathFirstDirection getFirstDirection() const; /** Returns the comvexity type, computing if needed. Never returns kUnknown. @return path's convexity type (convex or concave) @@ -2012,13 +1912,15 @@ private: // SkPathPriv::AnalyzeVerbs(). static SkPath MakeInternal(const SkPathVerbAnalysis& analsis, const SkPoint points[], - SkSpan<const SkPathVerb> verbs, + const uint8_t verbs[], + int verbCount, const SkScalar conics[], SkPathFillType fillType, bool isVolatile); - friend class SkAutoAddSimpleShape; + friend class SkAutoPathBoundsUpdate; friend class SkAutoDisableOvalCheck; + friend class SkAutoDisableDirectionCheck; friend class SkPathBuilder; friend class SkPathEdgeIter; friend class SkPathWriter; diff --git a/gfx/skia/skia/include/core/SkPathBuilder.h b/gfx/skia/skia/include/core/SkPathBuilder.h @@ -8,563 +8,95 @@ #ifndef SkPathBuilder_DEFINED #define SkPathBuilder_DEFINED -#include "include/core/SkMatrix.h" #include "include/core/SkPath.h" -#include "include/core/SkPathIter.h" #include "include/core/SkPathTypes.h" #include "include/core/SkPoint.h" #include "include/core/SkRect.h" #include "include/core/SkRefCnt.h" #include "include/core/SkScalar.h" -#include "include/core/SkSpan.h" #include "include/core/SkTypes.h" #include "include/private/SkPathRef.h" -#include "include/private/base/SkTArray.h" +#include "include/private/base/SkTo.h" -#include <cstdint> -#include <optional> -#include <tuple> +#include <initializer_list> class SkRRect; -struct SkPathRaw; class SK_API SkPathBuilder { public: - /** Constructs an empty SkPathBuilder. By default, SkPathBuilder has no verbs, no SkPoint, and - no weights. FillType is set to kWinding. - - @return empty SkPathBuilder - */ SkPathBuilder(); - - /** Constructs an empty SkPathBuilder with the given FillType. By default, SkPathBuilder has no - verbs, no SkPoint, and no weights. - - @param fillType SkPathFillType to set on the SkPathBuilder. - @return empty SkPathBuilder - */ - SkPathBuilder(SkPathFillType fillType); - - /** Constructs an SkPathBuilder that is a copy of an existing SkPath. - Copies the FillType and replays all of the verbs from the SkPath into the SkPathBuilder. - - @param path SkPath to copy - @return SkPathBuilder - */ - SkPathBuilder(const SkPath& path); - + SkPathBuilder(SkPathFillType); + SkPathBuilder(const SkPath&); SkPathBuilder(const SkPathBuilder&) = default; ~SkPathBuilder(); - /** Sets an SkPathBuilder to be a copy of an existing SkPath. - Copies the FillType and replays all of the verbs from the SkPath into the SkPathBuilder. - - @param path SkPath to copy - @return SkPathBuilder - */ SkPathBuilder& operator=(const SkPath&); - SkPathBuilder& operator=(const SkPathBuilder&) = default; - /** Returns SkPathFillType, the rule used to fill SkPath. - - @return current SkPathFillType setting - */ SkPathFillType fillType() const { return fFillType; } - - /** Returns minimum and maximum axes values of SkPoint array. - Returns (0, 0, 0, 0) if SkPathBuilder contains no points. Returned bounds width and height - may be larger or smaller than area affected when SkPath is drawn. - - SkRect returned includes all SkPoint added to SkPathBuilder, including SkPoint associated - with kMove_Verb that define empty contours. - - @return bounds of all SkPoint in SkPoint array - */ SkRect computeBounds() const; - /** Returns an SkPath representing the current state of the SkPathBuilder. The builder is - unchanged after returning the path. - - @param mx if present, applied to the points after they are copied into the resulting path. - @return SkPath representing the current state of the builder. - */ - SkPath snapshot(const SkMatrix* mx = nullptr) const; + SkPath snapshot() const; // the builder is unchanged after returning this path + SkPath detach(); // the builder is reset to empty after returning this path - /** Returns an SkPath representing the current state of the SkPathBuilder. The builder is - reset to empty after returning the path. - - @param mx if present, applied to the points after they are copied into the resulting path. - @return SkPath representing the current state of the builder. - */ - SkPath detach(const SkMatrix* mx = nullptr); - - /** Sets SkPathFillType, the rule used to fill SkPath. While there is no - check that ft is legal, values outside of SkPathFillType are not supported. - - @param ft SkPathFillType to be used by SKPaths generated from this builder. - @return reference to SkPathBuilder - */ SkPathBuilder& setFillType(SkPathFillType ft) { fFillType = ft; return *this; } - - /** Specifies whether SkPath is volatile; whether it will be altered or discarded - by the caller after it is drawn. SkPath by default have volatile set false, allowing - Skia to attach a cache of data which speeds repeated drawing. - - Mark temporary paths, discarded or modified after use, as volatile - to inform Skia that the path need not be cached. - - Mark animating SkPath volatile to improve performance. - Mark unchanging SkPath non-volatile to improve repeated rendering. - - raster surface SkPath draws are affected by volatile for some shadows. - GPU surface SkPath draws are affected by volatile for some shadows and concave geometries. - - @param isVolatile true if caller will alter SkPath after drawing - @return reference to SkPathBuilder - */ SkPathBuilder& setIsVolatile(bool isVolatile) { fIsVolatile = isVolatile; return *this; } - /** Sets SkPathBuilder to its initial state. - Removes verb array, SkPoint array, and weights, and sets FillType to kWinding. - Internal storage associated with SkPathBuilder is released. - - @return reference to SkPathBuilder - */ SkPathBuilder& reset(); - /** Specifies the beginning of contour. If the previous verb was a "move" verb, - * then this just replaces the point value of that move, otherwise it appends a new - * "move" verb to the builder using the point. - * - * Thus, each contour can only have 1 move verb in it (the last one specified). - */ - SkPathBuilder& moveTo(SkPoint point); - - SkPathBuilder& moveTo(SkScalar x, SkScalar y) { - return this->moveTo(SkPoint::Make(x, y)); - } - - /** Adds line from last point to SkPoint p. If SkPathBuilder is empty, or last SkPath::Verb is - kClose_Verb, last point is set to (0, 0) before adding line. + SkPathBuilder& moveTo(SkPoint pt); + SkPathBuilder& moveTo(SkScalar x, SkScalar y) { return this->moveTo(SkPoint::Make(x, y)); } - lineTo() first appends kMove_Verb to verb array and (0, 0) to SkPoint array, if needed. - lineTo() then appends kLine_Verb to verb array and SkPoint p to SkPoint array. - - @param p end SkPoint of added line - @return reference to SkPathBuilder - */ SkPathBuilder& lineTo(SkPoint pt); - - /** Adds line from last point to (x, y). If SkPathBuilder is empty, or last SkPath::Verb is - kClose_Verb, last point is set to (0, 0) before adding line. - - lineTo() appends kMove_Verb to verb array and (0, 0) to SkPoint array, if needed. - lineTo() then appends kLine_Verb to verb array and (x, y) to SkPoint array. - - @param x end of added line on x-axis - @param y end of added line on y-axis - @return reference to SkPathBuilder - */ SkPathBuilder& lineTo(SkScalar x, SkScalar y) { return this->lineTo(SkPoint::Make(x, y)); } - /** Adds quad from last point towards SkPoint p1, to SkPoint p2. - If SkPathBuilder is empty, or last SkPath::Verb is kClose_Verb, last point is set to (0, 0) - before adding quad. - - Appends kMove_Verb to verb array and (0, 0) to SkPoint array, if needed; - then appends kQuad_Verb to verb array; and SkPoint p1, p2 - to SkPoint array. - - @param p1 control SkPoint of added quad - @param p2 end SkPoint of added quad - @return reference to SkPathBuilder - */ SkPathBuilder& quadTo(SkPoint pt1, SkPoint pt2); - - /** Adds quad from last point towards (x1, y1), to (x2, y2). - If SkPath is empty, or last SkPath::Verb is kClose_Verb, last point is set to (0, 0) - before adding quad. - - Appends kMove_Verb to verb array and (0, 0) to SkPoint array, if needed; - then appends kQuad_Verb to verb array; and (x1, y1), (x2, y2) - to SkPoint array. - - @param x1 control SkPoint of quad on x-axis - @param y1 control SkPoint of quad on y-axis - @param x2 end SkPoint of quad on x-axis - @param y2 end SkPoint of quad on y-axis - @return reference to SkPath - - example: https://fiddle.skia.org/c/@Path_quadTo - */ SkPathBuilder& quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) { return this->quadTo(SkPoint::Make(x1, y1), SkPoint::Make(x2, y2)); } - - /** Adds quad from last point towards the first SkPoint in pts, to the second. - If SkPathBuilder is empty, or last SkPath::Verb is kClose_Verb, last point is set to (0, 0) - before adding quad. - - Appends kMove_Verb to verb array and (0, 0) to SkPoint array, if needed; - then appends kQuad_Verb to verb array; and the SkPoints to SkPoint array. - - @param pts control point and endpoint of added quad. - @return reference to SkPathBuilder - */ SkPathBuilder& quadTo(const SkPoint pts[2]) { return this->quadTo(pts[0], pts[1]); } - /** Adds conic from last point towards pt1, to pt2, weighted by w. - If SkPathBuilder is empty, or last SkPath::Verb is kClose_Verb, last point is set to (0, 0) - before adding conic. - - Appends kMove_Verb to verb array and (0, 0) to SkPoint array, if needed. - - If w is finite and not one, appends kConic_Verb to verb array; - and pt1, pt2 to SkPoint array; and w to conic weights. - - If w is one, appends kQuad_Verb to verb array, and - pt1, pt2 to SkPoint array. - - If w is not finite, appends kLine_Verb twice to verb array, and - pt1, pt2 to SkPoint array. - - @param pt1 control SkPoint of conic - @param pt2 end SkPoint of conic - @param w weight of added conic - @return reference to SkPathBuilder - */ SkPathBuilder& conicTo(SkPoint pt1, SkPoint pt2, SkScalar w); - - /** Adds conic from last point towards (x1, y1), to (x2, y2), weighted by w. - If SkPathBuilder is empty, or last SkPath::Verb is kClose_Verb, last point is set to (0, 0) - before adding conic. - - Appends kMove_Verb to verb array and (0, 0) to SkPoint array, if needed. - - If w is finite and not one, appends kConic_Verb to verb array; - and (x1, y1), (x2, y2) to SkPoint array; and w to conic weights. - - If w is one, appends kQuad_Verb to verb array, and - (x1, y1), (x2, y2) to SkPoint array. - - If w is not finite, appends kLine_Verb twice to verb array, and - (x1, y1), (x2, y2) to SkPoint array. - - @param x1 control SkPoint of conic on x-axis - @param y1 control SkPoint of conic on y-axis - @param x2 end SkPoint of conic on x-axis - @param y2 end SkPoint of conic on y-axis - @param w weight of added conic - @return reference to SkPathBuilder - */ SkPathBuilder& conicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar w) { return this->conicTo(SkPoint::Make(x1, y1), SkPoint::Make(x2, y2), w); } - - /** Adds conic from last point towards SkPoint p1, to SkPoint p2, weighted by w. - If SkPathBuilder is empty, or last SkPath::Verb is kClose_Verb, last point is set to (0, 0) - before adding conic. - - Appends kMove_Verb to verb array and (0, 0) to SkPoint array, if needed. - - If w is finite and not one, appends kConic_Verb to verb array; - and SkPoint p1, p2 to SkPoint array; and w to conic weights. - - If w is one, appends kQuad_Verb to verb array, and SkPoint p1, p2 - to SkPoint array. - - If w is not finite, appends kLine_Verb twice to verb array, and - SkPoint p1, p2 to SkPoint array. - - @param p1 control SkPoint of added conic - @param p2 end SkPoint of added conic - @param w weight of added conic - @return reference to SkPathBuilder - */ SkPathBuilder& conicTo(const SkPoint pts[2], SkScalar w) { return this->conicTo(pts[0], pts[1], w); } - /** Adds cubic from last point towards SkPoint p1, then towards SkPoint p2, ending at - SkPoint p3. If SkPathBuilder is empty, or last SkPath::Verb is kClose_Verb, last point is - set to (0, 0) before adding cubic. - - Appends kMove_Verb to verb array and (0, 0) to SkPoint array, if needed; - then appends kCubic_Verb to verb array; and SkPoint p1, p2, p3 - to SkPoint array. - - @param p1 first control SkPoint of cubic - @param p2 second control SkPoint of cubic - @param p3 end SkPoint of cubic - @return reference to SkPathBuilder - */ SkPathBuilder& cubicTo(SkPoint pt1, SkPoint pt2, SkPoint pt3); - - /** Adds cubic from last point towards (x1, y1), then towards (x2, y2), ending at - (x3, y3). If SkPathBuilder is empty, or last SkPath::Verb is kClose_Verb, last point is set - to (0, 0) before adding cubic. - - Appends kMove_Verb to verb array and (0, 0) to SkPoint array, if needed; - then appends kCubic_Verb to verb array; and (x1, y1), (x2, y2), (x3, y3) - to SkPoint array. - - @param x1 first control SkPoint of cubic on x-axis - @param y1 first control SkPoint of cubic on y-axis - @param x2 second control SkPoint of cubic on x-axis - @param y2 second control SkPoint of cubic on y-axis - @param x3 end SkPoint of cubic on x-axis - @param y3 end SkPoint of cubic on y-axis - @return reference to SkPathBuilder - */ SkPathBuilder& cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar x3, SkScalar y3) { return this->cubicTo(SkPoint::Make(x1, y1), SkPoint::Make(x2, y2), SkPoint::Make(x3, y3)); } - - /** Adds cubic from last point towards the first SkPoint, then towards the second, ending at - the third. If SkPathBuilder is empty, or last SkPath::Verb is kClose_Verb, last point is - set to (0, 0) before adding cubic. - - Appends kMove_Verb to verb array and (0, 0) to SkPoint array, if needed; - then appends kCubic_Verb to verb array; and SkPoint p1, p2, p3 - to SkPoint array. - - @param pts first and second control SkPoints of cubic, and end SkPoint. - @return reference to SkPathBuilder - */ SkPathBuilder& cubicTo(const SkPoint pts[3]) { return this->cubicTo(pts[0], pts[1], pts[2]); } - /** Appends kClose_Verb to SkPathBuilder. A closed contour connects the first and last SkPoint - with line, forming a continuous loop. Open and closed contour draw the same - with SkPaint::kFill_Style. With SkPaint::kStroke_Style, open contour draws - SkPaint::Cap at contour start and end; closed contour draws - SkPaint::Join at contour start and end. - - close() has no effect if SkPathBuilder is empty or last SkPath SkPath::Verb is kClose_Verb. - - @return reference to SkPathBuilder - */ SkPathBuilder& close(); - /** Append a series of lineTo(...) - - @param pts span of SkPoint - @return reference to SkPathBuilder. - */ - SkPathBuilder& polylineTo(SkSpan<const SkPoint> pts); + // Append a series of lineTo(...) + SkPathBuilder& polylineTo(const SkPoint pts[], int count); + SkPathBuilder& polylineTo(const std::initializer_list<SkPoint>& list) { + return this->polylineTo(list.begin(), SkToInt(list.size())); + } // Relative versions of segments, relative to the previous position. - /** Adds beginning of contour relative to last point. - If SkPathBuilder is empty, starts contour at (dx, dy). - Otherwise, start contour at last point offset by (dx, dy). - Function name stands for "relative move to". - - @param pt vector offset from last point to contour start - @return reference to SkPathBuilder - - example: https://fiddle.skia.org/c/@Path_rMoveTo - */ - SkPathBuilder& rMoveTo(SkPoint pt); - - /** Adds line from last point to vector given by pt. If SkPathBuilder is empty, or last - SkPath::Verb is kClose_Verb, last point is set to (0, 0) before adding line. - - Appends kMove_Verb to verb array and (0, 0) to SkPoint array, if needed; - then appends kLine_Verb to verb array and line end to SkPoint array. - Line end is last point plus vector given by pt. - Function name stands for "relative line to". - - @param pt vector offset from last point to line end - @return reference to SkPathBuilder - */ SkPathBuilder& rLineTo(SkPoint pt); - - /** Adds line from last point to vector (dx, dy). If SkPathBuilder is empty, or last - SkPath::Verb is kClose_Verb, last point is set to (0, 0) before adding line. - - Appends kMove_Verb to verb array and (0, 0) to SkPoint array, if needed; - then appends kLine_Verb to verb array and line end to SkPoint array. - Line end is last point plus vector (dx, dy). - Function name stands for "relative line to". - - @param dx offset from last point to line end on x-axis - @param dy offset from last point to line end on y-axis - @return reference to SkPathBuilder - */ SkPathBuilder& rLineTo(SkScalar x, SkScalar y) { return this->rLineTo({x, y}); } - - /** Adds quad from last point towards vector pt1, to vector pt2. - If SkPathBuilder is empty, or last SkPath::Verb - is kClose_Verb, last point is set to (0, 0) before adding quad. - - Appends kMove_Verb to verb array and (0, 0) to SkPoint array, - if needed; then appends kQuad_Verb to verb array; and appends quad - control and quad end to SkPoint array. - Quad control is last point plus vector pt1. - Quad end is last point plus vector pt2. - Function name stands for "relative quad to". - - @param pt1 offset vector from last point to quad control - @param pt2 offset vector from last point to quad end - @return reference to SkPathBuilder - */ SkPathBuilder& rQuadTo(SkPoint pt1, SkPoint pt2); - - /** Adds quad from last point towards vector (dx1, dy1), to vector (dx2, dy2). - If SkPathBuilder is empty, or last SkPath::Verb - is kClose_Verb, last point is set to (0, 0) before adding quad. - - Appends kMove_Verb to verb array and (0, 0) to SkPoint array, - if needed; then appends kQuad_Verb to verb array; and appends quad - control and quad end to SkPoint array. - Quad control is last point plus vector (dx1, dy1). - Quad end is last point plus vector (dx2, dy2). - Function name stands for "relative quad to". - - @param dx1 offset from last point to quad control on x-axis - @param dy1 offset from last point to quad control on y-axis - @param dx2 offset from last point to quad end on x-axis - @param dy2 offset from last point to quad end on y-axis - @return reference to SkPathBuilder - */ SkPathBuilder& rQuadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) { return this->rQuadTo({x1, y1}, {x2, y2}); } - - /** Adds conic from last point towards vector p1, to vector p2, - weighted by w. If SkPathBuilder is empty, or last SkPath::Verb - is kClose_Verb, last point is set to (0, 0) before adding conic. - - Appends kMove_Verb to verb array and (0, 0) to SkPoint array, - if needed. - - If w is finite and not one, next appends kConic_Verb to verb array, - and w is recorded as conic weight; otherwise, if w is one, appends - kQuad_Verb to verb array; or if w is not finite, appends kLine_Verb - twice to verb array. - - In all cases appends SkPoint control and end to SkPoint array. - control is last point plus vector p1. - end is last point plus vector p2. - - Function name stands for "relative conic to". - - @param p1 offset vector from last point to conic control - @param p2 offset vector from last point to conic end - @param w weight of added conic - @return reference to SkPathBuilder - */ SkPathBuilder& rConicTo(SkPoint p1, SkPoint p2, SkScalar w); - - /** Adds conic from last point towards vector (dx1, dy1), to vector (dx2, dy2), - weighted by w. If SkPathBuilder is empty, or last SkPath::Verb - is kClose_Verb, last point is set to (0, 0) before adding conic. - - Appends kMove_Verb to verb array and (0, 0) to SkPoint array, - if needed. - - If w is finite and not one, next appends kConic_Verb to verb array, - and w is recorded as conic weight; otherwise, if w is one, appends - kQuad_Verb to verb array; or if w is not finite, appends kLine_Verb - twice to verb array. - - In all cases appends SkPoint control and end to SkPoint array. - control is last point plus vector (dx1, dy1). - end is last point plus vector (dx2, dy2). - - Function name stands for "relative conic to". - - @param dx1 offset from last point to conic control on x-axis - @param dy1 offset from last point to conic control on y-axis - @param dx2 offset from last point to conic end on x-axis - @param dy2 offset from last point to conic end on y-axis - @param w weight of added conic - @return reference to SkPathBuilder - */ SkPathBuilder& rConicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar w) { return this->rConicTo({x1, y1}, {x2, y2}, w); } - - /** Adds cubic from last point towards vector pt1, then towards - vector pt2, to vector pt3. - If SkPathBuilder is empty, or last SkPath::Verb - is kClose_Verb, last point is set to (0, 0) before adding cubic. - - Appends kMove_Verb to verb array and (0, 0) to SkPoint array, - if needed; then appends kCubic_Verb to verb array; and appends cubic - control and cubic end to SkPoint array. - Cubic control is last point plus vector (dx1, dy1). - Cubic end is last point plus vector (dx2, dy2). - Function name stands for "relative cubic to". - - @param pt1 offset vector from last point to first cubic control - @param pt2 offset vector from last point to second cubic control - @param pt3 offset vector from last point to cubic end - @return reference to SkPathBuilder - */ SkPathBuilder& rCubicTo(SkPoint pt1, SkPoint pt2, SkPoint pt3); - - /** Adds cubic from last point towards vector (dx1, dy1), then towards - vector (dx2, dy2), to vector (dx3, dy3). - If SkPathBuilder is empty, or last SkPath::Verb - is kClose_Verb, last point is set to (0, 0) before adding cubic. - - Appends kMove_Verb to verb array and (0, 0) to SkPoint array, - if needed; then appends kCubic_Verb to verb array; and appends cubic - control and cubic end to SkPoint array. - Cubic control is last point plus vector (dx1, dy1). - Cubic end is last point plus vector (dx2, dy2). - Function name stands for "relative cubic to". - - @param dx1 offset from last point to first cubic control on x-axis - @param dy1 offset from last point to first cubic control on y-axis - @param dx2 offset from last point to second cubic control on x-axis - @param dy2 offset from last point to second cubic control on y-axis - @param dx3 offset from last point to cubic end on x-axis - @param dy3 offset from last point to cubic end on y-axis - @return reference to SkPathBuilder - */ SkPathBuilder& rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar x3, SkScalar y3) { return this->rCubicTo({x1, y1}, {x2, y2}, {x3, y3}); } - enum ArcSize { - kSmall_ArcSize, //!< smaller of arc pair - kLarge_ArcSize, //!< larger of arc pair - }; - - /** Appends arc to SkPathBuilder, relative to last SkPath SkPoint. Arc is implemented by one or - more conic, weighted to describe part of oval with radii (rx, ry) rotated by - xAxisRotate degrees. Arc curves from last SkPathBuilder SkPoint to relative end SkPoint: - (dx, dy), choosing one of four possible routes: clockwise or - counterclockwise, and smaller or larger. If SkPathBuilder is empty, the start arc SkPoint - is (0, 0). - - Arc sweep is always less than 360 degrees. arcTo() appends line to end SkPoint - if either radii are zero, or if last SkPath SkPoint equals end SkPoint. - arcTo() scales radii (rx, ry) to fit last SkPath SkPoint and end SkPoint if both are - greater than zero but too small to describe an arc. - - arcTo() appends up to four conic curves. - arcTo() implements the functionality of svg arc, although SVG "sweep-flag" value is - opposite the integer value of sweep; SVG "sweep-flag" uses 1 for clockwise, while - kCW_Direction cast to int is zero. - - @param rx radius before x-axis rotation - @param ry radius before x-axis rotation - @param xAxisRotate x-axis rotation in degrees; positive values are clockwise - @param largeArc chooses smaller or larger arc - @param sweep chooses clockwise or counterclockwise arc - @param dx x-axis offset end of arc from last SkPath SkPoint - @param dy y-axis offset end of arc from last SkPath SkPoint - @return reference to SkPath - */ - SkPathBuilder& rArcTo(SkScalar rx, SkScalar ry, SkScalar xAxisRotate, ArcSize largeArc, - SkPathDirection sweep, SkScalar dx, SkScalar dy); - // Arcs /** Appends arc to the builder. Arc added is part of ellipse @@ -606,6 +138,11 @@ public: */ SkPathBuilder& arcTo(SkPoint p1, SkPoint p2, SkScalar radius); + enum ArcSize { + kSmall_ArcSize, //!< smaller of arc pair + kLarge_ArcSize, //!< larger of arc pair + }; + /** Appends arc to SkPath. Arc is implemented by one or more conic weighted to describe part of oval with radii (r.fX, r.fY) rotated by xAxisRotate degrees. Arc curves from last SkPath SkPoint to (xy.fX, xy.fY), choosing one of four possible routes: @@ -648,319 +185,76 @@ public: */ SkPathBuilder& addArc(const SkRect& oval, SkScalar startAngleDeg, SkScalar sweepAngleDeg); - SkPathBuilder& addLine(SkPoint a, SkPoint b) { - return this->moveTo(a).lineTo(b); - } - - /** Adds a new contour to the SkPathBuilder, defined by the rect, and wound in the - specified direction. The verbs added to the path will be: - - kMove, kLine, kLine, kLine, kClose - - start specifies which corner to begin the contour: - 0: upper-left corner - 1: upper-right corner - 2: lower-right corner - 3: lower-left corner - - This start point also acts as the implied beginning of the subsequent, - contour, if it does not have an explicit moveTo(). e.g. + // Add a new contour - path.addRect(...) - // if we don't say moveTo() here, we will use the rect's start point - path.lineTo(...) - - @param rect SkRect to add as a closed contour - @param dir SkPath::Direction to orient the new contour - @param start initial corner of SkRect to add - @return reference to SkPathBuilder - */ SkPathBuilder& addRect(const SkRect&, SkPathDirection, unsigned startIndex); + SkPathBuilder& addOval(const SkRect&, SkPathDirection, unsigned startIndex); + SkPathBuilder& addRRect(const SkRRect&, SkPathDirection, unsigned startIndex); - /** Adds a new contour to the SkPathBuilder, defined by the rect, and wound in the - specified direction. The verbs added to the path will be: - - kMove, kLine, kLine, kLine, kClose - - The contour starts at the upper-left corner of the rect, which also acts as the implied - beginning of the subsequent contour, if it does not have an explicit moveTo(). e.g. - - path.addRect(...) - // if we don't say moveTo() here, we will use the rect's upper-left corner - path.lineTo(...) - - @param rect SkRect to add as a closed contour - @param dir SkPath::Direction to orient the new contour - @return reference to SkPathBuilder - */ - SkPathBuilder& addRect(const SkRect& rect, SkPathDirection dir = SkPathDirection::kDefault) { + SkPathBuilder& addRect(const SkRect& rect, SkPathDirection dir = SkPathDirection::kCW) { return this->addRect(rect, dir, 0); } - - /** Adds oval to SkPathBuilder, appending kMove_Verb, four kConic_Verb, and kClose_Verb. - Oval is upright ellipse bounded by SkRect oval with radii equal to half oval width - and half oval height. Oval begins at (oval.fRight, oval.centerY()) and continues - clockwise if dir is kCW_Direction, counterclockwise if dir is kCCW_Direction. - - @param oval bounds of ellipse added - @param dir SkPath::Direction to wind ellipse - @return reference to SkPathBuilder - */ - SkPathBuilder& addOval(const SkRect&, SkPathDirection, unsigned startIndex); - - /** Appends SkRRect to SkPathBuilder, creating a new closed contour. If dir is kCW_Direction, - SkRRect winds clockwise. If dir is kCCW_Direction, SkRRect winds counterclockwise. - - After appending, SkPathBuilder may be empty, or may contain: SkRect, oval, or SkRRect. - - @param rrect SkRRect to add - @param dir SkPath::Direction to wind SkRRect - @param start index of initial point of SkRRect - @return reference to SkPathBuilder - */ - SkPathBuilder& addRRect(const SkRRect& rrect, SkPathDirection, unsigned start); - - /** Appends SkRRect to SkPathBuilder, creating a new closed contour. If dir is kCW_Direction, - SkRRect starts at top-left of the lower-left corner and winds clockwise. If dir is - kCCW_Direction, SkRRect starts at the bottom-left of the upper-left corner and winds - counterclockwise. - - After appending, SkPathBuilder may be empty, or may contain: SkRect, oval, or SkRRect. - - @param rrect SkRRect to add - @param dir SkPath::Direction to wind SkRRect - @return reference to SkPathBuilder - */ - SkPathBuilder& addRRect(const SkRRect& rrect, SkPathDirection dir = SkPathDirection::kDefault) { + SkPathBuilder& addOval(const SkRect& rect, SkPathDirection dir = SkPathDirection::kCW) { + // legacy start index: 1 + return this->addOval(rect, dir, 1); + } + SkPathBuilder& addRRect(const SkRRect& rrect, SkPathDirection dir = SkPathDirection::kCW) { // legacy start indices: 6 (CW) and 7 (CCW) return this->addRRect(rrect, dir, dir == SkPathDirection::kCW ? 6 : 7); } - /** Adds oval to SkPathBuilder, appending kMove_Verb, four kConic_Verb, and kClose_Verb. - Oval is upright ellipse bounded by SkRect oval with radii equal to half oval width - and half oval height. Oval begins at start and continues - clockwise if dir is kCW_Direction, counterclockwise if dir is kCCW_Direction. - - @param oval bounds of ellipse added - @param dir SkPath::Direction to wind ellipse - @return reference to SkPath + SkPathBuilder& addCircle(SkScalar center_x, SkScalar center_y, SkScalar radius, + SkPathDirection dir = SkPathDirection::kCW); - example: https://fiddle.skia.org/c/@Path_addOval_2 - */ - SkPathBuilder& addOval(const SkRect& oval, SkPathDirection dir = SkPathDirection::kDefault) { - // legacy start index: 1 - return this->addOval(oval, dir, 1); + SkPathBuilder& addPolygon(const SkPoint pts[], int count, bool isClosed); + SkPathBuilder& addPolygon(const std::initializer_list<SkPoint>& list, bool isClosed) { + return this->addPolygon(list.begin(), SkToInt(list.size()), isClosed); } - /** Adds circle centered at (x, y) of size radius to SkPathBuilder, appending kMove_Verb, - four kConic_Verb, and kClose_Verb. Circle begins at: (x + radius, y), continuing - clockwise if dir is kCW_Direction, and counterclockwise if dir is kCCW_Direction. - - Has no effect if radius is zero or negative. - - @param x center of circle - @param y center of circle - @param radius distance from center to edge - @param dir SkPath::Direction to wind circle - @return reference to SkPathBuilder - */ - SkPathBuilder& addCircle(SkScalar x, SkScalar y, SkScalar radius, - SkPathDirection dir = SkPathDirection::kDefault); - - /** Adds contour created from line array, adding (pts.size() - 1) line segments. - Contour added starts at pts[0], then adds a line for every additional SkPoint - in pts array. If close is true, appends kClose_Verb to SkPath, connecting - pts[count - 1] and pts[0]. - - @param pts array of line sharing end and start SkPoint - @param close true to add line connecting contour end and start - @return reference to SkPath - */ - SkPathBuilder& addPolygon(SkSpan<const SkPoint> pts, bool close); - - /** Appends src to SkPathBuilder, offset by (dx, dy). - - If mode is kAppend_AddPathMode, src verb array, SkPoint array, and conic weights are - added unaltered. If mode is kExtend_AddPathMode, add line before appending - verbs, SkPoint, and conic weights. - - @param src SkPath verbs, SkPoint, and conic weights to add - @param dx offset added to src SkPoint array x-axis coordinates - @param dy offset added to src SkPoint array y-axis coordinates - @param mode kAppend_AddPathMode or kExtend_AddPathMode - @return reference to SkPathBuilder - */ - SkPathBuilder& addPath(const SkPath& src, SkScalar dx, SkScalar dy, - SkPath::AddPathMode mode = SkPath::kAppend_AddPathMode); - - /** Appends src to SkPathBuilder. - - If mode is kAppend_AddPathMode, src verb array, SkPoint array, and conic weights are - added unaltered. If mode is kExtend_AddPathMode, add line before appending - verbs, SkPoint, and conic weights. - - @param src SkPath verbs, SkPoint, and conic weights to add - @param mode kAppend_AddPathMode or kExtend_AddPathMode - @return reference to SkPathBuilder - */ - SkPathBuilder& addPath(const SkPath& src, - SkPath::AddPathMode mode = SkPath::kAppend_AddPathMode) { - SkMatrix m; - m.reset(); - return this->addPath(src, m, mode); - } - - /** Appends src to SkPathBuilder, transformed by matrix. Transformed curves may have different - verbs, SkPoint, and conic weights. - - If mode is kAppend_AddPathMode, src verb array, SkPoint array, and conic weights are - added unaltered. If mode is kExtend_AddPathMode, add line before appending - verbs, SkPoint, and conic weights. - - @param src SkPath verbs, SkPoint, and conic weights to add - @param matrix transform applied to src - @param mode kAppend_AddPathMode or kExtend_AddPathMode - @return reference to SkPathBuilder - */ - SkPathBuilder& addPath(const SkPath& src, const SkMatrix& matrix, - SkPath::AddPathMode mode = SkPath::AddPathMode::kAppend_AddPathMode); + SkPathBuilder& addPath(const SkPath&); // Performance hint, to reserve extra storage for subsequent calls to lineTo, quadTo, etc. - /** Grows SkPathBuilder verb array and SkPoint array to contain additional space. - May improve performance and use less memory by - reducing the number and size of allocations when creating SkPathBuilder. - - @param extraPtCount number of additional SkPoint to allocate - @param extraVerbCount number of additional verbs - */ void incReserve(int extraPtCount, int extraVerbCount); - - /** Grows SkPathBuilder verb array and SkPoint array to contain additional space. - May improve performance and use less memory by - reducing the number and size of allocations when creating SkPathBuilder. - - @param extraPtCount number of additional SkPoints and verbs to allocate - */ void incReserve(int extraPtCount) { this->incReserve(extraPtCount, extraPtCount); } - /** Offsets SkPoint array by (dx, dy). - - @param dx offset added to SkPoint array x-axis coordinates - @param dy offset added to SkPoint array y-axis coordinates - */ SkPathBuilder& offset(SkScalar dx, SkScalar dy); - /** Transforms verb array, SkPoint array, and weight by matrix. - transform may change verbs and increase their number. - - @param matrix SkMatrix to apply to SkPath - @param pc whether to apply perspective clipping - */ - SkPathBuilder& transform(const SkMatrix& matrix); - -#ifdef SK_SUPPORT_LEGACY_APPLYPERSPECTIVECLIP - SkPathBuilder& transform(const SkMatrix& matrix, SkApplyPerspectiveClip) { - return this->transform(matrix); - } -#endif - - /* - * Returns true if the builder is empty, or all of its points are finite. - */ - bool isFinite() const; - - /** Replaces SkPathFillType with its inverse. The inverse of SkPathFillType describes the area - unmodified by the original SkPathFillType. - */ SkPathBuilder& toggleInverseFillType() { fFillType = (SkPathFillType)((unsigned)fFillType ^ 2); return *this; } - /** Returns if SkPath is empty. - Empty SkPathBuilder may have FillType but has no SkPoint, SkPath::Verb, or conic weight. - SkPathBuilder() constructs empty SkPathBuilder; reset() and rewind() make SkPath empty. - - @return true if the path contains no SkPath::Verb array - */ - bool isEmpty() const { return fVerbs.empty(); } - - /** Returns last point on SkPathBuilder. Returns nullopt if SkPoint array is empty. - - @return last SkPoint if SkPoint array contains one or more SkPoint, otherwise nullopt - - example: https://fiddle.skia.org/c/@Path_getLastPt - */ - std::optional<SkPoint> getLastPt() const; - - /** Sets the last point on the path. If SkPoint array is empty, append kMove_Verb to - verb array and append p to SkPoint array. - - @param x x-value of last point - @param y y-value of last point - */ - void setLastPt(SkScalar x, SkScalar y); - - /** Returns the number of points in SkPathBuilder. - SkPoint count is initially zero. - - @return SkPathBuilder SkPoint array length - */ - int countPoints() const { return fPts.size(); } - - /** Returns if SkPathFillType describes area outside SkPath geometry. The inverse fill area - extends indefinitely. - - @return true if FillType is kInverseWinding or kInverseEvenOdd - */ - bool isInverseFillType() const { return SkPathFillType_IsInverse(fFillType); } - -#ifdef SK_SUPPORT_UNSPANNED_APIS - SkPathBuilder& addPolygon(const SkPoint pts[], int count, bool close) { - return this->addPolygon({pts, count}, close); - } - SkPathBuilder& polylineTo(const SkPoint pts[], int count) { - return this->polylineTo({pts, count}); - } -#endif - - SkSpan<const SkPoint> points() const { - return fPts; - } - SkSpan<const SkPathVerb> verbs() const { - return fVerbs; - } - SkSpan<const float> conicWeights() const { - return fConicWeights; - } - - SkPathBuilder& addRaw(const SkPathRaw&); - - SkPathIter iter() const; - private: SkPathRef::PointsArray fPts; SkPathRef::VerbsArray fVerbs; SkPathRef::ConicWeightsArray fConicWeights; - SkPathFillType fFillType; - bool fIsVolatile; - SkPathConvexity fConvexity; + SkPathFillType fFillType; + bool fIsVolatile; unsigned fSegmentMask; SkPoint fLastMovePoint; int fLastMoveIndex; // only needed until SkPath is immutable bool fNeedsMoveVerb; - SkPathIsAType fType = SkPathIsAType::kGeneral; - SkPathIsAData fIsA {}; + enum IsA { + kIsA_JustMoves, // we only have 0 or more moves + kIsA_MoreThanMoves, // we have verbs other than just move + kIsA_Oval, // we are 0 or more moves followed by an oval + kIsA_RRect, // we are 0 or more moves followed by a rrect + }; + IsA fIsA = kIsA_JustMoves; + int fIsAStart = -1; // tracks direction iff fIsA is not unknown + bool fIsACCW = false; // tracks direction iff fIsA is not unknown + + int countVerbs() const { return fVerbs.size(); } // called right before we add a (non-move) verb void ensureMove() { - fType = SkPathIsAType::kGeneral; + fIsA = kIsA_MoreThanMoves; if (fNeedsMoveVerb) { this->moveTo(fLastMovePoint); } @@ -968,16 +262,9 @@ private: SkPath make(sk_sp<SkPathRef>) const; - bool isZeroLengthSincePoint(int startPtIndex) const; - SkPathBuilder& privateReverseAddPath(const SkPath&); - SkPathBuilder& privateReversePathTo(const SkPath&); - - std::tuple<SkPoint*, SkScalar*> growForVerbsInPath(const SkPathRef& path); friend class SkPathPriv; - friend class SkStroke; - friend class SkPathStroker; }; #endif diff --git a/gfx/skia/skia/include/core/SkPathEffect.h b/gfx/skia/skia/include/core/SkPathEffect.h @@ -10,7 +10,7 @@ #include "include/core/SkFlattenable.h" #include "include/core/SkRefCnt.h" -#include "include/core/SkTypes.h" +#include "include/private/base/SkAPI.h" // TODO(kjlubick) update clients and remove this unnecessary #include #include "include/core/SkPath.h" // IWYU pragma: keep @@ -18,7 +18,6 @@ #include <cstddef> class SkMatrix; -class SkPathBuilder; class SkStrokeRec; struct SkDeserialProcs; struct SkRect; @@ -69,9 +68,11 @@ public: * If this method returns true, the caller will apply (as needed) the * resulting stroke-rec to dst and then draw. */ - bool filterPath(SkPathBuilder* dst, const SkPath& src, SkStrokeRec*, const SkRect* cullR, + bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*, const SkRect* cullR) const; + + /** Version of filterPath that can be called when the CTM is known. */ + bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*, const SkRect* cullR, const SkMatrix& ctm) const; - bool filterPath(SkPathBuilder* dst, const SkPath& src, SkStrokeRec*) const; /** True if this path effect requires a valid CTM */ bool needsCTM() const; @@ -79,14 +80,6 @@ public: static sk_sp<SkPathEffect> Deserialize(const void* data, size_t size, const SkDeserialProcs* procs = nullptr); -#ifdef SK_SUPPORT_MUTABLE_PATHEFFECT - bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*, const SkRect* cullR) const; - - /** Version of filterPath that can be called when the CTM is known. */ - bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*, const SkRect* cullR, - const SkMatrix& ctm) const; -#endif - private: SkPathEffect() = default; friend class SkPathEffectBase; diff --git a/gfx/skia/skia/include/core/SkPathIter.h b/gfx/skia/skia/include/core/SkPathIter.h @@ -1,90 +0,0 @@ -/* - * 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 SkPathIter_DEFINED -#define SkPathIter_DEFINED - -#include "include/core/SkPathTypes.h" -#include "include/core/SkPoint.h" -#include "include/core/SkSpan.h" - -#include <array> -#include <cstddef> -#include <optional> - -class SK_API SkPathIter { -public: - struct Rec { - SkSpan<const SkPoint> fPoints; - float fConicWeight; - SkPathVerb fVerb; - - float conicWeight() const { - SkASSERT(fVerb == SkPathVerb::kConic); - return fConicWeight; - } - }; - - SkPathIter(SkSpan<const SkPoint> pts, SkSpan<const SkPathVerb> vbs, SkSpan<const float> cns) - : pIndex(0), vIndex(0), cIndex(0) - , fPoints(pts), fVerbs(vbs), fConics(cns) - { - // For compat older iterators, we trim off a trailing Move. - // SkPathData is defined to never create this pattern, so perhaps in the future - // this check can be removed (or replaced by an assert) - if (!vbs.empty() && vbs.back() == SkPathVerb::kMove) { - fVerbs = vbs.first(vbs.size() - 1); - } - } - - /* Holds the current verb, and its associated points - * move: pts[0] - * line: pts[0..1] - * quad: pts[0..2] - * conic: pts[0..2] fConicWeight - * cubic: pts[0..3] - * close: pts[0..1] ... as if close were a line from pts[0] to pts[1] - */ - std::optional<Rec> next(); - - std::optional<SkPathVerb> peekNextVerb() const { - if (vIndex < fVerbs.size()) { - return fVerbs[vIndex]; - } - return {}; - } - -private: - size_t pIndex, vIndex, cIndex; - SkSpan<const SkPoint> fPoints; - SkSpan<const SkPathVerb> fVerbs; - SkSpan<const float> fConics; - std::array<SkPoint, 2> fClosePointStorage; -}; - -class SkPathContourIter { -public: - struct Rec { - SkSpan<const SkPoint> fPoints; - SkSpan<const SkPathVerb> fVerbs; - SkSpan<const float> fConics; - }; - - SkPathContourIter(SkSpan<const SkPoint> pts, SkSpan<const SkPathVerb> vbs, - SkSpan<const float> cns) - : fPoints(pts), fVerbs(vbs), fConics(cns) - {} - - std::optional<Rec> next(); - -private: - SkSpan<const SkPoint> fPoints; - SkSpan<const SkPathVerb> fVerbs; - SkSpan<const float> fConics; -}; - -#endif diff --git a/gfx/skia/skia/include/core/SkPathMeasure.h b/gfx/skia/skia/include/core/SkPathMeasure.h @@ -12,12 +12,11 @@ #include "include/core/SkPoint.h" #include "include/core/SkRefCnt.h" #include "include/core/SkScalar.h" -#include "include/core/SkTypes.h" +#include "include/private/base/SkAPI.h" #include "include/private/base/SkDebug.h" class SkMatrix; class SkPath; -class SkPathBuilder; class SK_API SkPathMeasure { public: @@ -71,10 +70,7 @@ public: then return false (and leave dst untouched). Begin the segment with a moveTo if startWithMoveTo is true */ - bool getSegment(SkScalar startD, SkScalar stopD, SkPathBuilder* dst, bool startWithMoveTo); -#ifdef SK_SUPPORT_MUTABLE_PATHEFFECT bool getSegment(SkScalar startD, SkScalar stopD, SkPath* dst, bool startWithMoveTo); -#endif /** Return true if the current contour is closed() */ diff --git a/gfx/skia/skia/include/core/SkPathTypes.h b/gfx/skia/skia/include/core/SkPathTypes.h @@ -8,11 +8,7 @@ #ifndef SkPathTypes_DEFINED #define SkPathTypes_DEFINED -#include "include/core/SkTypes.h" - -#include <cstdint> - -enum class SkPathFillType : uint8_t { +enum class SkPathFillType { /** Specifies that "inside" is computed by a non-zero sum of signed edge crossings */ kWinding, /** Specifies that "inside" is computed by an odd number of edge crossings */ @@ -20,9 +16,7 @@ enum class SkPathFillType : uint8_t { /** Same as Winding, but draws outside of the path, rather than inside */ kInverseWinding, /** Same as EvenOdd, but draws outside of the path, rather than inside */ - kInverseEvenOdd, - - kDefault = kWinding, + kInverseEvenOdd }; static inline bool SkPathFillType_IsEvenOdd(SkPathFillType ft) { @@ -37,13 +31,11 @@ static inline SkPathFillType SkPathFillType_ConvertToNonInverse(SkPathFillType f return static_cast<SkPathFillType>(static_cast<int>(ft) & 1); } -enum class SkPathDirection : uint8_t { +enum class SkPathDirection { /** clockwise direction for adding closed contours */ kCW, /** counter-clockwise direction for adding closed contours */ kCCW, - - kDefault = kCW, }; enum SkPathSegmentMask { @@ -53,7 +45,7 @@ enum SkPathSegmentMask { kCubic_SkPathSegmentMask = 1 << 3, }; -enum class SkPathVerb : uint8_t { +enum class SkPathVerb { kMove, //!< SkPath::RawIter returns 1 point kLine, //!< SkPath::RawIter returns 2 points kQuad, //!< SkPath::RawIter returns 3 points diff --git a/gfx/skia/skia/include/core/SkPathUtils.h b/gfx/skia/skia/include/core/SkPathUtils.h @@ -7,34 +7,28 @@ #ifndef SkPathUtils_DEFINED #define SkPathUtils_DEFINED -#include "include/core/SkScalar.h" // IWYU pragma: keep +#include "include/core/SkScalar.h" #include "include/core/SkTypes.h" class SkMatrix; class SkPaint; class SkPath; -class SkPathBuilder; struct SkRect; namespace skpathutils { -/* Returns the filled equivalent of the stroked path. - * - * @param src SkPath read to create a filled version - * @param paint uses settings for stroke cap, width, miter, join, and patheffect. - * @param dst results are written to this builder. - * @param cullRect optional limit passed to SkPathEffect - * @param ctm matrix to take into acount for increased precision (if it scales up). - * @return true if the result can be filled, or false if it is a hairline (to be stroked). - */ -SK_API bool FillPathWithPaint(const SkPath& src, const SkPaint& paint, SkPathBuilder* dst, - const SkRect* cullRect, const SkMatrix& ctm); - -SK_API bool FillPathWithPaint(const SkPath& src, const SkPaint& paint, SkPathBuilder* dst); - -SK_API SkPath FillPathWithPaint(const SkPath& src, const SkPaint& paint, bool* isFill = nullptr); - -#ifdef SK_SUPPORT_MUTABLE_PATHEFFECT +/** Returns the filled equivalent of the stroked path. + + @param src SkPath read to create a filled version + @param paint SkPaint, from which attributes such as stroke cap, width, miter, and join, + as well as pathEffect will be used. + @param dst resulting SkPath; may be the same as src, but may not be nullptr + @param cullRect optional limit passed to SkPathEffect + @param resScale if > 1, increase precision, else if (0 < resScale < 1) reduce precision + to favor speed and size + @return true if the dst path was updated, false if it was not (e.g. if the path + represents hairline and cannot be filled). +*/ SK_API bool FillPathWithPaint(const SkPath &src, const SkPaint &paint, SkPath *dst, const SkRect *cullRect, SkScalar resScale = 1); @@ -42,7 +36,6 @@ SK_API bool FillPathWithPaint(const SkPath &src, const SkPaint &paint, SkPath *d const SkRect *cullRect, const SkMatrix &ctm); SK_API bool FillPathWithPaint(const SkPath &src, const SkPaint &paint, SkPath *dst); -#endif } diff --git a/gfx/skia/skia/include/core/SkPictureRecorder.h b/gfx/skia/skia/include/core/SkPictureRecorder.h @@ -27,7 +27,7 @@ class SkCanvas; class SkDrawable; class SkPicture; class SkRecord; -class SkRecordCanvas; +class SkRecorder; class SK_API SkPictureRecorder { public: @@ -102,11 +102,11 @@ private: friend class SkPictureRecorderReplayTester; // for unit testing void partialReplay(SkCanvas* canvas) const; - sk_sp<SkBBoxHierarchy> fBBH; - std::unique_ptr<SkRecordCanvas> fRecorder; - sk_sp<SkRecord> fRecord; - SkRect fCullRect; - bool fActivelyRecording; + bool fActivelyRecording; + SkRect fCullRect; + sk_sp<SkBBoxHierarchy> fBBH; + std::unique_ptr<SkRecorder> fRecorder; + sk_sp<SkRecord> fRecord; SkPictureRecorder(SkPictureRecorder&&) = delete; SkPictureRecorder& operator=(SkPictureRecorder&&) = delete; diff --git a/gfx/skia/skia/include/core/SkRRect.h b/gfx/skia/skia/include/core/SkRRect.h @@ -17,7 +17,6 @@ #include <cstdint> #include <cstring> -#include <optional> class SkMatrix; class SkString; @@ -451,13 +450,18 @@ public: */ size_t readFromMemory(const void* buffer, size_t length); - /** Transforms by SkRRect by matrix and return it if possible. - * If the matrix does not preserve axis-alignment (e.g. rotates, skews, etc.) - * then this returns {}. - */ - std::optional<SkRRect> transform(const SkMatrix&) const; + /** Transforms by SkRRect by matrix, storing result in dst. + Returns true if SkRRect transformed can be represented by another SkRRect. + Returns false if matrix contains transformations that are not axis aligned. + + Asserts in debug builds if SkRRect equals dst. - // Deprecated: use optional form + @param matrix SkMatrix specifying the transform + @param dst SkRRect to store the result + @return true if transformation succeeded. + + example: https://fiddle.skia.org/c/@RRect_transform + */ bool transform(const SkMatrix& matrix, SkRRect* dst) const; /** Writes text representation of SkRRect to standard output. diff --git a/gfx/skia/skia/include/core/SkRecorder.h b/gfx/skia/skia/include/core/SkRecorder.h @@ -1,47 +0,0 @@ -/* - * 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 SkRecorder_DEFINED -#define SkRecorder_DEFINED - -#include "include/private/base/SkAPI.h" - -class SkCanvas; - -namespace skcpu { -class Recorder; -} - -class SK_API SkRecorder { -public: - SkRecorder() = default; - virtual ~SkRecorder() = default; - SkRecorder(const SkRecorder&) = delete; - SkRecorder(SkRecorder&&) = delete; - SkRecorder& operator=(const SkRecorder&) = delete; - - enum class Type { - kCPU, - kGanesh, - kGraphite, - }; - - virtual Type type() const = 0; - - virtual skcpu::Recorder* cpuRecorder() = 0; - -private: - - /** - * Attempts to create and return an SkCaptureCanvas that wraps the provided base canvas. - * Returns nullptr if capture is not enabled. - */ - virtual SkCanvas* makeCaptureCanvas(SkCanvas*) = 0; - - friend class SkSurface_Base; -}; - -#endif diff --git a/gfx/skia/skia/include/core/SkRect.h b/gfx/skia/skia/include/core/SkRect.h @@ -8,21 +8,17 @@ #ifndef SkRect_DEFINED #define SkRect_DEFINED -#include "include/core/SkPathTypes.h" #include "include/core/SkPoint.h" #include "include/core/SkSize.h" -#include "include/core/SkSpan.h" #include "include/core/SkTypes.h" #include "include/private/base/SkFloatingPoint.h" #include "include/private/base/SkSafe32.h" #include "include/private/base/SkTFitsIn.h" #include <algorithm> -#include <array> #include <cmath> #include <cstdint> #include <cstring> -#include <optional> struct SkRect; class SkString; @@ -575,13 +571,6 @@ struct SK_API SkIRect { return MakeLTRB(std::min(fLeft, fRight), std::min(fTop, fBottom), std::max(fLeft, fRight), std::max(fTop, fBottom)); } - - /** Returns pointer to first int32 in SkIRect, to treat it as an array with four - entries. - - @return pointer to fLeft - */ - const int32_t* asInt32s() const { return &fLeft; } }; /** \struct SkRect @@ -834,39 +823,16 @@ struct SK_API SkRect { return !(a == b); } - SkPoint TL() const { return {fLeft, fTop}; } - SkPoint TR() const { return {fRight, fTop}; } - SkPoint BL() const { return {fLeft, fBottom}; } - SkPoint BR() const { return {fRight, fBottom}; } + /** Returns four points in quad that enclose SkRect ordered as: top-left, top-right, + bottom-right, bottom-left. - /** Returns four points in quad that enclose SkRect, - * respect the specified path-direction. - */ - std::array<SkPoint, 4> toQuad(SkPathDirection dir = SkPathDirection::kCW) const { - std::array<SkPoint, 4> storage; - this->copyToQuad(storage, dir); - return storage; - } - - // Same as toQuad(), but copies the 4 points into the specified storage - // which must be at least a size of 4. - void copyToQuad(SkSpan<SkPoint> pts, SkPathDirection dir = SkPathDirection::kCW) const { - SkASSERT(pts.size() >= 4); - pts[0] = this->TL(); - pts[2] = this->BR(); - if (dir == SkPathDirection::kCW) { - pts[1] = this->TR(); - pts[3] = this->BL(); - } else { - pts[1] = this->BL(); - pts[3] = this->TR(); - } - } + TODO: Consider adding parameter to control whether quad is clockwise or counterclockwise. - // DEPRECATED: use std::array or copyToQuad versions - void toQuad(SkPoint quad[4]) const { - this->copyToQuad({quad, 4}); - } + @param quad storage for corners of SkRect + + example: https://fiddle.skia.org/c/@Rect_toQuad + */ + void toQuad(SkPoint quad[4]) const; /** Sets SkRect to (0, 0, 0, 0). @@ -904,64 +870,43 @@ struct SK_API SkRect { fBottom = bottom; } - /** - * Compute the bounds of the span of points. - * If the span is empty, returns the empty-rect {0, 0, 0, 0. - * If the span contains non-finite values (inf or nan), returns {} - */ - static std::optional<SkRect> Bounds(SkSpan<const SkPoint> pts); - - static SkRect BoundsOrEmpty(SkSpan<const SkPoint> pts) { - if (auto bounds = Bounds(pts)) { - return bounds.value(); - } else { - return MakeEmpty(); - } - } - /** Sets to bounds of SkPoint array with count entries. If count is zero or smaller, or if SkPoint array contains an infinity or NaN, sets to (0, 0, 0, 0). Result is either empty or sorted: fLeft is less than or equal to fRight, and fTop is less than or equal to fBottom. - @param pts SkPoint span + @param pts SkPoint array + @param count entries in array */ - void setBounds(SkSpan<const SkPoint> pts) { - (void)this->setBoundsCheck(pts); + void setBounds(const SkPoint pts[], int count) { + (void)this->setBoundsCheck(pts, count); } - /** Sets to bounds of the span of points, and return true (if all point values were finite). - * - * If the span is empty, set the rect to empty() and return true. - * If any point contains an infinity or NaN, set the rect to empty and return false. - * - * @param pts SkPoint span - * example: https://fiddle.skia.org/c/@Rect_setBoundsCheck - */ - bool setBoundsCheck(SkSpan<const SkPoint> pts); - - /** Sets to bounds of the span of points. - * - * If the span is empty, set the rect to empty(). - * If any point contains an infinity or NaN, set the rect to NaN. - * - * @param pts SkPoint span - * example: https://fiddle.skia.org/c/@Rect_setBoundsNoCheck - */ - void setBoundsNoCheck(SkSpan<const SkPoint> pts); + /** Sets to bounds of SkPoint array with count entries. Returns false if count is + zero or smaller, or if SkPoint array contains an infinity or NaN; in these cases + sets SkRect to (0, 0, 0, 0). -#ifdef SK_SUPPORT_UNSPANNED_APIS - void setBounds(const SkPoint pts[], int count) { - this->setBounds({pts, count}); - } - void setBoundsNoCheck(const SkPoint pts[], int count) { - this->setBoundsNoCheck({pts, count}); - } - bool setBoundsCheck(const SkPoint pts[], int count) { - return this->setBoundsCheck({pts, count}); - } -#endif + Result is either empty or sorted: fLeft is less than or equal to fRight, and + fTop is less than or equal to fBottom. + + @param pts SkPoint array + @param count entries in array + @return true if all SkPoint values are finite + + example: https://fiddle.skia.org/c/@Rect_setBoundsCheck + */ + bool setBoundsCheck(const SkPoint pts[], int count); + + /** Sets to bounds of SkPoint pts array with count entries. If any SkPoint in pts + contains infinity or NaN, all SkRect dimensions are set to NaN. + + @param pts SkPoint array + @param count entries in array + + example: https://fiddle.skia.org/c/@Rect_setBoundsNoCheck + */ + void setBoundsNoCheck(const SkPoint pts[], int count); /** Sets bounds to the smallest SkRect enclosing SkPoint p0 and p1. The result is sorted and may be empty. Does not check to see if values are finite. diff --git a/gfx/skia/skia/include/core/SkRegion.h b/gfx/skia/skia/include/core/SkRegion.h @@ -14,13 +14,11 @@ #include "include/private/base/SkDebug.h" #include "include/private/base/SkTypeTraits.h" -#include "include/core/SkPath.h" // IWYU -- for SK_HIDE_PATH_EDIT_METHODS - #include <cstddef> #include <cstdint> #include <type_traits> -class SkPathBuilder; +class SkPath; /** \class SkRegion SkRegion describes the set of pixels used to clip SkCanvas. SkRegion is compact, @@ -179,7 +177,7 @@ public: */ int computeRegionComplexity() const; - /** Appends outline of SkRegion to path builder. + /** Appends outline of SkRegion to path. Returns true if SkRegion is not empty; otherwise, returns false, and leaves path unmodified. @@ -188,16 +186,7 @@ public: example: https://fiddle.skia.org/c/@Region_getBoundaryPath */ - bool addBoundaryPath(SkPathBuilder*) const; - - /** - * Return the boundary of the region as a path. - */ - SkPath getBoundaryPath() const; - -#ifndef SK_HIDE_PATH_EDIT_METHODS bool getBoundaryPath(SkPath* path) const; -#endif /** Constructs an empty SkRegion. SkRegion is set to empty bounds at (0, 0) with zero width and height. Always returns false. @@ -571,7 +560,7 @@ public: /** \class SkRegion::Spanerator Returns the line segment ends within SkRegion that intersect a horizontal line. */ - class SK_API Spanerator { + class Spanerator { public: /** Sets SkRegion::Spanerator to return line segments in SkRegion on scan line. @@ -637,7 +626,7 @@ private: struct RunHead; static RunHead* emptyRunHeadPtr() { return (SkRegion::RunHead*) -1; } - static constexpr const RunHead* const kRectRunHeadPtr = nullptr; + static constexpr RunHead* kRectRunHeadPtr = nullptr; // allocate space for count runs void allocateRuns(int count); diff --git a/gfx/skia/skia/include/core/SkScalar.h b/gfx/skia/skia/include/core/SkScalar.h @@ -133,6 +133,18 @@ static inline SkScalar SkScalarInterp(SkScalar A, SkScalar B, SkScalar t) { return A + (B - A) * t; } +/** Interpolate along the function described by (keys[length], values[length]) + for the passed searchKey. SearchKeys outside the range keys[0]-keys[Length] + clamp to the min or max value. This function assumes the number of pairs + (length) will be small and a linear search is used. + + Repeated keys are allowed for discontinuous functions (so long as keys is + monotonically increasing). If key is the value of a repeated scalar in + keys the first one will be used. +*/ +SkScalar SkScalarInterpFunc(SkScalar searchKey, const SkScalar keys[], + const SkScalar values[], int length); + /* * Helper to compare an array of scalars. */ diff --git a/gfx/skia/skia/include/core/SkShader.h b/gfx/skia/skia/include/core/SkShader.h @@ -9,13 +9,13 @@ #define SkShader_DEFINED #include "include/core/SkColor.h" -#include "include/core/SkColorSpace.h" #include "include/core/SkFlattenable.h" #include "include/core/SkRefCnt.h" #include "include/private/base/SkAPI.h" class SkBlender; class SkColorFilter; +class SkColorSpace; class SkImage; class SkMatrix; enum class SkBlendMode; @@ -69,26 +69,13 @@ public: sk_sp<SkShader> makeWithColorFilter(sk_sp<SkColorFilter>) const; /** - * Return a shader that will compute this shader in a context such that any child shaders - * return RGBA values converted to the `inputCS` colorspace. - * - * It is then assumed that the RGBA values returned by this shader have been transformed into - * `outputCS` by the shader being wrapped. By default, shaders are assumed to return values - * in the destination colorspace and premultiplied. Using a different outputCS than inputCS - * allows custom shaders to replace the color management Skia normally performs w/o forcing - * authors to otherwise manipulate surface/image color info to avoid unnecessary or incorrect - * work. - * - * If the shader is not performing colorspace conversion but needs to operate in the `inputCS` - * then it should have `outputCS` be the same as `inputCS`. Regardless of the `outputCS` here, - * the RGBA values of the returned SkShader are always converted from `outputCS` to the - * destination surface color space. - * - * A null inputCS is assumed to be the destination CS. - * A null outputCS is assumed to be the inputCS. + * Return a shader that will compute this shader in a specific color space. + * By default, all shaders operate in the destination (surface) color space. + * The results of a shader are still always converted to the destination - this + * API has no impact on simple shaders or images. Primarily, it impacts shaders + * that perform mathematical operations, like Blend shaders, or runtime shaders. */ - sk_sp<SkShader> makeWithWorkingColorSpace(sk_sp<SkColorSpace> inputCS, - sk_sp<SkColorSpace> outputCS=nullptr) const; + sk_sp<SkShader> makeWithWorkingColorSpace(sk_sp<SkColorSpace>) const; private: SkShader() = default; diff --git a/gfx/skia/skia/include/core/SkStrokeRec.h b/gfx/skia/skia/include/core/SkStrokeRec.h @@ -17,7 +17,6 @@ #include <cstdint> class SkPath; -class SkPathBuilder; SK_BEGIN_REQUIRE_DENSE class SK_API SkStrokeRec { @@ -97,7 +96,7 @@ public: * * src and dst may be the same path. */ - bool applyToPath(SkPathBuilder* dst, const SkPath& src) const; + bool applyToPath(SkPath* dst, const SkPath& src) const; /** * Apply these stroke parameters to a paint. diff --git a/gfx/skia/skia/include/core/SkSurface.h b/gfx/skia/skia/include/core/SkSurface.h @@ -25,14 +25,13 @@ class GrBackendSemaphore; class GrBackendTexture; class GrRecordingContext; class GrSurfaceCharacterization; +enum GrSurfaceOrigin : int; class SkBitmap; class SkCanvas; class SkCapabilities; class SkColorSpace; class SkPaint; -class SkRecorder; class SkSurface; -enum GrSurfaceOrigin : int; struct SkIRect; struct SkISize; @@ -227,12 +226,6 @@ public: */ skgpu::graphite::Recorder* recorder() const; - /** Returns the base SkRecorder being used by the SkSurface. - - @return the recorder; should be non-null for drawable surfaces - */ - SkRecorder* baseRecorder() const; - enum class BackendHandleAccess { kFlushRead, //!< back-end object is readable kFlushWrite, //!< back-end object is writable diff --git a/gfx/skia/skia/include/core/SkTextBlob.h b/gfx/skia/skia/include/core/SkTextBlob.h @@ -10,12 +10,9 @@ #include "include/core/SkFont.h" #include "include/core/SkFontTypes.h" -#include "include/core/SkPoint.h" -#include "include/core/SkRSXform.h" #include "include/core/SkRect.h" #include "include/core/SkRefCnt.h" #include "include/core/SkScalar.h" -#include "include/core/SkSpan.h" #include "include/core/SkTypes.h" #include "include/private/base/SkDebug.h" #include "include/private/base/SkTemplates.h" @@ -28,6 +25,8 @@ class SkData; class SkPaint; class SkTypeface; struct SkDeserialProcs; +struct SkPoint; +struct SkRSXform; struct SkSerialProcs; namespace sktext { @@ -134,9 +133,8 @@ public: @return new textblob or nullptr */ static sk_sp<SkTextBlob> MakeFromPosTextH(const void* text, size_t byteLength, - SkSpan<const SkScalar> xpos, SkScalar constY, - const SkFont& font, - SkTextEncoding encoding = SkTextEncoding::kUTF8); + const SkScalar xpos[], SkScalar constY, const SkFont& font, + SkTextEncoding encoding = SkTextEncoding::kUTF8); /** Returns a textblob built from a single run of text with positions. This is equivalent to using SkTextBlobBuilder and calling allocRunPos(). @@ -150,33 +148,13 @@ public: @return new textblob or nullptr */ static sk_sp<SkTextBlob> MakeFromPosText(const void* text, size_t byteLength, - SkSpan<const SkPoint> pos, const SkFont& font, + const SkPoint pos[], const SkFont& font, SkTextEncoding encoding = SkTextEncoding::kUTF8); static sk_sp<SkTextBlob> MakeFromRSXform(const void* text, size_t byteLength, - SkSpan<const SkRSXform> xform, const SkFont& font, + const SkRSXform xform[], const SkFont& font, SkTextEncoding encoding = SkTextEncoding::kUTF8); - // Helpers for glyphs - - static sk_sp<SkTextBlob> MakeFromPosHGlyphs(SkSpan<const SkGlyphID> glyphs, - SkSpan<const SkScalar> xpos, SkScalar constY, - const SkFont& font) { - return MakeFromPosTextH(glyphs.data(), glyphs.size() * sizeof(SkGlyphID), xpos, constY, - font, SkTextEncoding::kGlyphID); - } - static sk_sp<SkTextBlob> MakeFromPosGlyphs(SkSpan<const SkGlyphID> glyphs, - SkSpan<const SkPoint> pos, const SkFont& font) { - return MakeFromPosText(glyphs.data(), glyphs.size() * sizeof(SkGlyphID), pos, font, - SkTextEncoding::kGlyphID); - } - static sk_sp<SkTextBlob> MakeFromRSXformGlyphs(SkSpan<const SkGlyphID> glyphs, - SkSpan<const SkRSXform> xform, - const SkFont& font) { - return MakeFromRSXform(glyphs.data(), glyphs.size() * sizeof(SkGlyphID), xform, font, - SkTextEncoding::kGlyphID); - } - /** Writes data to allow later reconstruction of SkTextBlob. memory points to storage to receive the encoded data, and memory_size describes the size of storage. Returns bytes used if provided storage is large enough to hold all data; @@ -262,28 +240,6 @@ public: const RunRecord* fRunRecord; }; -#ifdef SK_SUPPORT_UNSPANNED_APIS - static sk_sp<SkTextBlob> MakeFromPosTextH(const void* text, size_t byteLength, - const SkScalar xpos[], SkScalar constY, - const SkFont& font, - SkTextEncoding encoding = SkTextEncoding::kUTF8) { - const size_t worstCaseCount = byteLength; - return MakeFromPosTextH(text, byteLength, {xpos, worstCaseCount}, constY, font, encoding); - } - static sk_sp<SkTextBlob> MakeFromPosText(const void* text, size_t byteLength, - const SkPoint pos[], const SkFont& font, - SkTextEncoding encoding = SkTextEncoding::kUTF8) { - const size_t worstCaseCount = byteLength; - return MakeFromPosText(text, byteLength, {pos, worstCaseCount}, font, encoding); - } - static sk_sp<SkTextBlob> MakeFromRSXform(const void* text, size_t byteLength, - const SkRSXform xform[], const SkFont& font, - SkTextEncoding encoding = SkTextEncoding::kUTF8) { - const size_t worstCaseCount = byteLength; - return MakeFromRSXform(text, byteLength, {xform, worstCaseCount}, font, encoding); - } -#endif - private: friend class SkNVRefCnt<SkTextBlob>; diff --git a/gfx/skia/skia/include/core/SkTraceMemoryDump.h b/gfx/skia/skia/include/core/SkTraceMemoryDump.h @@ -104,13 +104,6 @@ public: */ virtual void dumpBudgetedState(const char* /*dumpName*/, bool /*isBudgeted*/) {} - /** - * Returns true if we should dump sizeless non-texture objects (e.g. Samplers, pipelines, etc). - * Memoryless textures are always dumped. This call is only used when dumping Graphite memory - * statistics. - */ - virtual bool shouldDumpSizelessObjects() const { return false; } - protected: virtual ~SkTraceMemoryDump() = default; SkTraceMemoryDump() = default; diff --git a/gfx/skia/skia/include/core/SkTypeface.h b/gfx/skia/skia/include/core/SkTypeface.h @@ -14,7 +14,6 @@ #include "include/core/SkFourByteTag.h" #include "include/core/SkRect.h" #include "include/core/SkRefCnt.h" -#include "include/core/SkSpan.h" #include "include/core/SkString.h" #include "include/core/SkTypes.h" #include "include/private/SkWeakRefCnt.h" @@ -69,28 +68,31 @@ public: /** Copy into 'coordinates' (allocated by the caller) the design variation coordinates. * - * @param coordinates the span into which to write the design variation coordinates. + * @param coordinates the buffer into which to write the design variation coordinates. + * @param coordinateCount the number of entries available through 'coordinates'. * * @return The number of axes, or -1 if there is an error. - * If 'coordinates.size() >= numAxes' then 'coordinates' will be + * If 'coordinates != nullptr' and 'coordinateCount >= numAxes' then 'coordinates' will be * filled with the variation coordinates describing the position of this typeface in design * variation space. It is possible the number of axes can be retrieved but actual position * cannot. */ - int getVariationDesignPosition( - SkSpan<SkFontArguments::VariationPosition::Coordinate> coordinates) const; + int getVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[], + int coordinateCount) const; /** Copy into 'parameters' (allocated by the caller) the design variation parameters. * - * @param parameters the span into which to write the design variation parameters. + * @param parameters the buffer into which to write the design variation parameters. + * @param coordinateCount the number of entries available through 'parameters'. * * @return The number of axes, or -1 if there is an error. - * If 'parameters.size() >= numAxes' then 'parameters' will be + * If 'parameters != nullptr' and 'parameterCount >= numAxes' then 'parameters' will be * filled with the variation parameters describing the position of this typeface in design * variation space. It is possible the number of axes can be retrieved but actual parameters * cannot. */ - int getVariationDesignParameters(SkSpan<SkFontParameters::Variation::Axis> parameters) const; + int getVariationDesignParameters(SkFontParameters::Variation::Axis parameters[], + int parameterCount) const; /** Return a 32bit value for this typeface, unique for the underlying font data. Will never return 0. @@ -147,13 +149,14 @@ public: /** * Given an array of UTF32 character codes, return their corresponding glyph IDs. * - * @param unis span of UTF32 chars + * @param chars pointer to the array of UTF32 chars + * @param number of chars and glyphs * @param glyphs returns the corresponding glyph IDs for each character. */ - void unicharsToGlyphs(SkSpan<const SkUnichar> unis, SkSpan<SkGlyphID> glyphs) const; + void unicharsToGlyphs(const SkUnichar uni[], int count, SkGlyphID glyphs[]) const; - size_t textToGlyphs(const void* text, size_t byteLength, SkTextEncoding encoding, - SkSpan<SkGlyphID> glyphs) const; + int textToGlyphs(const void* text, size_t byteLength, SkTextEncoding encoding, + SkGlyphID glyphs[], int maxGlyphCount) const; /** * Return the glyphID that corresponds to the specified unicode code-point @@ -176,10 +179,10 @@ public: /** Copy into tags[] (allocated by the caller) the list of table tags in * the font, and return the number. This will be the same as CountTables() - * or 0 if an error occured. If tags is empty, this only returns the count + * or 0 if an error occured. If tags == NULL, this only returns the count * (the same as calling countTables()). */ - int readTableTags(SkSpan<SkFontTableTag> tags) const; + int getTableTags(SkFontTableTag tags[]) const; /** Given a table tag, return the size of its contents, or 0 if not present */ @@ -229,22 +232,22 @@ public: * typeface's units per em (see getUnitsPerEm). * * Some typefaces are known to never support kerning. Calling this method - * with empty spans (e.g. getKerningPairAdustments({}, {})) returns + * with all zeros (e.g. getKerningPairAdustments(NULL, 0, NULL)) returns * a boolean indicating if the typeface might support kerning. If it * returns false, then it will always return false (no kerning) for all * possible glyph runs. If it returns true, then it *may* return true for - * some glyph runs. + * somne glyph runs. * - * If the method returns true, and there are 1 or more glyphs in the span, then - * this will return in adjustments N values, - * where N = min(glyphs.size() - 1, adjustments.size()). - - * If the method returns false, then no kerning should be applied, and the adjustments + * If count is non-zero, then the glyphs parameter must point to at least + * [count] valid glyph IDs, and the adjustments parameter must be + * sized to at least [count - 1] entries. If the method returns true, then + * [count-1] entries in the adjustments array will be set. If the method + * returns false, then no kerning should be applied, and the adjustments * array will be in an undefined state (possibly some values may have been * written, but none of them should be interpreted as valid values). */ - bool getKerningPairAdjustments(SkSpan<const SkGlyphID> glyphs, - SkSpan<int32_t> adjustments) const; + bool getKerningPairAdjustments(const SkGlyphID glyphs[], int count, + int32_t adjustments[]) const; struct LocalizedString { SkString fString; @@ -353,37 +356,7 @@ public: FactoryId id, sk_sp<SkTypeface> (*make)(std::unique_ptr<SkStreamAsset>, const SkFontArguments&)); -#ifdef SK_SUPPORT_UNSPANNED_APIS -public: - int getVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[], - int count) const { - return this->getVariationDesignPosition({coordinates, count}); - } - int getVariationDesignParameters(SkFontParameters::Variation::Axis parameters[], - int count) const { - return this->getVariationDesignParameters({parameters, count}); - } - void unicharsToGlyphs(const SkUnichar unis[], int count, SkGlyphID glyphs[]) const { - this->unicharsToGlyphs({unis, count}, {glyphs, count}); - } - int textToGlyphs(const void* text, size_t byteLength, SkTextEncoding encoding, - SkGlyphID glyphs[], int maxGlyphCount) const { - return (int)this->textToGlyphs(text, byteLength, encoding, {glyphs, maxGlyphCount}); - } - int getTableTags(SkFontTableTag tags[]) const { - const size_t count = tags ? MAX_REASONABLE_TABLE_COUNT : 0; - return this->readTableTags({tags, count}); - } - bool getKerningPairAdjustments(const SkGlyphID glyphs[], int count, - int32_t adjustments[]) const { - return this->getKerningPairAdjustments({glyphs, count}, {adjustments, count}); - } -#endif - protected: - // needed until onGetTableTags() is updated to take a span - enum { MAX_REASONABLE_TABLE_COUNT = (1 << 16) - 1 }; - explicit SkTypeface(const SkFontStyle& style, bool isFixedPitch = false); ~SkTypeface() override; @@ -416,7 +389,7 @@ protected: // The mapping from glyph to Unicode; array indices are glyph ids. // For each glyph, give the default Unicode value, if it exists. // dstArray is non-null, and points to an array of size this->countGlyphs(). - virtual void getGlyphToUnicodeMap(SkSpan<SkUnichar> dstArray) const = 0; + virtual void getGlyphToUnicodeMap(SkUnichar* dstArray) const = 0; virtual std::unique_ptr<SkStreamAsset> onOpenStream(int* ttcIndex) const = 0; @@ -425,18 +398,20 @@ protected: virtual bool onGlyphMaskNeedsCurrentColor() const = 0; virtual int onGetVariationDesignPosition( - SkSpan<SkFontArguments::VariationPosition::Coordinate>) const = 0; + SkFontArguments::VariationPosition::Coordinate coordinates[], + int coordinateCount) const = 0; - virtual int onGetVariationDesignParameters(SkSpan<SkFontParameters::Variation::Axis>) const = 0; + virtual int onGetVariationDesignParameters( + SkFontParameters::Variation::Axis parameters[], int parameterCount) const = 0; virtual void onGetFontDescriptor(SkFontDescriptor*, bool* isLocal) const = 0; - virtual void onCharsToGlyphs(SkSpan<const SkUnichar>, SkSpan<SkGlyphID>) const = 0; + virtual void onCharsToGlyphs(const SkUnichar* chars, int count, SkGlyphID glyphs[]) const = 0; virtual int onCountGlyphs() const = 0; virtual int onGetUPEM() const = 0; - virtual bool onGetKerningPairAdjustments(SkSpan<const SkGlyphID>, - SkSpan<int32_t> adjustments) const; + virtual bool onGetKerningPairAdjustments(const SkGlyphID glyphs[], int count, + int32_t adjustments[]) const; /** Returns the family name of the typeface as known by its font manager. * This name may or may not be produced by the family name iterator. @@ -448,7 +423,7 @@ protected: /** Returns an iterator over the family names in the font. */ virtual LocalizedStrings* onCreateFamilyNameIterator() const = 0; - virtual int onGetTableTags(SkSpan<SkFontTableTag>) const = 0; + virtual int onGetTableTags(SkFontTableTag tags[]) const = 0; virtual size_t onGetTableData(SkFontTableTag, size_t offset, size_t length, void* data) const = 0; virtual sk_sp<SkData> onCopyTableData(SkFontTableTag) const; @@ -472,7 +447,7 @@ private: friend class SkPDFFont; // getAdvancedMetrics friend class SkTypeface_proxy; friend class SkFontPriv; // getGlyphToUnicodeMap - friend void TestSkTypefaceGlyphToUnicodeMap(SkTypeface&, SkSpan<SkUnichar>); + 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 @@ -96,7 +96,6 @@ #if defined(SK_HISTOGRAM_ENUMERATION) || \ defined(SK_HISTOGRAM_BOOLEAN) || \ defined(SK_HISTOGRAM_EXACT_LINEAR) || \ - defined(SK_HISTOGRAM_CUSTOM_EXACT_LINEAR) || \ defined(SK_HISTOGRAM_MEMORY_KB) || \ defined(SK_HISTOGRAM_CUSTOM_COUNTS)|| \ defined(SK_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES) @@ -117,11 +116,6 @@ # define SK_HISTOGRAM_EXACT_LINEAR(name, sample, valueMax) #endif -#ifndef SK_HISTOGRAM_CUSTOM_EXACT_LINEAR -# define SK_HISTOGRAM_CUSTOM_EXACT_LINEAR(name, sample, value_min, \ - value_max, bucket_count) -#endif - #ifndef SK_HISTOGRAM_MEMORY_KB # define SK_HISTOGRAM_MEMORY_KB(name, sample) #endif @@ -149,10 +143,6 @@ # define SK_ENABLE_LEGACY_SHADERCONTEXT #endif -#ifndef SK_SUPPORT_MUTABLE_PATHEFFECT -# define SK_SUPPORT_MUTABLE_PATHEFFECT -#endif - #if defined(SK_BUILD_FOR_LIBFUZZER) || defined(SK_BUILD_FOR_AFL_FUZZ) #if !defined(SK_BUILD_FOR_FUZZER) #define SK_BUILD_FOR_FUZZER diff --git a/gfx/skia/skia/include/effects/SkDashPathEffect.h b/gfx/skia/skia/include/effects/SkDashPathEffect.h @@ -8,12 +8,12 @@ #ifndef SkDashPathEffect_DEFINED #define SkDashPathEffect_DEFINED -#include "include/core/SkPathEffect.h" // IWYU pragma: keep (for unspanned apis) #include "include/core/SkRefCnt.h" #include "include/core/SkScalar.h" -#include "include/core/SkSpan.h" #include "include/core/SkTypes.h" +class SkPathEffect; + class SK_API SkDashPathEffect { public: /** intervals: array containing an even number of entries (>=2), with @@ -37,13 +37,7 @@ public: Note: only affects stroked paths. */ - static sk_sp<SkPathEffect> Make(SkSpan<const SkScalar> intervals, SkScalar phase); - -#ifdef SK_SUPPORT_UNSPANNED_APIS - static sk_sp<SkPathEffect> Make(const SkScalar intervals[], int count, SkScalar phase) { - return intervals ? Make({intervals, count}, phase) : nullptr; - } -#endif + static sk_sp<SkPathEffect> Make(const SkScalar intervals[], int count, SkScalar phase); }; #endif diff --git a/gfx/skia/skia/include/effects/SkRuntimeEffect.h b/gfx/skia/skia/include/effects/SkRuntimeEffect.h @@ -139,7 +139,7 @@ public: // When not 0, this field allows Skia to assign a stable key to a known runtime effect uint32_t fStableKey = 0; - // TODO(skbug.com/40042585) - Replace this with a promised SkCapabilities? + // TODO(skia:11209) - Replace this with a promised SkCapabilities? // This flag lifts the ES2 restrictions on Runtime Effects that are gated by the // `strictES2Mode` check. Be aware that the software renderer and pipeline-stage effect are // still largely ES3-unaware and can still fail or crash if post-ES2 features are used. diff --git a/gfx/skia/skia/include/encode/SkEncoder.h b/gfx/skia/skia/include/encode/SkEncoder.h @@ -22,7 +22,7 @@ public: * A single frame to be encoded into an animated image. * * If a frame does not fit in the canvas size, this is an error. - * TODO(skbug.com/40044793): Add offsets when we have support for an encoder that supports using + * TODO(skia:13705): Add offsets when we have support for an encoder that supports using * offsets. */ struct SK_API Frame { diff --git a/gfx/skia/skia/include/encode/SkICC.h b/gfx/skia/skia/include/encode/SkICC.h @@ -26,7 +26,7 @@ SK_API sk_sp<SkData> SkWriteICCProfile(const skcms_ICCProfile*, const char* desc // Utility function for populating the grid_16 member of skcms_A2B and skcms_B2A // structures. This converts a point in XYZD50 to its representation in grid_16_lab. // It will write 6 bytes. The behavior of this function matches how skcms will decode -// values, but might not match the specification, see skbug.com/40044907. +// values, but might not match the specification, see https://crbug.com/skia/13807. SK_API void SkICCFloatXYZD50ToGrid16Lab(const float* float_xyz, uint8_t* grid16_lab); // Utility function for popluating the table_16 member of skcms_Curve structure. diff --git a/gfx/skia/skia/include/encode/SkJpegEncoder.h b/gfx/skia/skia/include/encode/SkJpegEncoder.h @@ -79,6 +79,16 @@ struct Options { */ const SkData* xmpMetadata = nullptr; + /** + * An optional ICC profile to override the default behavior. + * + * The default behavior is to generate an ICC profile using a primary matrix and + * analytic transfer function. If the color space of |src| cannot be represented + * in this way (e.g, it is HLG or PQ), then no profile will be embedded. + */ + const skcms_ICCProfile* fICCProfile = nullptr; + const char* fICCProfileDescription = nullptr; + std::optional<SkEncodedOrigin> fOrigin; }; @@ -95,11 +105,6 @@ SK_API bool Encode(SkWStream* dst, const Options& options); /** - * Returns the encoded data for the pixmap, or nullptr on failure. - */ -SK_API sk_sp<SkData> Encode(const SkPixmap& src, const Options& options); - -/** * Encode the provided image and return the resulting bytes. If the image was created as * a texture-backed image on a GPU context, that |ctx| must be provided so the pixels * can be read before being encoded. For raster-backed images, |ctx| can be nullptr. diff --git a/gfx/skia/skia/include/encode/SkPngEncoder.h b/gfx/skia/skia/include/encode/SkPngEncoder.h @@ -10,7 +10,6 @@ #include "include/core/SkDataTable.h" #include "include/core/SkRefCnt.h" -#include "include/private/SkHdrMetadata.h" #include "include/private/base/SkAPI.h" // TODO(kjlubick) update clients to directly include this @@ -74,9 +73,14 @@ struct Options { sk_sp<SkDataTable> fComments; /** - * Container for any HDR metadata to include in the encoded image. + * An optional ICC profile to override the default behavior. + * + * The default behavior is to generate an ICC profile using a primary matrix and + * analytic transfer function. If the color space of |src| cannot be represented + * in this way (e.g, it is HLG or PQ), then no profile will be embedded. */ - skhdr::Metadata fHdrMetadata; + const skcms_ICCProfile* fICCProfile = nullptr; + const char* fICCProfileDescription = nullptr; /** * If non-null, then a gainmap and its metadata will be encoded as png chunks. @@ -101,11 +105,6 @@ struct Options { SK_API bool Encode(SkWStream* dst, const SkPixmap& src, const Options& options); /** - * Returns the encoded data for the pixmap, or nullptr on failure. - */ -SK_API sk_sp<SkData> Encode(const SkPixmap& src, const Options& options); - -/** * Encode the provided image and return the resulting bytes. If the image was created as * a texture-backed image on a GPU context, that |ctx| must be provided so the pixels * can be read before being encoded. For raster-backed images, |ctx| can be nullptr. diff --git a/gfx/skia/skia/include/encode/SkPngRustEncoder.h b/gfx/skia/skia/include/encode/SkPngRustEncoder.h @@ -1,88 +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 SkPngRustEncoder_DEFINED -#define SkPngRustEncoder_DEFINED - -#include <cstdint> -#include <memory> - -#include "include/core/SkDataTable.h" -#include "include/core/SkRefCnt.h" -#include "include/private/base/SkAPI.h" - -class SkEncoder; -class SkPixmap; -class SkWStream; - -namespace SkPngRustEncoder { - -/* - * Compression level. - */ -enum class CompressionLevel : uint8_t { - // Low compression level - fast, but may result in bigger PNG files. - kLow, - - // Medium compression level - somewhere in-between `kLow` and `kHigh`. - kMedium, - - // High compression level - slow, but should results in smaller PNG files. - kHigh, -}; - -/* - * PNG encoding options. - * - * TODO(https://crbug.com/379312510): Add support for `SkPngEncoder::Options` - * like: - * - Comments - `tEXt` chunks. - * - Color profile - `iCCP` chunk. - */ -struct Options { - CompressionLevel fCompressionLevel = CompressionLevel::kMedium; - - /** - * Represents comments to be written into tEXt chunks of the png. - * - * The 2i-th entry is the keyword for the i-th comment, - * and the (2i + 1)-th entry is the text for the i-th comment. - * - * All entries are treated as strings encoded as Latin-1 (i.e. - * ISO-8859-1). The strings may, but don't have to be NUL-terminated - * (trailing NUL characters will be stripped). Encoding will fail if - * keyword or text don't meet the requirements of the PNG spec - text may - * have any length and contain any of the 191 Latin-1 characters (and/or - * the linefeed character), but keyword's length is restricted to at most - * 79 characters and it can't contain a non-breaking space character. - */ - sk_sp<SkDataTable> fComments; -}; - -/** - * Encode the |src| pixels to the |dst| stream. - * |options| may be used to control the encoding behavior. - * - * Returns true on success. Returns false on an invalid or unsupported |src|. - * - */ -SK_API bool Encode(SkWStream* dst, const SkPixmap& src, const Options& options); - -/** - * Create a png encoder that will encode the |src| pixels to the |dst| stream. - * |options| may be used to control the encoding behavior. - * - * The primary use of this is incremental encoding of the pixels. - * - * |dst| is unowned but must remain valid for the lifetime of the object. - * - * This returns nullptr on an invalid or unsupported |src|. - */ -SK_API std::unique_ptr<SkEncoder> Make(SkWStream* dst, const SkPixmap& src, const Options& options); - -} // namespace SkPngRustEncoder - -#endif // SkPngRustEncoder_DEFINED diff --git a/gfx/skia/skia/include/encode/SkWebpEncoder.h b/gfx/skia/skia/include/encode/SkWebpEncoder.h @@ -42,6 +42,16 @@ struct SK_API Options { */ Compression fCompression = Compression::kLossy; float fQuality = 100.0f; + + /** + * An optional ICC profile to override the default behavior. + * + * The default behavior is to generate an ICC profile using a primary matrix and + * analytic transfer function. If the color space of |src| cannot be represented + * in this way (e.g, it is HLG or PQ), then no profile will be embedded. + */ + const skcms_ICCProfile* fICCProfile = nullptr; + const char* fICCProfileDescription = nullptr; }; /** @@ -53,11 +63,6 @@ struct SK_API Options { SK_API bool Encode(SkWStream* dst, const SkPixmap& src, const Options& options); /** - * Returns the encoded data for the pixmap, or nullptr on failure. - */ -SK_API sk_sp<SkData> Encode(const SkPixmap& src, const Options& options); - -/** * Encode the provided image and return the resulting bytes. If the image was created as * a texture-backed image on a GPU context, that |ctx| must be provided so the pixels * can be read before being encoded. For raster-backed images, |ctx| can be nullptr. diff --git a/gfx/skia/skia/include/gpu/ganesh/GrDirectContext.h b/gfx/skia/skia/include/gpu/ganesh/GrDirectContext.h @@ -515,30 +515,7 @@ public: bool supportsDistanceFieldText() const; - /** - * Returns true if the underlying Vulkan implementation can accurately detect when the data in - * the pipeline cache changes. Returns false on non-Vulkan implementations. - * - * When this is false, the return value of `hasNewVkPipelineCacheData` will occasionally issue a - * false positive. - */ - bool canDetectNewVkPipelineCacheData() const; - /** - * For Vulkan implementations, returns true if the data in the pipeline cache could have changed - * since the last call to `storeVkPipelineCacheData`. Always returns true on non-Vulkan - * implementations. - * - * Pipeline cache changes are detected when creating new pipelines, however this will - * occasionally result in a false positive. When VK_EXT_pipeline_creation_cache_control is - * enabled, we additionally know when a pipeline creation does not change the cache, thus - * eliminating false-positives. - * - * Check `canDetectNewVkPipelineCacheData` to see whether VK_EXT_pipeline_creation_cache_control - * is available and enabled. - */ - bool hasNewVkPipelineCacheData() const; void storeVkPipelineCacheData(); - void storeVkPipelineCacheData(size_t maxSize); /** * Retrieve the default GrBackendFormat for a given SkColorType and renderability. diff --git a/gfx/skia/skia/include/gpu/ganesh/GrRecordingContext.h b/gfx/skia/skia/include/gpu/ganesh/GrRecordingContext.h @@ -33,13 +33,6 @@ class GrThreadSafeCache; class SkArenaAlloc; class SkCapabilities; class SkJSONWriter; -class SkGaneshRecorder; -class SkRecorder; - -namespace skcpu { -class ContextImpl; -class Recorder; -} // namespace skcpu namespace sktext::gpu { class SubRunAllocator; @@ -106,9 +99,6 @@ public: SK_API sk_sp<const SkCapabilities> skCapabilities() const; - SK_API SkRecorder* asRecorder(); - SK_API std::unique_ptr<skcpu::Recorder> makeCPURecorder(); - // Provides access to functions that aren't part of the public API. GrRecordingContextPriv priv(); const GrRecordingContextPriv priv() const; // NOLINT(readability-const-return-type) @@ -279,8 +269,6 @@ private: std::unique_ptr<GrDrawingManager> fDrawingManager; std::unique_ptr<GrProxyProvider> fProxyProvider; - std::unique_ptr<const skcpu::ContextImpl> fCPUContext; - std::unique_ptr<SkGaneshRecorder> fRecorder; #if defined(GPU_TEST_UTILS) int fSuppressWarningMessages = 0; diff --git a/gfx/skia/skia/include/gpu/graphite/BackendSemaphore.h b/gfx/skia/skia/include/gpu/graphite/BackendSemaphore.h @@ -8,13 +8,10 @@ #ifndef skgpu_graphite_BackendSemaphore_DEFINED #define skgpu_graphite_BackendSemaphore_DEFINED -#include "include/private/base/SkAPI.h" +#include "include/core/SkRefCnt.h" +#include "include/gpu/graphite/GraphiteTypes.h" #include "include/private/base/SkAnySubclass.h" -#include <cstddef> - -namespace skgpu { enum class BackendApi : unsigned int; } - namespace skgpu::graphite { class BackendSemaphoreData; diff --git a/gfx/skia/skia/include/gpu/graphite/BackendTexture.h b/gfx/skia/skia/include/gpu/graphite/BackendTexture.h @@ -8,15 +8,12 @@ #ifndef skgpu_graphite_BackendTexture_DEFINED #define skgpu_graphite_BackendTexture_DEFINED +#include "include/core/SkRefCnt.h" #include "include/core/SkSize.h" +#include "include/gpu/graphite/GraphiteTypes.h" #include "include/gpu/graphite/TextureInfo.h" -#include "include/private/base/SkAPI.h" #include "include/private/base/SkAnySubclass.h" -#include <cstddef> - -namespace skgpu { enum class BackendApi : unsigned int; } - namespace skgpu::graphite { class BackendTextureData; diff --git a/gfx/skia/skia/include/gpu/graphite/Context.h b/gfx/skia/skia/include/gpu/graphite/Context.h @@ -10,52 +10,36 @@ #include "include/core/SkImage.h" #include "include/core/SkRefCnt.h" -#include "include/core/SkSize.h" -#include "include/core/SkSpan.h" -#include "include/core/SkTypes.h" +#include "include/core/SkShader.h" +#include "include/gpu/graphite/ContextOptions.h" #include "include/gpu/graphite/GraphiteTypes.h" -#include "include/gpu/graphite/Recorder.h" // IWYU pragma: keep +#include "include/gpu/graphite/Recorder.h" #include "include/private/base/SingleOwner.h" -#include "include/private/base/SkThreadAnnotations.h" #if defined(GPU_TEST_UTILS) #include "include/private/base/SkMutex.h" #endif #include <chrono> -#include <cstddef> -#include <cstdint> #include <functional> #include <memory> -#include <vector> -class SkColorInfo; -class SkSurface; -enum SkYUVColorSpace : int; class SkColorSpace; +class SkRuntimeEffect; class SkTraceMemoryDump; -struct SkIRect; -struct SkImageInfo; - -namespace skcpu { -class ContextImpl; -class Recorder; -} // namespace skcpu - -namespace skgpu { -enum class BackendApi : unsigned int; -enum class GpuStatsFlags : uint32_t; -} namespace skgpu::graphite { class BackendTexture; class Buffer; class ClientMappedBufferManager; +class Context; class ContextPriv; -struct ContextOptions; +class GlobalCache; +class PaintOptions; class PrecompileContext; class QueueManager; +class Recording; class ResourceProvider; class SharedContext; class TextureProxy; @@ -72,15 +56,14 @@ public: BackendApi backend() const; std::unique_ptr<Recorder> makeRecorder(const RecorderOptions& = {}); - std::unique_ptr<skcpu::Recorder> makeCPURecorder(); /** Creates a helper object that can be moved to a different thread and used * for precompilation. */ std::unique_ptr<PrecompileContext> makePrecompileContext(); - InsertStatus insertRecording(const InsertRecordingInfo&); - bool submit(SubmitInfo submitInfo = {}); + bool insertRecording(const InsertRecordingInfo&); + bool submit(SyncToCpu = SyncToCpu::kNo); /** Returns true if there is work that was submitted to the GPU that has not finished. */ bool hasUnfinishedGpuWork() const; @@ -277,19 +260,6 @@ public: */ GpuStatsFlags supportedGpuStats() const; - /* - * TODO (b/412351769): Do not use startCapture() or endCapture() as the feature is still under - * development. - * - * Starts the SkCapture. Must have set ContextOptions::fEnableCapture to start. - */ - void startCapture(); - - /* - * Ends the SkCapture and returns the collected draws and surface creation. - */ - void endCapture(); - // Provides access to functions that aren't part of the public API. ContextPriv priv(); const ContextPriv priv() const; // NOLINT(readability-const-return-type) @@ -321,12 +291,6 @@ private: friend class ContextCtorAccessor; struct PixelTransferResult { - PixelTransferResult(); - PixelTransferResult(const PixelTransferResult&); - PixelTransferResult(PixelTransferResult&&); - PixelTransferResult& operator=(const PixelTransferResult&); - ~PixelTransferResult(); - using ConversionFn = void(void* dst, const void* mappedBuffer); // If null then the transfer could not be performed. Otherwise this buffer will contain // the pixel data when the transfer is complete. @@ -393,7 +357,6 @@ private: std::unique_ptr<ResourceProvider> fResourceProvider; std::unique_ptr<QueueManager> fQueueManager; std::unique_ptr<ClientMappedBufferManager> fMappedBufferManager; - std::unique_ptr<const skcpu::ContextImpl> fCPUContext; // In debug builds we guard against improper thread handling. This guard is passed to the // ResourceCache for the Context. diff --git a/gfx/skia/skia/include/gpu/graphite/ContextOptions.h b/gfx/skia/skia/include/gpu/graphite/ContextOptions.h @@ -9,7 +9,6 @@ #define skgpu_graphite_ContextOptions_DEFINED #include "include/core/SkRefCnt.h" -#include "include/core/SkSize.h" #include "include/core/SkSpan.h" #include "include/private/base/SkAPI.h" #include "include/private/base/SkMath.h" @@ -17,7 +16,6 @@ #include <optional> class SkData; -class SkExecutor; class SkRuntimeEffect; namespace skgpu { class ShaderErrorHandler; } @@ -47,24 +45,7 @@ struct SK_API ContextOptions { * * If <= 1, Graphite will disable internal code paths that use multisampling. */ - uint8_t fInternalMultisampleCount = 4; - - /** - * If set, this specifies the max width/height of MSAA textures that Graphite should use for - * internal draws. Graphite might have to break the drawing region into multiple tiles to - * satisfy the size constraint. - * Note: this option will be ignored if the backend doesn't support it, or if a more optimal HW - * feature is available. - */ - std::optional<SkISize> fInternalMSAATileSize = std::nullopt; - - /** - * If set, paths that are smaller than this size (in device space) will avoid MSAA techniques, - * even if MSAA is otherwise enabled via `fInternalMultisampleCount`. This should be smaller - * than `fGlyphsAsPathsFontSize` or large glyphs will not correctly avoid higher memory - * overhead. - */ - float fMinimumPathSizeForMSAA = 0; + int fInternalMultisampleCount = 4; /** * Will the client make sure to only ever be executing one thread that uses the Context and all @@ -108,18 +89,10 @@ struct SK_API ContextOptions { bool fSupportBilerpFromGlyphAtlas = false; /** - * For the moment, if Recordings from the same Recorder 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 + * 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. - * - * This is the default ordering requirement for a Recorder. It can be overridden by - * setting the same field on the RecorderOptions passed to makeRecorder. - * - * Regardless of this value or a per-Recorder's setting, Recordings from separate Recorders can - * always be inserted in any order but it is the application's responsible to ensure that any - * implicit dependencies between the Recorders are respected (e.g. rendering to an SkSurface - * in one Recorder and sampling from that SkSurface's SkImage view on another Recorder). */ bool fRequireOrderedRecordings = false; @@ -181,23 +154,6 @@ struct SK_API ContextOptions { SkSpan<sk_sp<SkRuntimeEffect>> fUserDefinedKnownRuntimeEffects; /** - * Executor to handle threaded work within Graphite. 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 Pipeline compilation, but may be used - * for other tasks. It is up to the client to ensure the SkExecutor remains valid throughout - * the lifetime of the Context. - */ - SkExecutor* fExecutor = nullptr; - - /** - * An experimental flag in development. Behavior and performance is subject to change. - * - * Enables the use of startCapture and endCapture functions. Calling these APIs will capture all - * draw calls and surface creation from Recorders spawned from the Context. - */ - bool fEnableCapture = false; - - /** * Private options that are only meant for testing within Skia's tools. */ ContextOptionsPriv* fOptionsPriv = nullptr; diff --git a/gfx/skia/skia/include/gpu/graphite/GraphiteTypes.h b/gfx/skia/skia/include/gpu/graphite/GraphiteTypes.h @@ -34,47 +34,6 @@ using GpuFinishedWithStatsProc = void (*)(GpuFinishedContext finishedContext, CallbackResult, const GpuStats&); -// NOTE: This can be converted to just an `enum class InsertStatus {}` once clients are migrated -// off of assuming `Context::insertRecording()` returns a boolean. -class InsertStatus { -public: - // Do not refer to V directly; use these constants as if InsertStatus were a class enum, e.g. - // InsertStatus::kSuccess. - enum V { - // Everything successfully added to underlying CommandBuffer - kSuccess, - // Recording or InsertRecordingInfo invalid, no CB changes - kInvalidRecording, - // Promise image instantiation failed, no CB changes - kPromiseImageInstantiationFailed, - // Internal failure, CB partially modified, state unrecoverable or unknown (e.g. dependent - // texture uploads for future Recordings may or may not get executed) - kAddCommandsFailed, - // Internal failure, shader pipeline compilation failed (driver issue, or disk corruption), - // state unrecoverable. - kAsyncShaderCompilesFailed - }; - - constexpr InsertStatus() : fValue(kSuccess) {} - /*implicit*/ constexpr InsertStatus(V v) : fValue(v) {} - - operator InsertStatus::V() const { - return fValue; - } - - // Assist migration from old bool return value of insertRecording; kSuccess is true, - // all other error statuses are false. - // NOTE: This is intentionally not explicit so that InsertStatus can be assigned correctly to - // a bool or returned as a bool, since these are not boolean contexts that automatically apply - // explicit bool operators (e.g. inside an if condition). - operator bool() const { - return fValue == kSuccess; - } - -private: - V fValue; -}; - /** * 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 @@ -129,17 +88,6 @@ struct InsertRecordingInfo { GpuFinishedContext fFinishedContext = nullptr; GpuFinishedProc fFinishedProc = nullptr; GpuFinishedWithStatsProc fFinishedWithStatsProc = nullptr; - - // For unit testing purposes, this can be used to induce a known failure status from - // Context::insertRecording(). When this set to anything other than kSuccess, insertRecording() - // will operate as normal until the first condition that would normally return the simulated - // status is encountered. At that point, operations are treated as if that condition had failed. - // This leaves the Context in a state consistent with encountering the InsertStatus in a normal - // application. - // - // NOTE: If the simulated failure status is one of the later error codes but the inserted - // Recording would fail with an earlier error code normally, that error is still returned. - InsertStatus fSimulatedStatus = InsertStatus::kSuccess; }; /** @@ -170,29 +118,6 @@ enum class SyncToCpu : bool { kNo = false }; -enum class MarkFrameBoundary : bool { - kYes = true, - kNo = false -}; - -struct SubmitInfo { - SyncToCpu fSync = SyncToCpu::kNo; - MarkFrameBoundary fMarkBoundary = MarkFrameBoundary::kNo; - uint64_t fFrameID = 0; - - constexpr SubmitInfo() = default; - - constexpr SubmitInfo(SyncToCpu sync) - : fSync(sync) - , fMarkBoundary(MarkFrameBoundary::kNo) - , fFrameID(0) {} - - constexpr SubmitInfo(SyncToCpu sync, uint64_t frameID) - : fSync(sync) - , fMarkBoundary(MarkFrameBoundary::kYes) - , fFrameID(frameID) {} -}; - /* * For Promise Images - should the Promise Image be fulfilled every time a Recording that references * it is inserted into the Context. @@ -243,11 +168,7 @@ enum DrawTypeFlags : uint16_t { // AnalyticRRectRenderStep // PerEdgeAAQuadRenderStep // CoverBoundsRenderStep[NonAAFill] - kAnalyticRRect = 1 << 7, - kPerEdgeAAQuad = 1 << 8, - kNonAAFillRect = 1 << 9, - - kSimpleShape = kAnalyticRRect | kPerEdgeAAQuad | kNonAAFillRect, + kSimpleShape = 1 << 7, // kNonSimpleShape should be used to generate Pipelines that use the following RenderSteps: // CoverageMaskRenderStep @@ -256,22 +177,9 @@ enum DrawTypeFlags : uint16_t { // TessellateWedgesRenderStep[*] for [Convex], [EvenOdd], [Winding] // TessellateCurvesRenderStep[*] for [EvenOdd], [Winding] // MiddleOutFanRenderStep[*] for [EvenOdd], [Winding] - kNonSimpleShape = 1 << 10, - - // This draw type covers all the methods Skia uses to draw drop shadows. It can be used to - // generate Pipelines which, as part of their labels, have: - // the AnalyticBlurRenderStep - // VerticesRenderStep[TrisColor] with a GaussianColorFilter - // For this draw type the PaintOptions parameter to Precompile() will be ignored. - kDropShadows = 1 << 11, - - // kAnalyticClip should be combined with the primary drawType for Pipelines that contain - // either of the following sub-strings: - // AnalyticClip - // AnalyticAndAtlasClip - kAnalyticClip = 1 << 12, + kNonSimpleShape = 1 << 8, - kLast = kAnalyticClip, + kLast = kNonSimpleShape, }; } // namespace skgpu::graphite diff --git a/gfx/skia/skia/include/gpu/graphite/PrecompileContext.h b/gfx/skia/skia/include/gpu/graphite/PrecompileContext.h @@ -10,11 +10,9 @@ #include "include/core/SkRefCnt.h" #include "include/private/base/SingleOwner.h" -#include "include/private/base/SkAPI.h" #include <chrono> #include <memory> -#include <string> class SkData; @@ -36,22 +34,10 @@ public: */ void purgePipelinesNotUsedInMs(std::chrono::milliseconds msNotUsed); - enum class StatOptions { - // Emit histograms (using the SK_HISTOGRAM* macros) for Skia's Precompiled Pipeline - // usage: - // Skia.Graphite.Precompile.NormalPreemptedByPrecompile - // Skia.Graphite.Precompile.UnpreemptedPrecompilePipelines - // Skia.Graphite.Precompile.UnusedPrecompiledPipelines - kPrecompile, - // Emit histograms (using the SK_HISTOGRAM* macros) for Skia's Pipeline cache usage: - // Skia.Graphite.PipelineCache.PipelineUsesInEpoch - kPipelineCache, - }; - /** - * Emit histograms histograms related to Skia's Pipelines (c.f. the StatOptions enum). + * Emit histograms (using the SK_HISTOGRAM* macros) for Skia's Pipeline usage. */ - void reportPipelineStats(StatOptions option = StatOptions::kPrecompile); + void reportPipelineStats(); /** * Precompile one specific Pipeline that has been previously serialized. Serialized pipeline diff --git a/gfx/skia/skia/include/gpu/graphite/Recorder.h b/gfx/skia/skia/include/gpu/graphite/Recorder.h @@ -8,34 +8,22 @@ #ifndef skgpu_graphite_Recorder_DEFINED #define skgpu_graphite_Recorder_DEFINED -#include "include/core/SkCPURecorder.h" -#include "include/core/SkRecorder.h" #include "include/core/SkRefCnt.h" +#include "include/core/SkSize.h" #include "include/gpu/graphite/GraphiteTypes.h" #include "include/gpu/graphite/Recording.h" #include "include/private/base/SingleOwner.h" -#include "include/private/base/SkAPI.h" #include "include/private/base/SkTArray.h" -#include "include/private/base/SkTDArray.h" #include <chrono> -#include <cstddef> -#include <cstdint> -#include <memory> -#include <optional> +struct AHardwareBuffer; class SkCanvas; +struct SkImageInfo; class SkPixmap; class SkTraceMemoryDump; -struct SkISize; -struct SkImageInfo; - -#if defined(SK_BUILD_FOR_ANDROID) -struct AHardwareBuffer; -#endif namespace skgpu { -enum class BackendApi : unsigned int; class RefCntedCallback; class TokenTracker; } @@ -49,22 +37,28 @@ namespace skgpu::graphite { class AtlasProvider; class BackendTexture; +class Caps; class Context; class Device; class DrawBufferManager; -class FloatStorageManager; +class GlobalCache; class ImageProvider; +class ProxyCache; class ProxyReadCountMap; class RecorderPriv; class ResourceProvider; class RuntimeEffectDictionary; class SharedContext; +class Task; class TaskList; +class TextureDataBlock; class TextureInfo; +class UniformDataBlock; class UploadBufferManager; class UploadList; -struct RecorderOptionsPriv; +template<typename T> class PipelineDataCache; +using TextureDataCache = PipelineDataCache<TextureDataBlock>; struct SK_API RecorderOptions final { RecorderOptions(); @@ -76,29 +70,19 @@ struct SK_API RecorderOptions final { static constexpr size_t kDefaultRecorderBudget = 256 * (1 << 20); // What is the budget for GPU resources allocated and held by this Recorder. size_t fGpuBudgetInBytes = kDefaultRecorderBudget; - // If Recordings are known to be played back in the order they are recorded, then Graphite - // may be able to make certain assumptions that improve performance. This is often the case - // if the content being drawn triggers the use of internal atlasing in Graphite (e.g. text). - std::optional<bool> fRequireOrderedRecordings; - - // Private options that are only meant for testing within Skia's tools. - RecorderOptionsPriv* fRecorderOptionsPriv = nullptr; }; -class SK_API Recorder final : public SkRecorder { +class SK_API Recorder final { public: Recorder(const Recorder&) = delete; Recorder(Recorder&&) = delete; Recorder& operator=(const Recorder&) = delete; Recorder& operator=(Recorder&&) = delete; - ~Recorder() override; + ~Recorder(); BackendApi backend() const; - Type type() const override { return SkRecorder::Type::kGraphite; } - skcpu::Recorder* cpuRecorder() override; - std::unique_ptr<Recording> snap(); ImageProvider* clientImageProvider() { return fClientImageProvider.get(); } @@ -273,22 +257,19 @@ private: void registerDevice(sk_sp<Device>); void deregisterDevice(const Device*); - SkCanvas* makeCaptureCanvas(SkCanvas*) override; - sk_sp<SharedContext> fSharedContext; ResourceProvider* fResourceProvider; // May point to the Context's resource provider std::unique_ptr<ResourceProvider> fOwnedResourceProvider; // May be null - - sk_sp<RuntimeEffectDictionary> fRuntimeEffectDict; + std::unique_ptr<RuntimeEffectDictionary> fRuntimeEffectDict; // NOTE: These are stored by pointer to allow them to be forward declared. std::unique_ptr<TaskList> fRootTaskList; // Aggregated one-time uploads that preceed all tasks in the root task list. std::unique_ptr<UploadList> fRootUploads; + std::unique_ptr<TextureDataCache> fTextureDataCache; std::unique_ptr<DrawBufferManager> fDrawBufferManager; std::unique_ptr<UploadBufferManager> fUploadBufferManager; - sk_sp<FloatStorageManager> fFloatStorageManager; std::unique_ptr<ProxyReadCountMap> fProxyReadCounts; // Iterating over tracked devices in flushTrackedDevices() needs to be re-entrant and support @@ -300,8 +281,6 @@ private: uint32_t fUniqueID; // Needed for MessageBox handling for text uint32_t fNextRecordingID = 1; - const bool fRequireOrderedRecordings; - std::unique_ptr<AtlasProvider> fAtlasProvider; std::unique_ptr<TokenTracker> fTokenTracker; std::unique_ptr<sktext::gpu::StrikeCache> fStrikeCache; @@ -323,14 +302,6 @@ private: // For testing use only -- the Context used to create this Recorder Context* fContext = nullptr; #endif - -#if defined(SK_DUMP_TASKS) - // Traverses and dumps the task list at Recorder::snap() - void dumpTasks(TaskList*) const; - - // Log of all callers of RecorderPriv::flushTrackedDevices - SkTDArray<const char*> fFlushSources; -#endif }; } // namespace skgpu::graphite diff --git a/gfx/skia/skia/include/gpu/graphite/Recording.h b/gfx/skia/skia/include/gpu/graphite/Recording.h @@ -9,16 +9,13 @@ #define skgpu_graphite_Recording_DEFINED #include "include/core/SkRefCnt.h" -#include "include/private/base/SkAPI.h" +#include "include/core/SkSize.h" #include "include/private/base/SkTArray.h" -#include <cstddef> -#include <cstdint> + #include <memory> #include <unordered_set> #include <vector> -struct SkISize; - namespace skgpu { class RefCntedCallback; } @@ -50,7 +47,6 @@ private: class LazyProxyData { public: LazyProxyData(const Caps*, SkISize dimensions, const TextureInfo&); - ~LazyProxyData(); TextureProxy* lazyProxy(); sk_sp<TextureProxy> refLazyProxy(); @@ -68,12 +64,15 @@ private: Recording(uint32_t uniqueID, uint32_t recorderID, + std::unordered_set<sk_sp<TextureProxy>, ProxyHash>&& nonVolatileLazyProxies, + std::unordered_set<sk_sp<TextureProxy>, ProxyHash>&& volatileLazyProxies, std::unique_ptr<LazyProxyData> targetProxyData, skia_private::TArray<sk_sp<RefCntedCallback>>&& finishedProcs); + bool addCommands(CommandBuffer*, ResourceProvider*); void addResourceRef(sk_sp<Resource>); - // Used to verify ordering if recorder ID is not SK_InvalidGenID + // Used to verify ordering uint32_t fUniqueID; uint32_t fRecorderID; diff --git a/gfx/skia/skia/include/gpu/graphite/TextureInfo.h b/gfx/skia/skia/include/gpu/graphite/TextureInfo.h @@ -49,7 +49,7 @@ private: public: virtual ~Data() = default; - Data(uint8_t sampleCount, skgpu::Mipmapped mipmapped) + Data(uint32_t sampleCount, skgpu::Mipmapped mipmapped) : fSampleCount(sampleCount) , fMipmapped(mipmapped) {} @@ -59,7 +59,7 @@ private: Data& operator=(const Data&) = default; // NOTE: These fields are accessible via the backend-specific subclasses. - uint8_t fSampleCount = 1; + uint32_t fSampleCount = 1; Mipmapped fMipmapped = Mipmapped::kNo; private: @@ -92,7 +92,7 @@ public: return fBackend; } - uint8_t numSamples() const { return fData.has_value() ? fData->fSampleCount : 1; } + 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; } diff --git a/gfx/skia/skia/include/gpu/graphite/mtl/MtlBackendContext.h b/gfx/skia/skia/include/gpu/graphite/mtl/MtlBackendContext.h @@ -8,12 +8,10 @@ #ifndef skgpu_graphite_MtlBackendContext_DEFINED #define skgpu_graphite_MtlBackendContext_DEFINED -#include "include/gpu/graphite/Context.h" #include "include/ports/SkCFObject.h" #include "include/private/base/SkAPI.h" #import <CoreFoundation/CoreFoundation.h> -#include <memory> namespace skgpu::graphite { diff --git a/gfx/skia/skia/include/gpu/graphite/precompile/PaintOptions.h b/gfx/skia/skia/include/gpu/graphite/precompile/PaintOptions.h @@ -27,11 +27,11 @@ class PrecompileShader; enum class Coverage; enum DrawTypeFlags : uint16_t; enum class PrecompileImageFilterFlags : uint32_t; -enum class TextureFormat : uint8_t; class KeyContext; class PaintOptionsPriv; class PaintParamsKeyBuilder; +class PipelineDataGatherer; struct RenderPassDesc; class UniquePaintParamsID; @@ -118,9 +118,6 @@ public: SkSpan<const SkBlendMode> getBlendModes() const { return SkSpan<const SkBlendMode>(fBlendModeOptions.data(), fBlendModeOptions.size()); } - void addBlendMode(SkBlendMode bm) { - fBlendModeOptions.push_back(bm); - } /** Sets the blender options used when generating precompilation combinations. @@ -142,11 +139,6 @@ public: void setDither(bool dither) { fDither = dither; } bool isDither() const { return fDither; } - void setPaintColorIsOpaque(bool paintColorIsOpaque) { - fPaintColorIsOpaque = paintColorIsOpaque; - } - bool isPaintColorOpaque() const { return fPaintColorIsOpaque; } - // Provides access to functions that aren't part of the public API. PaintOptionsPriv priv(); const PaintOptionsPriv priv() const; // NOLINT(readability-const-return-type) @@ -157,13 +149,12 @@ private: friend class PrecompileMaskFilter; // for ProcessCombination access void addColorFilter(sk_sp<PrecompileColorFilter> cf); + void addBlendMode(SkBlendMode bm) { + fBlendModeOptions.push_back(bm); + } void setClipShaders(SkSpan<const sk_sp<PrecompileShader>> clipShaders); - // In the main API this is specified via the SkBlender parameter to drawVertices - void setPrimitiveBlendMode(SkBlendMode bm) { fPrimitiveBlendMode = bm; } - void setSkipColorXform(bool skipColorXform) { fSkipColorXform = skipColorXform; } - int numShaderCombinations() const; int numColorFilterCombinations() const; int numBlendCombinations() const; @@ -172,10 +163,10 @@ private: int numCombinations() const; // 'desiredCombination' must be less than the result of the numCombinations call void createKey(const KeyContext&, - TextureFormat, + PaintParamsKeyBuilder*, + PipelineDataGatherer*, int desiredCombination, bool addPrimitiveBlender, - bool addAnalyticClip, Coverage coverage) const; typedef std::function<void(UniquePaintParamsID id, @@ -185,6 +176,7 @@ private: const RenderPassDesc&)> ProcessCombination; void buildCombinations(const KeyContext&, + PipelineDataGatherer*, DrawTypeFlags, bool addPrimitiveBlender, Coverage, @@ -200,10 +192,7 @@ private: skia_private::TArray<sk_sp<PrecompileImageFilter>> fImageFilterOptions; skia_private::TArray<sk_sp<PrecompileMaskFilter>> fMaskFilterOptions; - SkBlendMode fPrimitiveBlendMode = SkBlendMode::kSrcOver; - bool fSkipColorXform = false; bool fDither = false; - bool fPaintColorIsOpaque = true; }; } // namespace skgpu::graphite diff --git a/gfx/skia/skia/include/gpu/graphite/precompile/PrecompileBase.h b/gfx/skia/skia/include/gpu/graphite/precompile/PrecompileBase.h @@ -15,6 +15,7 @@ namespace skgpu::graphite { class KeyContext; class PaintParamsKeyBuilder; +class PipelineDataGatherer; class PrecompileBasePriv; /** \class PrecompileBase @@ -50,7 +51,10 @@ protected: return this->numIntrinsicCombinations() * this->numChildCombinations(); } - virtual void addToKey(const KeyContext&, int desiredCombination) const = 0; + virtual void addToKey(const KeyContext&, + PaintParamsKeyBuilder*, + PipelineDataGatherer*, + int desiredCombination) const = 0; // This returns the desired option along with the child options. template<typename T> @@ -60,7 +64,11 @@ protected: // In general, derived classes should use AddToKey to select the desired child option from // a span and then have it added to the key with its reduced/nested child option. template<typename T> - static void AddToKey(const KeyContext&, SkSpan<const sk_sp<T>> options, int desiredOption); + static void AddToKey(const KeyContext&, + PaintParamsKeyBuilder*, + PipelineDataGatherer*, + SkSpan<const sk_sp<T>> options, + int desiredOption); private: friend class PrecompileBasePriv; diff --git a/gfx/skia/skia/include/gpu/graphite/precompile/PrecompileImageFilter.h b/gfx/skia/skia/include/gpu/graphite/precompile/PrecompileImageFilter.h @@ -52,15 +52,20 @@ private: // The PrecompileImageFilter classes do not use the PrecompileBase::addToKey virtual since // they, in general, do not themselves contribute to a given SkPaint/Pipeline but, rather, // create separate SkPaints/Pipelines from whole cloth (in onCreatePipelines). - void addToKey(const KeyContext& /* keyContext */, int /* desiredCombination */) const final { + void addToKey(const KeyContext& /* keyContext */, + PaintParamsKeyBuilder* /* builder */, + PipelineDataGatherer* /* gatherer */, + int /* desiredCombination */) const final { SkASSERT(false); } virtual void onCreatePipelines(const KeyContext&, + PipelineDataGatherer*, const RenderPassDesc&, const PaintOptions::ProcessCombination&) const = 0; void createPipelines(const KeyContext&, + PipelineDataGatherer*, const RenderPassDesc&, const PaintOptions::ProcessCombination&); diff --git a/gfx/skia/skia/include/gpu/graphite/precompile/PrecompileMaskFilter.h b/gfx/skia/skia/include/gpu/graphite/precompile/PrecompileMaskFilter.h @@ -24,9 +24,13 @@ protected: PrecompileMaskFilter() : PrecompileBase(Type::kMaskFilter) {} ~PrecompileMaskFilter() override; - void addToKey(const KeyContext&, int desiredCombination) const final; + void addToKey(const KeyContext&, + PaintParamsKeyBuilder*, + PipelineDataGatherer*, + int desiredCombination) const final; virtual void createPipelines(const KeyContext&, + PipelineDataGatherer*, const PaintOptions&, const RenderPassDesc&, const PaintOptions::ProcessCombination&) const = 0; diff --git a/gfx/skia/skia/include/gpu/graphite/precompile/PrecompileShader.h b/gfx/skia/skia/include/gpu/graphite/precompile/PrecompileShader.h @@ -72,8 +72,7 @@ public: * sk_sp<PrecompileShader> combinedOptions = WorkingColorSpace({ source1, source2 }, * { colorSpace }); */ - sk_sp<PrecompileShader> makeWithWorkingColorSpace(sk_sp<SkColorSpace> inputCS, - sk_sp<SkColorSpace> outputCS=nullptr) const; + sk_sp<PrecompileShader> makeWithWorkingColorSpace(sk_sp<SkColorSpace>) const; // Provides access to functions that aren't part of the public API. PrecompileShaderPriv priv(); @@ -107,113 +106,37 @@ namespace PrecompileShaders { SkSpan<const sk_sp<PrecompileShader>> srcs); SK_API sk_sp<PrecompileShader> CoordClamp(SkSpan<const sk_sp<PrecompileShader>>); - enum class ImageShaderFlags : uint16_t { - kNone = 0, - - kCubicSampling = 1 << 1, - kIncludeAlphaOnly = 1 << 2, - - kAll = kCubicSampling | kIncludeAlphaOnly, - kExcludeCubic = kIncludeAlphaOnly, - kNoAlphaNoCubic = kNone, - }; - - static constexpr SkTileMode kAllTileModes[] = { - SkTileMode::kClamp, - SkTileMode::kRepeat, - SkTileMode::kMirror, - SkTileMode::kDecal, - }; - - /** - In the main Skia API ImageShaders are usually created via a SkImage::makeShader call. - Since the SkImage used to create the ImageShader is unlikely to be present at precompilation - 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. - - @param shaderFlags Specify the sampling variations that will be precompiled. - @param colorInfos a span of SkColorInfos to narrow down the combinations. - In general, the color info affects the swizzle and colorSpace - transformation of the final Pipeline. - @param tileModes a span of SkTileModes to narrow down the combinations. - The default will generate all possible combinations. Passing an - empty SkSpan will eliminate the Shader-based sampling combinations. - @return A precompile shader capturing the specified combinations - */ - SK_API sk_sp<PrecompileShader> Image(ImageShaderFlags = ImageShaderFlags::kAll, - SkSpan<const SkColorInfo> = {}, - SkSpan<const SkTileMode> = { kAllTileModes }); - - /** DEPRECATED - * Use above version. - */ - SK_API sk_sp<PrecompileShader> Image(SkSpan<const SkColorInfo> colorInfos, - SkSpan<const SkTileMode> = { kAllTileModes }); - + // In the main Skia API ImageShaders are usually created via a SkImage::makeShader call. + // Since the SkImage used to create the ImageShader is unlikely to be present at precompilation + // time this entry point allows the equivalent precompilation program structure to be created. + // Note that this factory is for non-YUV SkImages, the YUVImage factory (below) should be used + // to represent the shading and sampling required for YUV images. + SK_API sk_sp<PrecompileShader> Image(SkSpan<const SkColorInfo> = {}, + SkSpan<const SkTileMode> = {}); // As with the above Image call, raw ImageShaders are usually created via an // SkImage::makeRawShader call. The RawImage call allows the equivalent precompilation // program structure to be created without needing the SkImage. - SK_API sk_sp<PrecompileShader> RawImage(ImageShaderFlags = ImageShaderFlags::kExcludeCubic, - SkSpan<const SkColorInfo> = {}, - SkSpan<const SkTileMode> = { kAllTileModes }); - - enum class YUVImageShaderFlags : uint16_t { - kNone = 0, - - kHardwareSamplingNoSwizzle = 1 << 1, - kHardwareSampling = 1 << 2, - kShaderBasedSampling = 1 << 3, - kCubicSampling = 1 << 4, + SK_API sk_sp<PrecompileShader> RawImage(SkSpan<const SkColorInfo> = {}, + SkSpan<const SkTileMode> = {}); - kExcludeCubic = kHardwareSamplingNoSwizzle | kHardwareSampling | kShaderBasedSampling, - kNoCubicNoNonSwizzledHW = kHardwareSamplingNoSwizzle | kShaderBasedSampling, - }; - - /** - 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 - to specify that the future image will be a YUV image. - - @param shaderFlags Specify the sampling variations that will be precompiled. - In practice, YUV images are rarely cubic-sampling. - @param colorInfos a span of SkColorInfos to narrow down the combinations. - In general, the color info affects the swizzle and colorSpace - transformation of the final Pipeline. - @return A precompile shader capturing the specified combinations - */ - SK_API sk_sp<PrecompileShader> YUVImage( - YUVImageShaderFlags = YUVImageShaderFlags::kExcludeCubic, - SkSpan<const SkColorInfo> = {}); + // 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 + // to specify that the future image will be a YUV image. + SK_API sk_sp<PrecompileShader> YUVImage(); // --- This block of two matches the SkShaders factories in SkPerlinNoiseShader.h // Again, most of the details have been elided. SK_API sk_sp<PrecompileShader> MakeFractalNoise(); SK_API sk_sp<PrecompileShader> MakeTurbulence(); - enum class GradientShaderFlags : uint16_t { - kNone = 0, - - kSmall = 1 << 1, - kMedium = 1 << 2, - kLarge = 1 << 3, - - kAll = kSmall | kMedium | kLarge, - kNoLarge = kSmall | kMedium, - }; - // --- This block of four matches all the factories in SkGradientShader (SkGradientShader.h) SK_API sk_sp<PrecompileShader> LinearGradient( - GradientShaderFlags = GradientShaderFlags::kAll, SkGradientShader::Interpolation = SkGradientShader::Interpolation()); SK_API sk_sp<PrecompileShader> RadialGradient( - GradientShaderFlags = GradientShaderFlags::kAll, SkGradientShader::Interpolation = SkGradientShader::Interpolation()); SK_API sk_sp<PrecompileShader> TwoPointConicalGradient( - GradientShaderFlags = GradientShaderFlags::kAll, SkGradientShader::Interpolation = SkGradientShader::Interpolation()); SK_API sk_sp<PrecompileShader> SweepGradient( - GradientShaderFlags = GradientShaderFlags::kAll, SkGradientShader::Interpolation = SkGradientShader::Interpolation()); // Normally, SkPicture shaders are only created via SkPicture::makeShader. Since the @@ -245,20 +168,8 @@ namespace PrecompileShaders { // WorkingColorSpaceShaders (i.e., pass SkSpans to the factory function vs just creating a // single option). This entry point allows that use case. // Note: PrecompileShader::makeWithWorkingColorSpace can still be used and works as expected. - - // This variant creates the cross product of `inputSpaces` and `outputSpaces`. - // An empty list is interpreted as matching either the dst color space (for `inputSpaces`) or - // matching the input space (for `outputSpaces`). - SK_API sk_sp<PrecompileShader> WorkingColorSpace( - SkSpan<const sk_sp<PrecompileShader>> shaders, - SkSpan<const sk_sp<SkColorSpace>> inputSpaces, - SkSpan<const sk_sp<SkColorSpace>> outputSpaces = {}); - - // This variant takes a span of input and output color spaces pairs explicitly. - SK_API sk_sp<PrecompileShader> WorkingColorSpaceExplicit( - SkSpan<const sk_sp<PrecompileShader>> shaders, - SkSpan<const std::pair</*input =*/sk_sp<SkColorSpace>, - /*output=*/sk_sp<SkColorSpace>>> inputAndOutputSpaces); + SK_API sk_sp<PrecompileShader> WorkingColorSpace(SkSpan<const sk_sp<PrecompileShader>> shaders, + SkSpan<const sk_sp<SkColorSpace>> colorSpaces); } // namespace PrecompileShaders diff --git a/gfx/skia/skia/include/gpu/graphite/vk/precompile/VulkanPrecompileShader.h b/gfx/skia/skia/include/gpu/graphite/vk/precompile/VulkanPrecompileShader.h @@ -1,39 +0,0 @@ -/* - * 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_vk_precompile_PrecompileShader_DEFINED -#define skgpu_graphite_vk_precompile_PrecompileShader_DEFINED - -#include "include/gpu/graphite/precompile/PrecompileShader.h" - -namespace skgpu { - struct VulkanYcbcrConversionInfo; -} - -namespace skgpu::graphite { - -namespace PrecompileShaders { - -/** - In the main Skia API YCbCr Images are usually created by wrapping a BackendTexture. - Such backend textures are, in turn, created by a backend-specific entry point like: - BackendTextures::MakeVulkan(SkISize, const VulkanTextureInfo&, ...) - where the VulkanTextureInfo contains additional YCbCr information. This API cuts right to - providing the backend-specific YCbCr information (i.e., the VulkanYcbcrConversionInfo). - - @return A precompile shader for a specific type of YCbCr image -*/ -SK_API sk_sp<PrecompileShader> VulkanYCbCrImage(skgpu::VulkanYcbcrConversionInfo& YCbCrInfo, - ImageShaderFlags = ImageShaderFlags::kAll, - SkSpan<const SkColorInfo> = {}, - SkSpan<const SkTileMode> = { kAllTileModes }); - -} // namespace PrecompileShaders - -} // namespace skgpu::graphite - -#endif // skgpu_graphite_vk_precompile_PrecompileShader_DEFINED diff --git a/gfx/skia/skia/include/gpu/vk/VulkanBackendContext.h b/gfx/skia/skia/include/gpu/vk/VulkanBackendContext.h @@ -29,7 +29,7 @@ struct SK_API VulkanBackendContext { VkQueue fQueue = VK_NULL_HANDLE; uint32_t fGraphicsQueueIndex = 0; // The max api version set here should match the value set in VkApplicationInfo::apiVersion when - // then VkInstance was created. Skia requires Vulkan 1.1 as the minimum version. + // then VkInstance was created. uint32_t fMaxAPIVersion = 0; const skgpu::VulkanExtensions* fVkExtensions = nullptr; // The client can create their VkDevice with either a VkPhysicalDeviceFeatures or diff --git a/gfx/skia/skia/include/gpu/vk/VulkanPreferredFeatures.h b/gfx/skia/skia/include/gpu/vk/VulkanPreferredFeatures.h @@ -1,217 +0,0 @@ -/* - * 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_VulkanPreferredFeatures_DEFINED -#define skgpu_VulkanPreferredFeatures_DEFINED - -#include "include/private/base/SkAPI.h" -#include "include/private/gpu/vk/SkiaVulkan.h" - -#include <cstddef> -#include <cstdint> -#include <vector> - -namespace skgpu { - -class SK_API VulkanPreferredFeatures { -public: - ~VulkanPreferredFeatures(); - - /* - * VulkanPreferredFeatures is used by Skia to add extensions and features on top of what the app - * wants to enable. The flow of Vulkan initialization for an app that wants to use Skia would - * be: - * - * ``` - * // Query the loader for instance information, decide on API version to use - * - * // Prepare for Skia-enabled extensions and features - * skgpu::VulkanPreferredFeatures skiaFeatures; - * skiaFeatures.init(apiVersion); - * - * // Decide on instance extensions to use in the app. - * - * // Allow Skia to add to the instance extension list. - * skiaFeatures.addToInstanceExtensions(...); - * - * // Create the instance, choose physical device, query available extensions, decide on - * // device features to query - * - * // Allow Skia to add to the feature query list. - * skiaFeatures.addFeaturesToQuery(...); - * - * // Query features, decide on extensions and features to enable. - * - * // Allow Skia to add to the device extension and feature list. - * skiaFeatures.addFeaturesToEnable(...); - * - * // Create the Vulkan device. - * ``` - * - * Parameters: - * - * * appAPIVersion: The API version the app has specified in VkApplicationInfo::apiVersion (and - * will later provide in VulkanBackendContext::fMaxAPIVersion). The minimum supported version - * is VK_API_VERSION_1_1, maximum is VK_API_VERSION_1_4. - */ - void init(uint32_t appAPIVersion); - - /* - * Before creating a Vulkan instance, call addToInstanceExtensions to give Skia a chance to add - * instance extensions it may take advantage of. Extensions are only added to appExtensions if - * caller hasn't already included them to be enabled. - * - * Parameters: - * - * * instanceExtensions, instanceExtensionCount: The list of available instance extensions as - * queried from the loader. - * * appExtensions: The list of extensions to be enabled on the instance; more extensions may be - * added to this list by this call. - */ - void addToInstanceExtensions(const VkExtensionProperties* instanceExtensions, - size_t instanceExtensionCount, - std::vector<const char*>& appExtensions); - - /* - * Before querying Vulkan device features, call addFeaturesToQuery to give Skia a chance to add - * device extension features it may take advantage of to the query. Features are only added to - * appFeatures if caller hasn't already included them in the query. - * - * Beware that the structs that get chained to appFeatures are member variables of this class, - * so this object must not leave scope until feature query and device creation is complete. - * - * Parameters: - * - * * deviceExtensions, deviceExtensionCount: The list of available device extensions as queried - * from the physical device. - * * appFeatures: The features to be queried from the physical device; more features may be - * added to the pNext chain by this call. - */ - void addFeaturesToQuery(const VkExtensionProperties* deviceExtensions, - size_t deviceExtensionCount, - VkPhysicalDeviceFeatures2& appFeatures); - - /* - * Before creating the Vulkan device, call addFeaturesToEnable to give Skia a chance to add - * device extensions and features it may take advantage of. Extensions and features are only - * added to appExtensions and appFeatures respectively if caller hasn't already included them to - * be enabled. This function may replace chained features with VkPhysicalDeviceVulkanNNFeatures - * structs instead in order to add features. Features that are already enabled by the - * application are retained in that case in the new struct. - * - * Parameters: - * - * * appExtensions: The list of extensions to be enabled on the device; more extensions may be - * added to this list by this call. - * * appFeatures: The features to be enabled on the device; more features may be - * added to the pNext chain by this call. - */ - void addFeaturesToEnable(std::vector<const char*>& appExtensions, - VkPhysicalDeviceFeatures2& appFeatures); - -private: - uint32_t fAPIVersion = 0; - // Track what the application did, so warnings can be generated if the class is not - // fully/correctly used. - bool fHasAddedToInstanceExtensions = false; - bool fHasAddedFeaturesToQuery = false; - bool fHasAddedFeaturesToEnable = false; - - // The list of device features Skia is interested in. If any are included in - // VkPhysicalDeviceFeatures2::pNext by the app, it is not included again by Skia. However, to - // take best advantage of the Vulkan API, the app should not intentionally include these feature - // structs only to forcefully disable the feature - this blocks Skia from leveraging it. - // - // When ambiguous, the extension name is included to determine which extension a feature struct - // has come from. This is usually needed when addFeaturesToQuery has included a feature struct - // that is present in multiple extensions (e.g. due to extension promotion). It is later used by - // addFeaturesToEnable to know which extension to enable. - - // Available since Vulkan 1.2 - VkPhysicalDeviceVulkan11Features fVulkan11 = {}; - - // Available since Vulkan 1.2 - VkPhysicalDeviceVulkan12Features fVulkan12 = {}; - - // Available since Vulkan 1.3 - VkPhysicalDeviceVulkan13Features fVulkan13 = {}; - - // Available since Vulkan 1.4 - VkPhysicalDeviceVulkan14Features fVulkan14 = {}; - - // Feature of VK_EXT_rasterization_order_attachment_access or - // VK_ARM_rasterization_order_attachment_access. - VkPhysicalDeviceRasterizationOrderAttachmentAccessFeaturesEXT - fRasterizationOrderAttachmentAccess = {}; - const char* fRasterizationOrderAttachmentAccessExtension = nullptr; - - // Feature of VK_EXT_blend_operation_advanced - VkPhysicalDeviceBlendOperationAdvancedFeaturesEXT fBlendOperationAdvanced = {}; - - // Feature of VK_EXT_extended_dynamic_state - VkPhysicalDeviceExtendedDynamicStateFeaturesEXT fExtendedDynamicState = {}; - - // Feature of VK_EXT_extended_dynamic_state2 - VkPhysicalDeviceExtendedDynamicState2FeaturesEXT fExtendedDynamicState2 = {}; - - // Feature of VK_EXT_vertex_input_dynamic_state - VkPhysicalDeviceVertexInputDynamicStateFeaturesEXT fVertexInputDynamicState = {}; - - // Feature of VK_EXT_graphics_pipeline_library - VkPhysicalDeviceGraphicsPipelineLibraryFeaturesEXT fGraphicsPipelineLibrary = {}; - - // Feature of VK_KHR_sampler_ycbcr_conversion or Vulkan 1.1 - VkPhysicalDeviceSamplerYcbcrConversionFeatures fSamplerYcbcrConversion = {}; - - // Feature of VK_EXT_rgba10x6_formats - VkPhysicalDeviceRGBA10X6FormatsFeaturesEXT fRGBA10x6Formats = {}; - - // Feature of VK_KHR_synchronization2 or Vulkan 1.3 - VkPhysicalDeviceDynamicRenderingFeatures fSynchronization2 = {}; - - // Feature of VK_KHR_dynamic_rendering or Vulkan 1.3 - VkPhysicalDeviceDynamicRenderingFeatures fDynamicRendering = {}; - - // Feature of VK_KHR_dynamic_rendering_local_read or Vulkan 1.4 - VkPhysicalDeviceDynamicRenderingLocalReadFeatures fDynamicRenderingLocalRead = {}; - - // Feature of VK_EXT_multisampled_render_to_single_sampled - VkPhysicalDeviceMultisampledRenderToSingleSampledFeaturesEXT - fMultisampledRenderToSingleSampled = {}; - - // Feature of VK_EXT_host_image_copy or Vulkan 1.4 - VkPhysicalDeviceHostImageCopyFeatures fHostImageCopy = {}; - - // Feature of VK_EXT_pipeline_creation_cache_control or Vulkan 1.3 - VkPhysicalDevicePipelineCreationCacheControlFeatures fPipelineCreationCacheControl = {}; - - // Feature of VK_EXT_frame_boundary - VkPhysicalDeviceFrameBoundaryFeaturesEXT fFrameBoundary = {}; - - // Extensions that don't have a feature: - // VK_KHR_driver_properties or Vulkan 1.2 - const char* fDriverPropertiesExtension = nullptr; - // VK_KHR_create_renderpass2 or Vulkan 1.2 - const char* fCreateRenderpass2Extension = nullptr; - // VK_EXT_load_store_op_none, VK_KHR_load_store_op_none or Vulkan 1.4 - const char* fLoadStoreOpNoneExtension = nullptr; - // VK_EXT_conservative_rasterization - const char* fConservativeRasterizationExtension = nullptr; - - // Extensions that the other extensions above depend on: - // Dependency of VK_EXT_graphics_pipeline_library: VK_KHR_pipeline_library - const char* fPipelineLibraryExtension = nullptr; - // Dependencies of VK_EXT_host_image_copy: VK_KHR_copy_commands2, VK_KHR_format_feature_flags2 - const char* fCopyCommands2Extension = nullptr; - const char* fFormatFeatureFlags2Extension = nullptr; - // Dependency of VK_EXT_multisampled_render_to_single_sampled: VK_KHR_depth_stencil_resolve - const char* fDepthStencilResolveExtension = nullptr; -}; - -} // namespace skgpu - -#endif // skgpu_VulkanPreferredFeatures_DEFINED diff --git a/gfx/skia/skia/include/pathops/SkPathOps.h b/gfx/skia/skia/include/pathops/SkPathOps.h @@ -12,10 +12,9 @@ #include "include/private/base/SkTArray.h" #include "include/private/base/SkTDArray.h" -#include <optional> - struct SkRect; + // FIXME: move everything below into the SkPath class /** * The logical operations that can be performed when combining two paths. @@ -44,35 +43,21 @@ enum SkPathOp { inputs. @return True if the operation succeeded. */ -std::optional<SkPath> SK_API Op(const SkPath& one, const SkPath& two, SkPathOp op); +bool SK_API Op(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result); -// DEPRECATED -static inline bool Op(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result) { - if (auto res = Op(one, two, op)) { - *result = *res; - return true; - } - return false; -} - -/** Return a path with a set of non-overlapping contours that describe the +/** Set this path to a set of non-overlapping contours that describe the same area as the original path. The curve order is reduced where possible so that cubics may be turned into quadratics, and quadratics maybe turned into lines. + Returns true if operation was able to produce a result; + otherwise, result is unmodified. + @param path The path to simplify. - @return The simplified path, or {} on failure. + @param result The simplified path. The result may be the input. + @return True if simplification succeeded. */ -std::optional<SkPath> SK_API Simplify(const SkPath& path); - -// DEPRECATED -static inline bool Simplify(const SkPath& path, SkPath* result) { - if (auto res = Simplify(path)) { - *result = *res; - return true; - } - return false; -} +bool SK_API Simplify(const SkPath& path, SkPath* result); /** Set the resulting rectangle to the tight bounds of the path. @@ -90,25 +75,19 @@ static inline bool TightBounds(const SkPath& path, SkRect* result) { return false; } -/** Returns a path with fill type winding to area equivalent to the input. - Does not detect if path contains contours which +/** Set the result with fill type winding to area equivalent to path. + Returns true if successful. Does not detect if path contains contours which contain self-crossings or cross other contours; in these cases, may return - a result even though it does not fill same area as the input. + true even though result does not fill same area as path. - If it fails to compute a result, returns {}. + Returns true if operation was able to produce a result; + otherwise, result is unmodified. The result may be the input. @param path The path typically with fill type set to even odd. + @param result The equivalent path with fill type set to winding. + @return True if winding path was set. */ -std::optional<SkPath> SK_API AsWinding(const SkPath& path); - -// DEPRECATED -static inline bool AsWinding(const SkPath& path, SkPath* result) { - if (auto res = AsWinding(path)) { - *result = *res; - return true; - } - return false; -} +bool SK_API AsWinding(const SkPath& path, SkPath* result); /** Perform a series of path operations, optimized for unioning many paths together. */ @@ -125,18 +104,10 @@ public: /** Computes the sum of all paths and operands, and resets the builder to its initial state. - @return result The product of the operands, {} on failure. + @param result The product of the operands. + @return True if the operation succeeded. */ - std::optional<SkPath> resolve(); - - // DEPRECATED - bool resolve(SkPath* result) { - if (auto res = this->resolve()) { - *result = *res; - return true; - } - return false; - } + bool resolve(SkPath* result); private: skia_private::TArray<SkPath> fPathRefs; diff --git a/gfx/skia/skia/include/ports/SkFontMgr_FontConfigInterface.h b/gfx/skia/skia/include/ports/SkFontMgr_FontConfigInterface.h @@ -11,13 +11,12 @@ #include "include/core/SkRefCnt.h" #include "include/core/SkTypes.h" -#include <memory> - class SkFontMgr; class SkFontConfigInterface; class SkFontScanner; /** Creates a SkFontMgr which wraps a SkFontConfigInterface. */ +SK_API sk_sp<SkFontMgr> SkFontMgr_New_FCI(sk_sp<SkFontConfigInterface> fci); SK_API sk_sp<SkFontMgr> SkFontMgr_New_FCI(sk_sp<SkFontConfigInterface> fci, std::unique_ptr<SkFontScanner> scanner); diff --git a/gfx/skia/skia/include/ports/SkFontMgr_android.h b/gfx/skia/skia/include/ports/SkFontMgr_android.h @@ -43,6 +43,10 @@ struct SkFontMgr_Android_CustomFonts { }; /** Create a font manager for Android. If 'custom' is NULL, use only system fonts. */ + +// Deprecated +SK_API sk_sp<SkFontMgr> SkFontMgr_New_Android(const SkFontMgr_Android_CustomFonts* custom); + SK_API sk_sp<SkFontMgr> SkFontMgr_New_Android(const SkFontMgr_Android_CustomFonts* custom, std::unique_ptr<SkFontScanner> scanner); #endif // SkFontMgr_android_DEFINED diff --git a/gfx/skia/skia/include/ports/SkFontMgr_android_ndk.h b/gfx/skia/skia/include/ports/SkFontMgr_android_ndk.h @@ -16,11 +16,9 @@ class SkFontMgr; class SkFontScanner; -/** Create a font manager for Android NDK. May return nullptr if unavailable (API < 29). +/** Create a font manager for Android NDK. May return nullptr if unavailable (API < 29). */ +SK_API sk_sp<SkFontMgr> SkFontMgr_New_AndroidNDK(bool cacheFontFiles); - The Android NDK Font API only works correctly in an Android Application process. - If running in a bare executable (for testing or the like) it will use the legacy fonts.xml data. - */ SK_API sk_sp<SkFontMgr> SkFontMgr_New_AndroidNDK(bool cacheFontFiles, std::unique_ptr<SkFontScanner> scanner); diff --git a/gfx/skia/skia/include/ports/SkFontMgr_fontconfig.h b/gfx/skia/skia/include/ports/SkFontMgr_fontconfig.h @@ -20,5 +20,5 @@ class SkFontMgr; */ class SkFontScanner; SK_API sk_sp<SkFontMgr> SkFontMgr_New_FontConfig(FcConfig* fc, std::unique_ptr<SkFontScanner> scanner); - +SK_API sk_sp<SkFontMgr> SkFontMgr_New_FontConfig(FcConfig* fc); #endif // #ifndef SkFontMgr_fontconfig_DEFINED diff --git a/gfx/skia/skia/include/ports/SkTypeface_cairo.h b/gfx/skia/skia/include/ports/SkTypeface_cairo.h @@ -10,6 +10,8 @@ typedef FT_FaceRec_* FT_Face; SK_API extern void SkInitCairoFT(bool fontHintingEnabled); SK_API extern SkTypeface* SkCreateTypefaceFromCairoFTFont( - FT_Face face = nullptr, void* faceContext = nullptr, uint8_t lcdFilter = 0); + FT_Face face = nullptr, void* faceContext = nullptr, + SkPixelGeometry pixelGeometry = kUnknown_SkPixelGeometry, + uint8_t lcdFilter = 0); #endif diff --git a/gfx/skia/skia/include/private/SkEncodedInfo.h b/gfx/skia/skia/include/private/SkEncodedInfo.h @@ -15,13 +15,11 @@ #include "include/core/SkImageInfo.h" #include "include/core/SkRefCnt.h" #include "include/core/SkTypes.h" -#include "include/private/SkHdrMetadata.h" #include "include/private/base/SkTo.h" #include "modules/skcms/skcms.h" #include <cstdint> #include <memory> -#include <tuple> #include <utility> struct SkEncodedInfo { @@ -120,27 +118,61 @@ public: static SkEncodedInfo Make(int width, int height, Color color, Alpha alpha, int bitsPerComponent, std::unique_ptr<ICCProfile> profile, int colorDepth) { - return Make(width, height, color, alpha, bitsPerComponent, colorDepth, std::move(profile), - skhdr::Metadata::MakeEmpty()); - } - - static SkEncodedInfo Make(int width, int height, Color color, - Alpha alpha, int bitsPerComponent, int colorDepth, std::unique_ptr<ICCProfile> profile, - const skhdr::Metadata& hdrMetadata) { SkASSERT(1 == bitsPerComponent || 2 == bitsPerComponent || 4 == bitsPerComponent || 8 == bitsPerComponent || 16 == bitsPerComponent); - VerifyColor(color, alpha, bitsPerComponent); + + switch (color) { + case kGray_Color: + SkASSERT(kOpaque_Alpha == alpha); + break; + case kGrayAlpha_Color: + SkASSERT(kOpaque_Alpha != alpha); + break; + case kPalette_Color: + SkASSERT(16 != bitsPerComponent); + break; + case kRGB_Color: + case kBGR_Color: + case kBGRX_Color: + SkASSERT(kOpaque_Alpha == alpha); + SkASSERT(bitsPerComponent >= 8); + break; + case kYUV_Color: + case kInvertedCMYK_Color: + case kYCCK_Color: + SkASSERT(kOpaque_Alpha == alpha); + SkASSERT(8 == bitsPerComponent); + break; + case kRGBA_Color: + SkASSERT(bitsPerComponent >= 8); + break; + case kBGRA_Color: + case kYUVA_Color: + SkASSERT(8 == bitsPerComponent); + break; + case kXAlpha_Color: + SkASSERT(kUnpremul_Alpha == alpha); + SkASSERT(8 == bitsPerComponent); + break; + case k565_Color: + SkASSERT(kOpaque_Alpha == alpha); + SkASSERT(8 == bitsPerComponent); + break; + default: + SkASSERT(false); + break; + } + return SkEncodedInfo(width, height, color, alpha, SkToU8(bitsPerComponent), SkToU8(colorDepth), - std::move(profile), - hdrMetadata); + std::move(profile)); } /* @@ -200,9 +232,10 @@ public: case kInvertedCMYK_Color: case kYCCK_Color: return 4 * fBitsPerComponent; + default: + SkASSERT(false); + return 0; } - SkASSERT(false); - return 0; } SkEncodedInfo(const SkEncodedInfo& orig) = delete; @@ -213,9 +246,12 @@ public: // Explicit copy method, to avoid accidental copying. SkEncodedInfo copy() const { - return SkEncodedInfo( - fWidth, fHeight, fColor, fAlpha, fBitsPerComponent, fColorDepth, - fProfile ? std::make_unique<ICCProfile>(*fProfile) : nullptr, fHdrMetadata); + auto copy = SkEncodedInfo::Make( + fWidth, fHeight, fColor, fAlpha, fBitsPerComponent, nullptr, fColorDepth); + if (fProfile) { + copy.fProfile = std::make_unique<ICCProfile>(*fProfile); + } + return copy; } // Return number of bits of R/G/B channel @@ -223,16 +259,9 @@ public: return fColorDepth; } - // Return the HDR metadata associated with this image. Note that even SDR images can include - // HDR metadata (e.g, indicating how to inverse tone map when displayed on an HDR display). - const skhdr::Metadata& getHdrMetadata() const { - return fHdrMetadata; - } - private: SkEncodedInfo(int width, int height, Color color, Alpha alpha, - uint8_t bitsPerComponent, uint8_t colorDepth, std::unique_ptr<ICCProfile> profile, - const skhdr::Metadata& hdrMetadata) + uint8_t bitsPerComponent, uint8_t colorDepth, std::unique_ptr<ICCProfile> profile) : fWidth(width) , fHeight(height) , fColor(color) @@ -240,55 +269,8 @@ private: , fBitsPerComponent(bitsPerComponent) , fColorDepth(colorDepth) , fProfile(std::move(profile)) - , fHdrMetadata(hdrMetadata) {} - static void VerifyColor(Color color, Alpha alpha, int bitsPerComponent) { - // Avoid `-Wunused-parameter` warnings on non-debug builds. - std::ignore = alpha; - std::ignore = bitsPerComponent; - - switch (color) { - case kGray_Color: - SkASSERT(kOpaque_Alpha == alpha); - return; - case kGrayAlpha_Color: - SkASSERT(kOpaque_Alpha != alpha); - return; - case kPalette_Color: - SkASSERT(16 != bitsPerComponent); - return; - case kRGB_Color: - case kBGR_Color: - case kBGRX_Color: - SkASSERT(kOpaque_Alpha == alpha); - SkASSERT(bitsPerComponent >= 8); - return; - case kYUV_Color: - case kInvertedCMYK_Color: - case kYCCK_Color: - SkASSERT(kOpaque_Alpha == alpha); - SkASSERT(8 == bitsPerComponent); - return; - case kRGBA_Color: - SkASSERT(bitsPerComponent >= 8); - return; - case kBGRA_Color: - case kYUVA_Color: - SkASSERT(8 == bitsPerComponent); - return; - case kXAlpha_Color: - SkASSERT(kUnpremul_Alpha == alpha); - SkASSERT(8 == bitsPerComponent); - return; - case k565_Color: - SkASSERT(kOpaque_Alpha == alpha); - SkASSERT(8 == bitsPerComponent); - return; - } - SkASSERT(false); // Unrecognized `color` enum value. - } - int fWidth; int fHeight; Color fColor; @@ -296,7 +278,6 @@ private: uint8_t fBitsPerComponent; uint8_t fColorDepth; std::unique_ptr<ICCProfile> fProfile; - skhdr::Metadata fHdrMetadata; }; #endif diff --git a/gfx/skia/skia/include/private/SkGainmapShader.h b/gfx/skia/skia/include/private/SkGainmapShader.h @@ -34,6 +34,10 @@ public: * map to the rectangle dstRect. Sampling will be done according to gainmapSamplingOptions. * * The gainmap will be applied according to the HDR to SDR ratio specified in dstHdrRatio. + * + * This shader must know the color space of the canvas that it will be rendered to. This color + * space must be specified in dstColorSpace. + * TODO(ccameron): Remove the need for dstColorSpace. */ static sk_sp<SkShader> Make(const sk_sp<const SkImage>& baseImage, const SkRect& baseRect, @@ -43,16 +47,6 @@ public: const SkSamplingOptions& gainmapSamplingOptions, const SkGainmapInfo& gainmapInfo, const SkRect& dstRect, - float dstHdrRatio); - - static sk_sp<SkShader> Make(const sk_sp<const SkImage>& baseImage, - const SkRect& baseRect, - const SkSamplingOptions& baseSamplingOptions, - const sk_sp<const SkImage>& gainmapImage, - const SkRect& gainmapRect, - const SkSamplingOptions& gainmapSamplingOptions, - const SkGainmapInfo& gainmapInfo, - const SkRect& dstRect, float dstHdrRatio, sk_sp<SkColorSpace> dstColorSpace); }; diff --git a/gfx/skia/skia/include/private/SkHdrMetadata.h b/gfx/skia/skia/include/private/SkHdrMetadata.h @@ -1,173 +0,0 @@ -/* - * 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 SkHdrMetadata_DEFINED -#define SkHdrMetadata_DEFINED - -#include "include/core/SkColorSpace.h" -#include "include/core/SkRefCnt.h" -#include "include/private/base/SkAPI.h" - -#include <optional> - - -class SkData; -class SkString; - -namespace skhdr { - -/** - * Content light level metadata. - * The semantics of this metadata is defined in: - * ANSI/CTA-861-H A DTV Profile for Uncompressed High Speed Digital Interfaces - * Annex P Calculation of MaxCLL and MaxFALL - * Slightly different semantics for this metadata are defined in: - * Portable Network Graphics (PNG) Specification (Third Edition) - * 11.3.2.8 cLLI Content Light Level Information - * https://www.w3.org/TR/png-3/#cLLI-chunk - * This metadata should only be used in ways that work with both semantics. - */ -struct SK_API ContentLightLevelInformation { - float fMaxCLL = 0.f; - float fMaxFALL = 0.f; - - /** - * Decode from the binary encoding listed at: - * AV1 Bitstream & Decoding Process Specification Version 1.0.0 Errata 1 - * https://aomediacodec.github.io/av1-spec/av1-spec.pdf - * 5.8.3 Metadata high dynamic range content light level syntax - * This encoding is equivalent to: - * ITU-T H.265 (V10) (07/2024) - * D.2.35 Content light level information SEI message syntax - * Return false if parsing fails. - */ - bool parse(const SkData* data); - - /** - * Serialize to the encoding used by parse(). - */ - sk_sp<SkData> serialize() const; - - /** - * Decode from the binary encoding listed at: - * Portable Network Graphics (PNG) Specification (Third Edition) - * 11.3.2.8 cLLI Content Light Level Information - * https://www.w3.org/TR/png-3/#cLLI-chunk - * This encoding is not equivalent to the encoding used by parse(). - * Return false if parsing fails. - */ - bool parsePngChunk(const SkData* data); - - /** - * Serialize to the encoding used by parsePngChunk(). - */ - sk_sp<SkData> serializePngChunk() const; - - /** - * Return a human-readable description. - */ - SkString toString() const; - - bool operator==(const ContentLightLevelInformation& other) const; - bool operator!=(const ContentLightLevelInformation& other) const { - return !(*this == other); - } -}; - -/** - * Mastering display color volume metadata. - * The semantics of this metadata is defined in: - * SMPTE ST 2086:2018 Mastering Display Color Volume Metadata Supporting - * High Luminance and Wide Color Gamut Images - */ -struct SK_API MasteringDisplayColorVolume { - SkColorSpacePrimaries fDisplayPrimaries = {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}; - float fMaximumDisplayMasteringLuminance = 0.f; - float fMinimumDisplayMasteringLuminance = 0.f; - - /** - * The encoding as defined in: - * AV1 Bitstream & Decoding Process Specification Version 1.0.0 Errata 1 - * https://aomediacodec.github.io/av1-spec/av1-spec.pdf - * 5.8.4 Metadata high dynamic range mastering display color volume syntax - * This encoding is equivalent to: - * ITU-T H.265 (V10) (07/2024) - * D.2.35 Content light level information SEI message syntax - * This encoding is also equivalent to: - * Portable Network Graphics (PNG) Specification (Third Edition) - * 11.3.2.7 mDCV Mastering Display Color Volume - * https://www.w3.org/TR/png-3/#mDCV-chunk - * Return false if parsing fails. - */ - bool parse(const SkData* data); - - /** - * Serialize to the encoding used by parse(). - */ - sk_sp<SkData> serialize() const; - - /** - * Return a human-readable description. - */ - SkString toString() const; - - bool operator==(const MasteringDisplayColorVolume& other) const; - bool operator!=(const MasteringDisplayColorVolume& other) const { - return !(*this == other); - } -}; - -/** - * Structure containing all HDR metadata that can be attached to an image or video frame. - */ -class SK_API Metadata { - public: - /** - * Return a container with no metadata. - */ - static Metadata MakeEmpty(); - - /** - * If there does not exists Content Light Level Information metadata, then return false. - * Otherwise return true and if `clli` is non-nullptr then write the metadata to `clli`. - */ - bool getContentLightLevelInformation(ContentLightLevelInformation* clli) const; - - /** - * Set the Content Light Level Information metadata. - */ - void setContentLightLevelInformation(const ContentLightLevelInformation& clli); - - /** - * If there does not exists Mastering Display Color Volume metadata, then return false. - * Otherwise return true and if `mdcv` is non-nullptr then write the metadata to `mdcv`. - */ - bool getMasteringDisplayColorVolume(MasteringDisplayColorVolume* mdcv) const; - - /** - * Set the Mastering Display Color Volume metadata. - */ - void setMasteringDisplayColorVolume(const MasteringDisplayColorVolume& mdcv); - - /** - * Return a human-readable description. - */ - SkString toString() const; - - bool operator==(const Metadata& other) const; - bool operator!=(const Metadata& other) const { - return !(*this == other); - } - - private: - std::optional<ContentLightLevelInformation> fContentLightLevelInformation; - std::optional<MasteringDisplayColorVolume> fMasteringDisplayColorVolume; -}; - -} // namespace skhdr - -#endif diff --git a/gfx/skia/skia/include/private/SkPathRef.h b/gfx/skia/skia/include/private/SkPathRef.h @@ -9,10 +9,7 @@ #define SkPathRef_DEFINED #include "include/core/SkArc.h" -#include "include/core/SkMatrix.h" -#include "include/core/SkPathTypes.h" // IWYU pragma: keep #include "include/core/SkPoint.h" -#include "include/core/SkRRect.h" #include "include/core/SkRect.h" #include "include/core/SkRefCnt.h" #include "include/core/SkScalar.h" @@ -29,63 +26,7 @@ #include <tuple> class SkMatrix; - -/* - * These "info" structs are used to return identifying information, when a path - * is queried if it is a special "shape". (e.g. isOval(), isRRect()) - */ - -struct SkPathRectInfo { - SkRect fRect; - SkPathDirection fDirection; - uint8_t fStartIndex; -}; - -struct SkPathOvalInfo { - SkRect fBounds; - SkPathDirection fDirection; - uint8_t fStartIndex; -}; - -struct SkPathRRectInfo { - SkRRect fRRect; - SkPathDirection fDirection; - uint8_t fStartIndex; -}; - -/* - * Paths can be tagged with a "Type" -- the IsAType - * This signals that it was built from a high-level shape: oval, rrect, arc, wedge. - * We try to retain this tag, but still build the explicitly line/quad/conic/cubic - * structure need to represent that shape. Thus a user of path can always just look - * at the points and verbs, and draw it correctly. - * - * The GPU backend sometimes will sniff the path for this tag/type, and may have a - * more optimal way to draw the shape if they know its "really" an oval or whatever. - * - * Path's can also identify as a "rect" -- but we don't store any special tag for this. - * - * Here are the special "types" we have APIs for (e.g. isRRect()) and what we store: - * - * kGeneral : no identifying shape, no extra data - * (Rect) : no tag, but isRect() will examing the points/verbs, and try to - * deduce that it represents a rect. - * kOval : the path bounds is also the oval's bounds -- we store the direction - * and start_index (important for dashing). see SkPathMakers.h - * kRRect : same as kOval for implicit bounds, direction and start_index. - * Note: we don't store its radii -- we deduce those when isRRect() is - * called, by examining the points/verbs. - */ -enum class SkPathIsAType : uint8_t { - kGeneral, - kOval, - kRRect, -}; - -struct SkPathIsAData { - uint8_t fStartIndex; - SkPathDirection fDirection; -}; +class SkRRect; /** * Holds the path verbs and points. It is versioned by a generation ID. None of its public methods @@ -107,11 +48,18 @@ public: // See https://bugs.chromium.org/p/skia/issues/detail?id=13817 for how these sizes were // determined. using PointsArray = skia_private::STArray<4, SkPoint>; - using VerbsArray = skia_private::STArray<4, SkPathVerb>; - using ConicWeightsArray = skia_private::STArray<2, float>; + using VerbsArray = skia_private::STArray<4, uint8_t>; + using ConicWeightsArray = skia_private::STArray<2, SkScalar>; + + enum class PathType : uint8_t { + kGeneral, + kOval, + kRRect, + kArc, + }; - SkPathRef(SkSpan<const SkPoint> points, SkSpan<const SkPathVerb> verbs, - SkSpan<const SkScalar> weights, unsigned segmentMask, const SkMatrix* mx) + SkPathRef(SkSpan<const SkPoint> points, SkSpan<const uint8_t> verbs, + SkSpan<const SkScalar> weights, unsigned segmentMask) : fPoints(points) , fVerbs(verbs) , fConicWeights(weights) @@ -119,11 +67,15 @@ public: fBoundsIsDirty = true; // this also invalidates fIsFinite fGenerationID = 0; // recompute fSegmentMask = segmentMask; - fType = SkPathIsAType::kGeneral; + fType = PathType::kGeneral; + // The next two values don't matter unless fType is kOval or kRRect + fRRectOrOvalIsCCW = false; + fRRectOrOvalStartIdx = 0xAC; + fArcOval.setEmpty(); + fArcStartAngle = fArcSweepAngle = 0.0f; + fArcType = SkArc::Type::kArc; SkDEBUGCODE(fEditorsAttached.store(0);) - if (mx && !mx->isIdentity()) { - mx->mapPoints(fPoints); - } + this->computeBounds(); // do this now, before we worry about multiple owners/threads SkDEBUGCODE(this->validate();) } @@ -154,7 +106,7 @@ public: * return value is a pointer to where the points for the verb should be written. * 'weight' is only used if 'verb' is kConic_Verb */ - SkPoint* growForVerb(SkPathVerb verb, SkScalar weight = 0) { + SkPoint* growForVerb(int /*SkPath::Verb*/ verb, SkScalar weight = 0) { SkDEBUGCODE(fPathRef->validate();) return fPathRef->growForVerb(verb, weight); } @@ -166,7 +118,7 @@ public: * If 'verb' is kConic_Verb, 'weights' will return a pointer to the * space for the conic weights (indexed normally). */ - SkPoint* growForRepeatedVerb(SkPathVerb verb, + SkPoint* growForRepeatedVerb(int /*SkPath::Verb*/ verb, int numVbs, SkScalar** weights = nullptr) { return fPathRef->growForRepeatedVerb(verb, numVbs, weights); @@ -196,12 +148,16 @@ public: */ SkPathRef* pathRef() { return fPathRef; } - void setIsOval(SkPathDirection dir, unsigned start) { - fPathRef->setIsOval(dir, start); + void setIsOval(bool isCCW, unsigned start) { + fPathRef->setIsOval(isCCW, start); } - void setIsRRect(SkPathDirection dir, unsigned start) { - fPathRef->setIsRRect(dir, start); + void setIsRRect(bool isCCW, unsigned start) { + fPathRef->setIsRRect(isCCW, start); + } + + void setIsArc(const SkArc& arc) { + fPathRef->setIsArc(arc); } void setBounds(const SkRect& rect) { fPathRef->setBounds(rect); } @@ -210,6 +166,34 @@ public: SkPathRef* fPathRef; }; + class SK_API Iter { + public: + Iter(); + Iter(const SkPathRef&); + + void setPathRef(const SkPathRef&); + + /** Return the next verb in this iteration of the path. When all + segments have been visited, return kDone_Verb. + + If any point in the path is non-finite, return kDone_Verb immediately. + + @param pts The points representing the current verb and/or segment + This must not be NULL. + @return The verb for the current segment + */ + uint8_t next(SkPoint pts[4]); + uint8_t peek() const; + + SkScalar conicWeight() const { return *fConicWeights; } + + private: + const SkPoint* fPts; + const uint8_t* fVerbs; + const uint8_t* fVerbStop; + const SkScalar* fConicWeights; + }; + public: /** * Gets a path ref with no verbs or points. @@ -234,23 +218,46 @@ public: */ uint32_t getSegmentMasks() const { return fSegmentMask; } - /** Returns Info struct if the path is an oval, else return {}. - * Tracking whether a path is an oval is considered an - * optimization for performance and so some paths that are in - * fact ovals can report {}. + /** Returns true if the path is an oval. + * + * @param rect returns the bounding rect of this oval. It's a circle + * if the height and width are the same. + * @param isCCW is the oval CCW (or CW if false). + * @param start indicates where the contour starts on the oval (see + * SkPath::addOval for intepretation of the index). + * + * @return true if this path is an oval. + * Tracking whether a path is an oval is considered an + * optimization for performance and so some paths that are in + * fact ovals can report false. */ - std::optional<SkPathOvalInfo> isOval() const { - if (fType == SkPathIsAType::kOval) { - return {{ - this->getBounds(), - fIsA.fDirection, - fIsA.fStartIndex, - }}; + bool isOval(SkRect* rect, bool* isCCW, unsigned* start) const { + if (fType == PathType::kOval) { + if (rect) { + *rect = this->getBounds(); + } + if (isCCW) { + *isCCW = SkToBool(fRRectOrOvalIsCCW); + } + if (start) { + *start = fRRectOrOvalStartIdx; + } } - return {}; + + return fType == PathType::kOval; } - std::optional<SkPathRRectInfo> isRRect() const; + bool isRRect(SkRRect* rrect, bool* isCCW, unsigned* start) const; + + bool isArc(SkArc* arc) const { + if (fType == PathType::kArc) { + if (arc) { + *arc = SkArc::Make(fArcOval, fArcStartAngle, fArcSweepAngle, fArcType); + } + } + + return fType == PathType::kArc; + } bool hasComputedBounds() const { return !fBoundsIsDirty; @@ -296,14 +303,12 @@ public: /** * Returns a pointer one beyond the first logical verb (last verb in memory order). */ - const SkPathVerb* verbsBegin() const { return fVerbs.begin(); } + const uint8_t* verbsBegin() const { return fVerbs.begin(); } /** * Returns a const pointer to the first verb in memory (which is the last logical verb). */ - const SkPathVerb* verbsEnd() const { return fVerbs.end(); } - - SkSpan<const SkPathVerb> verbs() const { return fVerbs; } + const uint8_t* verbsEnd() const { return fVerbs.end(); } /** * Returns a const pointer to the first point. @@ -315,18 +320,14 @@ public: */ const SkPoint* pointsEnd() const { return this->points() + this->countPoints(); } - SkSpan<const SkPoint> pointSpan() const { return fPoints; } - SkSpan<const float> conicSpan() const { return fConicWeights; } - const SkScalar* conicWeights() const { return fConicWeights.begin(); } const SkScalar* conicWeightsEnd() const { return fConicWeights.end(); } - /** * Convenience methods for getting to a verb or point by index. */ - SkPathVerb atVerb(int index) const { return fVerbs[index]; } - SkPoint atPoint(int index) const { return fPoints[index]; } + uint8_t atVerb(int index) const { return fVerbs[index]; } + const SkPoint& atPoint(int index) const { return fPoints[index]; } bool operator== (const SkPathRef& ref) const; @@ -336,7 +337,7 @@ public: * Gets an ID that uniquely identifies the contents of the path ref. If two path refs have the * same ID then they have the same verbs and points. However, two path refs may have the same * contents but different genIDs. - * skbug.com/40032862 for background on why fillType is necessary (for now). + * skbug.com/1762 for background on why fillType is necessary (for now). */ uint32_t genID(uint8_t fillType) const; @@ -370,8 +371,13 @@ private: fBoundsIsDirty = true; // this also invalidates fIsFinite fGenerationID = kEmptyGenID; fSegmentMask = 0; - fType = SkPathIsAType::kGeneral; - + fType = PathType::kGeneral; + // The next two values don't matter unless fType is kOval or kRRect + fRRectOrOvalIsCCW = false; + fRRectOrOvalStartIdx = 0xAC; + fArcOval.setEmpty(); + fArcStartAngle = fArcSweepAngle = 0.0f; + fArcType = SkArc::Type::kArc; if (numPoints > 0) { fPoints.reserve_exact(numPoints); } @@ -389,7 +395,7 @@ private: // Return true if the computed bounds are finite. static bool ComputePtBounds(SkRect* bounds, const SkPathRef& ref) { - return bounds->setBoundsCheck({ref.points(), ref.countPoints()}); + return bounds->setBoundsCheck(ref.points(), ref.countPoints()); } // called, if dirty, by getBounds() @@ -438,7 +444,7 @@ private: fGenerationID = 0; fSegmentMask = 0; - fType = SkPathIsAType::kGeneral; + fType = PathType::kGeneral; } /** Resets the path ref with verbCount verbs and pointCount points, all uninitialized. Also @@ -465,14 +471,14 @@ private: * verb. If 'verb' is kConic_Verb, 'weights' will return a pointer to the * uninitialized conic weights. */ - SkPoint* growForRepeatedVerb(SkPathVerb, int numVbs, SkScalar** weights); + SkPoint* growForRepeatedVerb(int /*SkPath::Verb*/ verb, int numVbs, SkScalar** weights); /** * Increases the verb count 1, records the new verb, and creates room for the requisite number * of additional points. A pointer to the first point is returned. Any new points are * uninitialized. */ - SkPoint* growForVerb(SkPathVerb, SkScalar weight); + SkPoint* growForVerb(int /*SkPath::Verb*/ verb, SkScalar weight); /** * Concatenates all verbs from 'path' onto our own verbs array. Increases the point count by the @@ -485,29 +491,37 @@ private: /** * Private, non-const-ptr version of the public function verbsMemBegin(). */ - uint8_t* verbsBeginWritable() { return (uint8_t*)fVerbs.begin(); } + uint8_t* verbsBeginWritable() { return fVerbs.begin(); } /** * Called the first time someone calls CreateEmpty to actually create the singleton. */ friend SkPathRef* sk_create_empty_pathref(); - void setIsOval(SkPathDirection dir, unsigned start) { - fType = SkPathIsAType::kOval; - fIsA.fDirection = dir; - fIsA.fStartIndex = SkToU8(start); + void setIsOval(bool isCCW, unsigned start) { + fType = PathType::kOval; + fRRectOrOvalIsCCW = isCCW; + fRRectOrOvalStartIdx = SkToU8(start); + } + + void setIsRRect(bool isCCW, unsigned start) { + fType = PathType::kRRect; + fRRectOrOvalIsCCW = isCCW; + fRRectOrOvalStartIdx = SkToU8(start); } - void setIsRRect(SkPathDirection dir, unsigned start) { - fType = SkPathIsAType::kRRect; - fIsA.fDirection = dir; - fIsA.fStartIndex = SkToU8(start); + void setIsArc(const SkArc& arc) { + fType = PathType::kArc; + fArcOval = arc.fOval; + fArcStartAngle = arc.fStartAngle; + fArcSweepAngle = arc.fSweepAngle; + fArcType = arc.fType; } // called only by the editor. Note that this is not a const function. SkPoint* getWritablePoints() { SkDEBUGCODE(this->validate();) - fType = SkPathIsAType::kGeneral; + fType = PathType::kGeneral; return fPoints.begin(); } @@ -523,6 +537,7 @@ private: ConicWeightsArray fConicWeights; mutable SkRect fBounds; + SkRect fArcOval; enum { kEmptyGenID = 1, // GenID reserved for path ref with zero points and zero verbs. @@ -532,13 +547,23 @@ private: SkDEBUGCODE(std::atomic<int> fEditorsAttached;) // assert only one editor in use at any time. - // based on fType - SkPathIsAData fIsA {}; + SkScalar fArcStartAngle; + SkScalar fArcSweepAngle; + + PathType fType; + + 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; - SkPathIsAType fType; - uint8_t fSegmentMask; - mutable bool fBoundsIsDirty; - mutable bool fIsFinite; // only meaningful if bounds are valid + 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/SkAlign.h b/gfx/skia/skia/include/private/base/SkAlign.h @@ -41,15 +41,4 @@ static inline constexpr size_t SkAlignTo(size_t x, size_t alignment) { return (x + alignment - 1) & ~(alignment - 1); } -/** - * align up to a non power of 2 - */ -static inline constexpr size_t SkAlignNonPow2(size_t x, size_t alignment) { - const size_t misalignment = x % alignment; - if (misalignment) { - x += alignment - misalignment; - } - return x; -} - #endif diff --git a/gfx/skia/skia/include/private/base/SkTemplates.h b/gfx/skia/skia/include/private/base/SkTemplates.h @@ -102,17 +102,17 @@ template <typename T> class AutoTArray { public: AutoTArray() {} // Allocate size number of T elements - explicit AutoTArray(size_t size) - : fData(size > 0 ? new T[check_size_bytes_too_big<T>(size)] : nullptr) - , fSize(size) {} + explicit AutoTArray(size_t size) { + fSize = check_size_bytes_too_big<T>(size); + fData.reset(size > 0 ? new T[size] : nullptr); + } // TODO: remove when all uses are gone. explicit AutoTArray(int size) : AutoTArray(SkToSizeT(size)) {} - AutoTArray(AutoTArray&& other) - : fData(std::move(other.fData)) - , fSize(std::exchange(other.fSize, 0)) {} - + AutoTArray(AutoTArray&& other) : fData(std::move(other.fData)) { + fSize = std::exchange(other.fSize, 0); + } AutoTArray& operator=(AutoTArray&& other) { if (this != &other) { fData = std::move(other.fData); @@ -122,7 +122,6 @@ public: } // Reallocates given a new count. Reallocation occurs even if new count equals old count. - [[clang::reinitializes]] void reset(size_t count = 0) { *this = AutoTArray(count); } @@ -167,30 +166,17 @@ private: size_t fSize = 0; }; -/** Like AutoTArray with storage for some number of elements "nested within". The requested number - * of elements to fit in the storage is specified by kCountRequested. kCount is the actual number - * of elements that will fit in the storage. If the runtime number of elements exceeds the space of - * the storage, the elements will live on the heap. +/** Like AutoTArray with room for kCountRequested elements preallocated on + * the Stack. If count exceeds the space of the preallocation, the elements + * will live on the heap. Once this goes out of scope, the elements will be + * cleaned up "auto"matically. */ template <int kCountRequested, typename T> class AutoSTArray { public: + AutoSTArray(AutoSTArray&&) = delete; AutoSTArray(const AutoSTArray&) = delete; - AutoSTArray& operator=(const AutoSTArray&) = delete; - - AutoSTArray(AutoSTArray&& that) { - if (that.fArray == nullptr) { - fArray = nullptr; - fCount = 0; - } else if (that.fArray == (T*) that.fStorage) { - fArray = (T*) fStorage; - fCount = that.fCount; - std::uninitialized_move(that.fArray, that.fArray + that.fCount, fArray); - } else { - fArray = std::exchange(that.fArray, nullptr); - fCount = std::exchange(that.fCount, 0); - } - } AutoSTArray& operator=(AutoSTArray&&) = delete; + AutoSTArray& operator=(const AutoSTArray&) = delete; /** Initialize with no objects */ AutoSTArray() { @@ -198,7 +184,8 @@ public: fCount = 0; } - /** Allocate count number of T elements */ + /** Allocate count number of T elements + */ AutoSTArray(int count) { fArray = nullptr; fCount = 0; @@ -210,17 +197,18 @@ public: } /** Destroys previous objects in the array and default constructs count number of objects */ - [[clang::reinitializes]] void reset(int count) { - T* start = begin(); - T* iter = end(); + T* start = fArray; + T* iter = start + fCount; while (iter > start) { (--iter)->~T(); } SkASSERT(count >= 0); if (fCount != count) { - if (fArray != (T*) fStorage) { + if (fCount > kCount) { + // 'fArray' was allocated last time so free it now + SkASSERT((T*) fStorage != fArray); sk_free(fArray); } @@ -235,31 +223,19 @@ public: fCount = count; } - iter = begin(); - T* stop = end(); + iter = fArray; + T* stop = fArray + count; while (iter < stop) { new (iter++) T; } } - /* Removes elements with index >= count */ - void trimTo(int count) { - SkASSERT(count >= 0); - if (count >= fCount) { - return; - } - T* start = begin() + count; - T* iter = end(); - while (iter > start) { - (--iter)->~T(); - } - fCount = count; - } - - /** Return the number of T elements in the array */ + /** Return the number of T elements in the array + */ int count() const { return fCount; } - /** Return the array of T elements. Will be nullptr if count == 0 */ + /** Return the array of T elements. Will be NULL if count == 0 + */ T* get() const { return fArray; } T* begin() { return fArray; } @@ -270,7 +246,8 @@ public: const T* end() const { return fArray + fCount; } - /** Return the nth element in the array */ + /** Return the nth element in the array + */ T& operator[](int index) const { return fArray[sk_collection_check_bounds(index, fCount)]; } @@ -297,11 +274,9 @@ private: // 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)); -public: static constexpr int kCount = SkAlignTo(kMinCount*sizeof(T) + sizeof(int), std::max(alignof(T*), alignof(T))) / sizeof(T); -private: T* fArray; alignas(T) std::byte fStorage[kCount * sizeof(T)]; int fCount; @@ -331,7 +306,6 @@ public: } /** Resize the memory area pointed to by the current ptr without preserving contents. */ - [[clang::reinitializes]] T* reset(size_t count = 0) { fPtr.reset(count ? (T*)sk_malloc_throw(count, sizeof(T)) : nullptr); return this->get(); @@ -380,20 +354,10 @@ public: } } + AutoSTMalloc(AutoSTMalloc&&) = delete; AutoSTMalloc(const AutoSTMalloc&) = delete; - AutoSTMalloc& operator=(const AutoSTMalloc&) = delete; - - AutoSTMalloc(AutoSTMalloc&& that) { - if (that.fPtr == nullptr) { - fPtr = nullptr; - } else if (that.fPtr == that.fTStorage) { - fPtr = fTStorage; - memcpy(fPtr, that.fPtr, kCount * sizeof(T)); - } else { - fPtr = std::exchange(that.fPtr, nullptr); - } - } AutoSTMalloc& operator=(AutoSTMalloc&&) = delete; + AutoSTMalloc& operator=(const AutoSTMalloc&) = delete; ~AutoSTMalloc() { if (fPtr != fTStorage) { @@ -402,7 +366,6 @@ public: } // doesn't preserve contents - [[clang::reinitializes]] T* reset(size_t count) { if (fPtr != fTStorage) { sk_free(fPtr); @@ -464,18 +427,13 @@ private: // 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 constexpr size_t kMaxBytes = 4 * 1024; - static constexpr size_t kMinCount = kCountRequested * sizeof(T) > kMaxBytes + static constexpr size_t kCount = kCountRequested * sizeof(T) > kMaxBytes ? kMaxBytes / sizeof(T) : kCountWithPadding; #else - static constexpr size_t kMinCount = kCountWithPadding; + static constexpr size_t kCount = kCountWithPadding; #endif -public: - static constexpr size_t kCount = kMinCount; - -private: - T* fPtr; union { uint32_t fStorage32[SkAlign4(kCount*sizeof(T)) >> 2]; diff --git a/gfx/skia/skia/include/private/gpu/ganesh/GrTypesPriv.h b/gfx/skia/skia/include/private/gpu/ganesh/GrTypesPriv.h @@ -473,7 +473,7 @@ constexpr static int kGrInternalTextureFlagsMask = static_cast<int>( // if the proxy has it set then the surface must also have it set. All other flags listed here must // match on the proxy and surface. // TODO: Add back kFramebufferOnly flag here once we update GrSurfaceCharacterization to take it -// as a flag. skbug.com/40042017 +// as a flag. skbug.com/10672 constexpr static int kGrInternalRenderTargetFlagsMask = static_cast<int>( GrInternalSurfaceFlags::kGLRTFBOIDIs0 | GrInternalSurfaceFlags::kRequiresManualMSAAResolve/* | diff --git a/gfx/skia/skia/include/utils/SkParsePath.h b/gfx/skia/skia/include/utils/SkParsePath.h @@ -16,15 +16,7 @@ class SkString; class SK_API SkParsePath { public: - static std::optional<SkPath> FromSVGString(const char str[]); - // Deprecated - static bool FromSVGString(const char str[], SkPath* outPath) { - if (auto result = FromSVGString(str)) { - *outPath = *result; - return true; - } - return false; - } + static bool FromSVGString(const char str[], SkPath*); enum class PathEncoding { Absolute, Relative }; static SkString ToSVGString(const SkPath&, PathEncoding = PathEncoding::Absolute); diff --git a/gfx/skia/skia/modules/skcms/README.chromium b/gfx/skia/skia/modules/skcms/README.chromium @@ -2,4 +2,5 @@ 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 @@ -161,16 +161,6 @@ static skcms_TFType classify(const skcms_TransferFunction& tf, TF_PQish* pq = memcpy(hlg, &tf.a, sizeof(*hlg)); } return skcms_TFType_HLGinvish; - case skcms_TFType_PQ: - if (tf.b != 0.f || tf.c != 0.f || tf.d != 0.f || tf.e != 0.f || tf.f != 0.f) { - return skcms_TFType_Invalid; - } - return skcms_TFType_PQ; - case skcms_TFType_HLG: - if (tf.d != 0.f || tf.e != 0.f || tf.f != 0.f) { - return skcms_TFType_Invalid; - } - return skcms_TFType_HLG; } return skcms_TFType_Invalid; } @@ -202,12 +192,6 @@ bool skcms_TransferFunction_isPQish(const skcms_TransferFunction* tf) { bool skcms_TransferFunction_isHLGish(const skcms_TransferFunction* tf) { return classify(*tf) == skcms_TFType_HLGish; } -bool skcms_TransferFunction_isPQ(const skcms_TransferFunction* tf) { - return classify(*tf) == skcms_TFType_PQ; -} -bool skcms_TransferFunction_isHLG(const skcms_TransferFunction* tf) { - return classify(*tf) == skcms_TFType_HLG; -} bool skcms_TransferFunction_makePQish(skcms_TransferFunction* tf, float A, float B, float C, @@ -225,28 +209,6 @@ bool skcms_TransferFunction_makeScaledHLGish(skcms_TransferFunction* tf, return true; } -void skcms_TransferFunction_makePQ( - skcms_TransferFunction* tf, - float hdr_reference_white_luminance) { - *tf = { TFKind_marker(skcms_TFType_PQ), - hdr_reference_white_luminance, - 0.f,0.f,0.f,0.f,0.f }; - assert(skcms_TransferFunction_isPQ(tf)); -} - -void skcms_TransferFunction_makeHLG( - skcms_TransferFunction* tf, - float hdr_reference_white_luminance, - float peak_luminance, - float system_gamma) { - *tf = { TFKind_marker(skcms_TFType_HLG), - hdr_reference_white_luminance, - peak_luminance, - system_gamma, - 0.f, 0.f, 0.f }; - assert(skcms_TransferFunction_isHLG(tf)); -} - float skcms_TransferFunction_eval(const skcms_TransferFunction* tf, float x) { float sign = x < 0 ? -1.0f : 1.0f; x *= sign; @@ -256,13 +218,6 @@ float skcms_TransferFunction_eval(const skcms_TransferFunction* tf, float x) { switch (classify(*tf, &pq, &hlg)) { case skcms_TFType_Invalid: break; - case skcms_TFType_HLG: { - const float a = 0.17883277f; - const float b = 0.28466892f; - const float c = 0.55991073f; - return sign * (x <= 0.5f ? x*x/3.f : (expf_((x-c)/a) + b) / 12.f); - } - case skcms_TFType_HLGish: { const float K = hlg.K_minus_1 + 1.0f; return K * sign * (x*hlg.R <= 1 ? powf_(x*hlg.R, hlg.G) @@ -281,16 +236,6 @@ float skcms_TransferFunction_eval(const skcms_TransferFunction* tf, float x) { return sign * (x < tf->d ? tf->c * x + tf->f : powf_(tf->a * x + tf->b, tf->g) + tf->e); - case skcms_TFType_PQ: { - const float c1 = 107 / 128.f; - const float c2 = 2413 / 128.f; - const float c3 = 2392 / 128.f; - const float m1 = 1305 / 8192.f; - const float m2 = 2523 / 32.f; - const float p = powf_(x, 1.f / m2); - return powf_((p - c1) / (c2 - c3 * p), 1.f / m1); - } - case skcms_TFType_PQish: return sign * powf_((pq.A + pq.B * powf_(x, pq.C)) / (pq.D + pq.E * powf_(x, pq.C)), pq.F); @@ -1985,8 +1930,6 @@ bool skcms_TransferFunction_invert(const skcms_TransferFunction* src, skcms_Tran TF_HLGish hlg; switch (classify(*src, &pq, &hlg)) { case skcms_TFType_Invalid: return false; - case skcms_TFType_PQ: return false; - case skcms_TFType_HLG: return false; case skcms_TFType_sRGBish: break; // handled below case skcms_TFType_PQish: @@ -2515,11 +2458,6 @@ static OpAndArg select_curve_op(const skcms_Curve* curve, int channel) { switch (classify(tf)) { case skcms_TFType_Invalid: return noop; - // TODO(https://issues.skia.org/issues/420956739): Consider adding - // support for PQ and HLG. Generally any code that goes through this - // path would also want tone mapping too. - case skcms_TFType_PQ: return noop; - case skcms_TFType_HLG: return noop; case skcms_TFType_sRGBish: return OpAndArg{op.sRGBish, &tf}; case skcms_TFType_PQish: return OpAndArg{op.PQish, &tf}; case skcms_TFType_HLGish: return OpAndArg{op.HLGish, &tf}; @@ -2601,96 +2539,23 @@ static size_t bytes_per_pixel(skcms_PixelFormat fmt) { return 0; } -// See ITU-T H.273 Table 3 for the full list of codes. -const uint8_t kTransferCicpIdPQ = 16; -const uint8_t kTransferCicpIdHLG = 18; - -static bool has_cicp_pq_trc(const skcms_ICCProfile* profile) { - return profile->has_CICP - && profile->CICP.transfer_characteristics == kTransferCicpIdPQ; -} - -static bool has_cicp_hlg_trc(const skcms_ICCProfile* profile) { - return profile->has_CICP - && profile->CICP.transfer_characteristics == kTransferCicpIdHLG; -} - -// Set tf to be the PQ transfer function, scaled such that 1.0 will map to 10,000 / 203. -static void set_reference_pq_ish_trc(skcms_TransferFunction* tf) { - // Initialize such that 1.0 maps to 1.0. - skcms_TransferFunction_makePQish(tf, - -107/128.0f, 1.0f, 32/2523.0f, 2413/128.0f, -2392/128.0f, 8192/1305.0f); - - // Distribute scaling factor W by scaling A and B with X ^ (1/F): - // ((A + Bx^C) / (D + Ex^C))^F * W = ((A + Bx^C) / (D + Ex^C) * W^(1/F))^F - // See https://crbug.com/1058580#c32 for discussion. - const float w = 10000.0f / 203.0f; - const float ws = powf_(w, 1.0f / tf->f); - tf->a = ws * tf->a; - tf->b = ws * tf->b; -} - -// Set tf to be the HLG inverse OETF, scaled such that 1.0 will map to 1.0. -// While this is one version of HLG, there are many others. A better version -// would be to use the 1,000 nit reference version, but that will require -// adding opt-optical transform support. -static void set_sdr_hlg_ish_trc(skcms_TransferFunction* tf) { - skcms_TransferFunction_makeHLGish(tf, - 2.0f, 2.0f, 1/0.17883277f, 0.28466892f, 0.55991073f); - tf->f = 1.0f / 12.0f - 1.0f; -} - static bool prep_for_destination(const skcms_ICCProfile* profile, skcms_Matrix3x3* fromXYZD50, skcms_TransferFunction* invR, skcms_TransferFunction* invG, - skcms_TransferFunction* invB, - bool* dst_using_B2A, - bool* dst_using_hlg_ootf) { - const bool has_xyzd50 = - profile->has_toXYZD50 && - skcms_Matrix3x3_invert(&profile->toXYZD50, fromXYZD50); - *dst_using_B2A = false; - *dst_using_hlg_ootf = false; - - // CICP-specified PQ or HLG transfer functions take precedence. - // TODO: Add the ability to parse CICP primaries to not require - // the XYZD50 matrix. - if (has_cicp_pq_trc(profile) && has_xyzd50) { - skcms_TransferFunction trc_pq; - set_reference_pq_ish_trc(&trc_pq); - skcms_TransferFunction_invert(&trc_pq, invR); - skcms_TransferFunction_invert(&trc_pq, invG); - skcms_TransferFunction_invert(&trc_pq, invB); - return true; - } - if (has_cicp_hlg_trc(profile) && has_xyzd50) { - skcms_TransferFunction trc_hlg; - set_sdr_hlg_ish_trc(&trc_hlg); - skcms_TransferFunction_invert(&trc_hlg, invR); - skcms_TransferFunction_invert(&trc_hlg, invG); - skcms_TransferFunction_invert(&trc_hlg, invB); - *dst_using_hlg_ootf = true; - return true; - } - - // Then prefer the B2A transformation. - // skcms_Transform() supports B2A destinations. - if (profile->has_B2A) { - *dst_using_B2A = true; - return true; - } - - // Finally use parametric transfer functions. - // TODO: Reject non sRGB-ish transfer functions here. - return has_xyzd50 - && profile->has_trc + skcms_TransferFunction* invB) { + // skcms_Transform() supports B2A destinations... + if (profile->has_B2A) { return true; } + // ...and destinations with parametric transfer functions and an XYZD50 gamut matrix. + return profile->has_trc + && profile->has_toXYZD50 && profile->trc[0].table_entries == 0 && profile->trc[1].table_entries == 0 && profile->trc[2].table_entries == 0 && skcms_TransferFunction_invert(&profile->trc[0].parametric, invR) && skcms_TransferFunction_invert(&profile->trc[1].parametric, invG) - && skcms_TransferFunction_invert(&profile->trc[2].parametric, invB); + && skcms_TransferFunction_invert(&profile->trc[2].parametric, invB) + && skcms_Matrix3x3_invert(&profile->toXYZD50, fromXYZD50); } bool skcms_Transform(const void* src, @@ -2751,21 +2616,13 @@ bool skcms_Transform(const void* src, } }; - // If the source has a TRC that is specified by CICP and not the TRC - // entries, then store it here for future use. - skcms_TransferFunction src_cicp_trc; - // These are always parametric curves of some sort. skcms_Curve dst_curves[3]; dst_curves[0].table_entries = dst_curves[1].table_entries = dst_curves[2].table_entries = 0; - // This will store the XYZD50 to destination gamut conversion matrix, if it is needed. - skcms_Matrix3x3 dst_from_xyz; - - // This will store the full source to destination gamut conversion matrix, if it is needed. - skcms_Matrix3x3 dst_from_src; + skcms_Matrix3x3 from_xyz; switch (srcFmt >> 1) { default: return false; @@ -2832,32 +2689,15 @@ bool skcms_Transform(const void* src, if (dstProfile != srcProfile) { - // Track whether or not the A2B or B2A transforms are used. the CICP - // values take precedence over A2B and B2A. - bool src_using_A2B = false; - bool src_using_hlg_ootf = false; - bool dst_using_B2A = false; - bool dst_using_hlg_ootf = false; - if (!prep_for_destination(dstProfile, - &dst_from_xyz, + &from_xyz, &dst_curves[0].parametric, &dst_curves[1].parametric, - &dst_curves[2].parametric, - &dst_using_B2A, - &dst_using_hlg_ootf)) { + &dst_curves[2].parametric)) { return false; } - if (has_cicp_pq_trc(srcProfile) && srcProfile->has_toXYZD50) { - set_reference_pq_ish_trc(&src_cicp_trc); - add_op_ctx(Op::pq_rgb, &src_cicp_trc); - } else if (has_cicp_hlg_trc(srcProfile) && srcProfile->has_toXYZD50) { - src_using_hlg_ootf = true; - set_sdr_hlg_ish_trc(&src_cicp_trc); - add_op_ctx(Op::hlg_rgb, &src_cicp_trc); - } else if (srcProfile->has_A2B) { - src_using_A2B = true; + if (srcProfile->has_A2B) { if (srcProfile->A2B.input_channels) { add_curve_ops(srcProfile->A2B.input_curves, (int)srcProfile->A2B.input_channels); @@ -2895,14 +2735,10 @@ bool skcms_Transform(const void* src, // A2B sources are in XYZD50 by now, but TRC sources are still in their original gamut. assert (srcProfile->has_A2B || srcProfile->has_toXYZD50); - if (dst_using_B2A) { + if (dstProfile->has_B2A) { // B2A needs its input in XYZD50, so transform TRC sources now. - if (!src_using_A2B) { + if (!srcProfile->has_A2B) { add_op_ctx(Op::matrix_3x3, &srcProfile->toXYZD50); - // Apply the HLG OOTF in XYZD50 space, if needed. - if (src_using_hlg_ootf) { - add_op(Op::hlg_ootf_scale); - } } if (dstProfile->pcs == skcms_Signature_Lab) { @@ -2935,34 +2771,22 @@ bool skcms_Transform(const void* src, } } else { // This is a TRC destination. - - // Transform to the destination gamut. - if (src_using_hlg_ootf != dst_using_hlg_ootf) { - // If just the src or the dst has an HLG OOTF then we will apply the OOTF in XYZD50 - // space. If both the src and dst has an HLG OOTF then they will cancel. - if (!src_using_A2B) { - add_op_ctx(Op::matrix_3x3, &srcProfile->toXYZD50); - } - if (src_using_hlg_ootf) { - add_op(Op::hlg_ootf_scale); - } - if (dst_using_hlg_ootf) { - add_op(Op::hlginv_ootf_scale); - } - add_op_ctx(Op::matrix_3x3, &dst_from_xyz); - } else if (src_using_A2B) { - // If the source is A2B then we are already in XYZD50. Just apply the xyz->dst - // matrix. - add_op_ctx(Op::matrix_3x3, &dst_from_xyz); - } else { - const skcms_Matrix3x3* to_xyz = &srcProfile->toXYZD50; - // There's a chance the source and destination gamuts are identical, - // in which case we can skip the gamut transform. - if (0 != memcmp(&dstProfile->toXYZD50, to_xyz, sizeof(skcms_Matrix3x3))) { - // Concat the entire gamut transform into dst_from_src. - dst_from_src = skcms_Matrix3x3_concat(&dst_from_xyz, to_xyz); - add_op_ctx(Op::matrix_3x3, &dst_from_src); - } + // We'll concat any src->xyz matrix with our xyz->dst matrix into one src->dst matrix. + // (A2B sources are already in XYZD50, making that src->xyz matrix I.) + static const skcms_Matrix3x3 I = {{ + { 1.0f, 0.0f, 0.0f }, + { 0.0f, 1.0f, 0.0f }, + { 0.0f, 0.0f, 1.0f }, + }}; + const skcms_Matrix3x3* to_xyz = srcProfile->has_A2B ? &I : &srcProfile->toXYZD50; + + // There's a chance the source and destination gamuts are identical, + // in which case we can skip the gamut transform. + if (0 != memcmp(&dstProfile->toXYZD50, to_xyz, sizeof(skcms_Matrix3x3))) { + // Concat the entire gamut transform into from_xyz, + // now slightly misnamed but it's a handy spot to stash the result. + from_xyz = skcms_Matrix3x3_concat(&from_xyz, to_xyz); + add_op_ctx(Op::matrix_3x3, &from_xyz); } // Encode back to dst RGB using its parametric transfer functions. @@ -3064,9 +2888,7 @@ static void assert_usable_as_destination(const skcms_ICCProfile* profile) { #else skcms_Matrix3x3 fromXYZD50; skcms_TransferFunction invR, invG, invB; - bool useB2A = false; - bool useHlgOotf = false; - assert(prep_for_destination(profile, &fromXYZD50, &invR, &invG, &invB, &useB2A, &useHlgOotf)); + assert(prep_for_destination(profile, &fromXYZD50, &invR, &invG, &invB)); #endif } diff --git a/gfx/skia/skia/modules/skcms/skcms.gni b/gfx/skia/skia/modules/skcms/skcms.gni @@ -1,43 +0,0 @@ -# DO NOT EDIT: This is a generated file. -# See //bazel/exporter_tool/README.md for more information. -# -# The source of truth is //modules/skcms/BUILD.bazel - -# To update this file, run make -C bazel generate_gni - -_modules = get_path_info("../../modules", "abspath") - -# Generated by Bazel rule //modules/skcms:public_hdrs -skcms_public_headers = [ "$_modules/skcms/skcms.h" ] - -# Generated by Bazel rule //modules/skcms:skcms_public -skcms_public = [ - "$_modules/skcms/skcms.cc", - "$_modules/skcms/skcms.h", - "$_modules/skcms/src/skcms_internals.h", - "$_modules/skcms/src/skcms_public.h", -] - -# Generated by Bazel rule //modules/skcms:skcms_TransformBaseline -skcms_TransformBaseline = [ - "$_modules/skcms/src/skcms_Transform.h", - "$_modules/skcms/src/skcms_TransformBaseline.cc", - "$_modules/skcms/src/skcms_internals.h", - "$_modules/skcms/src/skcms_public.h", -] - -# Generated by Bazel rule //modules/skcms:skcms_TransformHsw -skcms_TransformHsw = [ - "$_modules/skcms/src/skcms_Transform.h", - "$_modules/skcms/src/skcms_TransformHsw.cc", - "$_modules/skcms/src/skcms_internals.h", - "$_modules/skcms/src/skcms_public.h", -] - -# Generated by Bazel rule //modules/skcms:skcms_TransformSkx -skcms_TransformSkx = [ - "$_modules/skcms/src/skcms_Transform.h", - "$_modules/skcms/src/skcms_TransformSkx.cc", - "$_modules/skcms/src/skcms_internals.h", - "$_modules/skcms/src/skcms_public.h", -] diff --git a/gfx/skia/skia/modules/skcms/src/Transform_inl.h b/gfx/skia/skia/modules/skcms/src/Transform_inl.h @@ -111,9 +111,14 @@ template <typename D, typename S> SI D cast(const S& v) { #if N == 1 return (D)v; -#else +#elif defined(__clang__) return __builtin_convertvector(v, D); - +#else + D d; + for (int i = 0; i < N; i++) { + d[i] = v[i]; + } + return d; #endif } @@ -151,13 +156,8 @@ SI F F_from_Half(U16 half) { #elif defined(USING_AVX512F) return (F)_mm512_cvtph_ps((__m256i)half); #elif defined(USING_AVX_F16C) -#if defined(__clang__) && __clang_major__ >= 15 // for _Float16 support - typedef _Float16 __attribute__((vector_size(16))) F16; - return __builtin_convertvector((F16)half, F); -#else typedef int16_t __attribute__((vector_size(16))) I16; return __builtin_ia32_vcvtph2ps256((I16)half); -#endif // defined(__clang)) #else U32 wide = cast<U32>(half); // A half is 1-5-10 sign-exponent-mantissa, with 15 exponent bias. @@ -359,14 +359,6 @@ SI F apply_hlginv(const skcms_TransferFunction* tf, F x) { return bit_pun<F>(sign | bit_pun<U32>(v)); } -// Compute the luminance Y used in the HLG OOTF. This is equivalent to computing the dot product -// with the vector [0.2627 0.678 0.0593] in Rec2020 primaries, but is performed in the XYZD50 -// space to simplify the pipeline. -SI F compute_Y_in_xyzd50(F x, F y, F z) { - return -0.02831655f * x + - 1.00995452f * y + - 0.02102382f * z; -} // Strided loads and stores of N values, starting from p. template <typename T, typename P> @@ -1224,27 +1216,6 @@ STAGE(hlg_rgb, const skcms_TransferFunction* tf) { b = apply_hlg(tf, b); } -// Apply the HLG Reference OOTF, as described in ITU-R BT.2100-3 Table 5. -STAGE(hlg_ootf_scale, const void*) { - // Compute Y in the XYZD50 primaries. - F Y = compute_Y_in_xyzd50(r, g, b); - - // Apply the gamma of 1.2. - const float gamma_minus_1 = 0.2f; - U32 sign; - Y = strip_sign(Y, &sign); - F Y_to_gamma_minus1 = apply_sign(approx_pow(Y, gamma_minus_1), sign); - r = r * Y_to_gamma_minus1; - g = g * Y_to_gamma_minus1; - b = b * Y_to_gamma_minus1; - - // Scale to the reference peak white (1000 nits) to get display luminance. Then divide by the - // HDR reference white (203 nits), to get a value in relative linear color space. - r *= 1000.0f / 203.0f; - g *= 1000.0f / 203.0f; - b *= 1000.0f / 203.0f; -} - STAGE(hlginv_r, const skcms_TransferFunction* tf) { r = apply_hlginv(tf, r); } STAGE(hlginv_g, const skcms_TransferFunction* tf) { g = apply_hlginv(tf, g); } STAGE(hlginv_b, const skcms_TransferFunction* tf) { b = apply_hlginv(tf, b); } @@ -1256,23 +1227,6 @@ STAGE(hlginv_rgb, const skcms_TransferFunction* tf) { b = apply_hlginv(tf, b); } -// Perform the inverse of the operation in hlg_ootf_scale. -STAGE(hlginv_ootf_scale, const void*) { - r *= (203.f / 1000.0f); - g *= (203.f / 1000.0f); - b *= (203.f / 1000.0f); - - const float gamma_inv_minus_1 = 1.0f / 1.2f - 1.0f; - F Y = compute_Y_in_xyzd50(r, g, b); - U32 sign; - Y = strip_sign(Y, &sign); - F Y_to_gamma_minus1 = apply_sign(approx_pow(Y, gamma_inv_minus_1), sign); - - r = r * Y_to_gamma_minus1; - g = g * Y_to_gamma_minus1; - b = b * Y_to_gamma_minus1; -} - STAGE(table_r, const skcms_Curve* curve) { r = table(curve, r); } STAGE(table_g, const skcms_Curve* curve) { g = table(curve, g); } STAGE(table_b, const skcms_Curve* curve) { b = table(curve, b); } diff --git a/gfx/skia/skia/modules/skcms/src/skcms_Transform.h b/gfx/skia/skia/modules/skcms/src/skcms_Transform.h @@ -72,14 +72,12 @@ namespace skcms_private { M(hlg_b) \ M(hlg_a) \ M(hlg_rgb) \ - M(hlg_ootf_scale) \ \ M(hlginv_r) \ M(hlginv_g) \ M(hlginv_b) \ M(hlginv_a) \ M(hlginv_rgb) \ - M(hlginv_ootf_scale) \ \ M(table_r) \ M(table_g) \ diff --git a/gfx/skia/skia/modules/skcms/src/skcms_internals.h b/gfx/skia/skia/modules/skcms/src/skcms_internals.h @@ -21,14 +21,12 @@ extern "C" { // skcms can leverage some C++ extensions when they are present. #define ARRAY_COUNT(arr) (int)(sizeof((arr)) / sizeof(*(arr))) -#if defined(__has_cpp_attribute) +#if defined(__clang__) && defined(__has_cpp_attribute) #if __has_cpp_attribute(clang::fallthrough) #define SKCMS_FALLTHROUGH [[clang::fallthrough]] - #elif __has_cpp_attribute(gnu::fallthrough) - #define SKCMS_FALLTHROUGH [[gnu::fallthrough]] #endif - #if defined(__clang__) && !defined(SKCMS_HAS_MUSTTAIL) + #ifndef SKCMS_HAS_MUSTTAIL // [[clang::musttail]] is great for performance, but it's not well supported and we run into // a variety of problems when we use it. Fortunately, it's an optional feature that doesn't // affect correctness, and usually the compiler will generate a tail-call even for us @@ -56,20 +54,6 @@ extern "C" { && !defined(_WIN32) && !defined(__SYMBIAN32__) #define SKCMS_HAS_MUSTTAIL 1 #endif - #elif defined(__GNUC__) && !defined(SKCMS_HAS_MUSTTAIL) - // GCC on riscv64 does not support our tail call functions - // cf. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121784 - #if __has_cpp_attribute(clang::musttail) && !defined(__riscv) - #define SKCMS_HAS_MUSTTAIL 1 - #else - #define SKCMS_HAS_MUSTTAIL 0 - #endif - #elif !defined(__clang__) && !defined(SKCMS_HAS_MUSTTAIL) - #if __has_cpp_attribute(clang::musttail) - #define SKCMS_HAS_MUSTTAIL 1 - #else - #define SKCMS_HAS_MUSTTAIL 0 - #endif #endif #endif diff --git a/gfx/skia/skia/modules/skcms/src/skcms_public.h b/gfx/skia/skia/modules/skcms/src/skcms_public.h @@ -57,8 +57,6 @@ typedef enum skcms_TFType { skcms_TFType_PQish, skcms_TFType_HLGish, skcms_TFType_HLGinvish, - skcms_TFType_PQ, - skcms_TFType_HLG, } skcms_TFType; // Identify which kind of transfer function is encoded in an skcms_TransferFunction @@ -88,48 +86,21 @@ static inline bool skcms_TransferFunction_makeHLGish(skcms_TransferFunction* fn, return skcms_TransferFunction_makeScaledHLGish(fn, 1.0f, R,G, a,b,c); } -// The PQ transfer function. The function skcms_TransferFunction_eval will always evaluate to the -// unit PQ EOTF, which maps [0, 1] to [0, 1], regardless of the other parameters. -// This is stored differently from PQish transfer functions. In particular: -// - the constant -5 is stored in g -// - the hdr_reference_white_luminance parameter is stored in a -// - all other parameters are set to 0 -// When this is used as an SkColorSpace, the transformation to XYZD50 will be as follows: -// 1. Apply the unit PQ EOTF to each channel -// 2. Multiply by 10,000 nits -// 3. Divide by hdr_reference_white_luminance nits (default is 203) -// 4. Transform primaries to XYZD50 -SKCMS_API void skcms_TransferFunction_makePQ( - skcms_TransferFunction*, - float hdr_reference_white_luminance); - -// The HLG transfer function. The function skcms_TransferFunction_eval will always evaluate to the -// HLG inverse OETF, which maps [0, 1] to [0, 1], regardless of the other parameters. -// This is stored differently from PQish transfer functions. In particular: -// - the constant -6 is stored in g -// - the hdr_reference_white_luminance parameter is stored in a -// - the peak_white_luminance parameter is stored in b -// - the system_gamma parameter is stored in c -// - all other parameters are set to 0 -// When this is used as an SkColorSpace, the transformation to XYZD50 will be as follows: -// 1. Apply the HLG inverse OETF to each channel -// 2. Transform primaries to Rec2020 -// 3. Apply the channel-mixing HLG OOTF using system_gamma (default is 1.2) -// 4. Multiply by peak_luminance nits (default is 1,000) -// 5. Divide by hdr_reference_white nits (default is 203) -// 6. Transform primaries to XYZD50 -SKCMS_API void skcms_TransferFunction_makeHLG( - skcms_TransferFunction*, - float hdr_reference_white_luminance, - float peak_luminance, - float system_gamma); +// PQ mapping encoded [0,1] to linear [0,1]. +static inline bool skcms_TransferFunction_makePQ(skcms_TransferFunction* tf) { + return skcms_TransferFunction_makePQish(tf, -107/128.0f, 1.0f, 32/2523.0f + , 2413/128.0f, -2392/128.0f, 8192/1305.0f); +} +// HLG mapping encoded [0,1] to linear [0,12]. +static inline bool skcms_TransferFunction_makeHLG(skcms_TransferFunction* tf) { + return skcms_TransferFunction_makeHLGish(tf, 2.0f, 2.0f + , 1/0.17883277f, 0.28466892f, 0.55991073f); +} // Is this an ordinary sRGB-ish transfer function, or one of the HDR forms we support? SKCMS_API bool skcms_TransferFunction_isSRGBish(const skcms_TransferFunction*); SKCMS_API bool skcms_TransferFunction_isPQish (const skcms_TransferFunction*); SKCMS_API bool skcms_TransferFunction_isHLGish (const skcms_TransferFunction*); -SKCMS_API bool skcms_TransferFunction_isPQ (const skcms_TransferFunction*); -SKCMS_API bool skcms_TransferFunction_isHLG (const skcms_TransferFunction*); // Unified representation of 'curv' or 'para' tag data, or a 1D table from 'mft1' or 'mft2' typedef union skcms_Curve { diff --git a/gfx/skia/skia/modules/skcms/version.sha1 b/gfx/skia/skia/modules/skcms/version.sha1 @@ -1 +1 @@ -96d9171c94b937a1b5f0293de7309ac16311b722 +1e365691d01ad13edd93056c2731a5c6e0be2a15 diff --git a/gfx/skia/skia/src/base/SkArenaAlloc.h b/gfx/skia/skia/src/base/SkArenaAlloc.h @@ -268,7 +268,6 @@ private: template <typename T> T* allocUninitializedArray(size_t countZ) { - static_assert(!std::has_virtual_destructor<T>::value, "Can't make an array of objects which have a vtable"); AssertRelease(SkTFitsIn<uint32_t>(countZ)); uint32_t count = SkToU32(countZ); diff --git a/gfx/skia/skia/src/base/SkCubics.cpp b/gfx/skia/skia/src/base/SkCubics.cpp @@ -129,7 +129,7 @@ int SkCubics::RootsValidT(double A, double B, double C, double D, int foundRoots = 0; for (int index = 0; index < realRoots; ++index) { double tValue = allRoots[index]; - if (tValue <= 1.00005 && (tValue >= 1.0 || sk_doubles_nearly_equal_ulps(tValue, 1.0))) { + if (tValue >= 1.0 && tValue <= 1.00005) { // Make sure we do not already have 1 (or something very close) in the list of roots. if ((foundRoots < 1 || !sk_doubles_nearly_equal_ulps(solution[0], 1)) && (foundRoots < 2 || !sk_doubles_nearly_equal_ulps(solution[1], 1))) { diff --git a/gfx/skia/skia/src/base/SkSafeMath.h b/gfx/skia/skia/src/base/SkSafeMath.h @@ -51,15 +51,6 @@ public: return a + b; } - int mulInt(int x, int y) { - int64_t result = (int64_t)x * (int64_t)y; - if (result > std::numeric_limits<int>::max() || result < std::numeric_limits<int>::min()) { - fOK = false; - return x; - } - return (int)result; - } - size_t alignUp(size_t x, size_t alignment) { SkASSERT(alignment && !(alignment & (alignment - 1))); return add(x, alignment - 1) & ~(alignment - 1); diff --git a/gfx/skia/skia/src/base/SkTLazy.h b/gfx/skia/skia/src/base/SkTLazy.h @@ -13,14 +13,6 @@ #include <optional> #include <utility> -template <typename T> T* SkOptAddressOrNull(std::optional<T>& object) { - return object.has_value() ? &object.value() : nullptr; -} - -template <typename T> const T* SkOptAddressOrNull(const std::optional<T>& object) { - return object.has_value() ? &object.value() : nullptr; -} - /** * Efficient way to defer allocating/initializing a class until it is needed * (if ever). diff --git a/gfx/skia/skia/src/base/SkTime.cpp b/gfx/skia/skia/src/base/SkTime.cpp @@ -14,7 +14,7 @@ #if __has_feature(memory_sanitizer) || defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_ANDROID) #include <time.h> double SkTime::GetNSecs() { - // See skbug.com/40037711 + // See skia:6504 struct timespec tp; clock_gettime(CLOCK_MONOTONIC, &tp); return tp.tv_sec * 1e9 + tp.tv_nsec; diff --git a/gfx/skia/skia/src/base/SkVx.h b/gfx/skia/skia/src/base/SkVx.h @@ -46,7 +46,6 @@ #elif SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE41 #include <smmintrin.h> #elif SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE1 - #include <emmintrin.h> #include <xmmintrin.h> #elif defined(SK_ARM_HAS_NEON) #include <arm_neon.h> diff --git a/gfx/skia/skia/src/codec/SkCodec.cpp b/gfx/skia/skia/src/codec/SkCodec.cpp @@ -46,6 +46,10 @@ #include "include/codec/SkGifDecoder.h" #endif +#if defined(SK_HAS_HEIF_LIBRARY) +#include "include/android/SkHeifDecoder.h" +#endif + #if defined(SK_CODEC_DECODES_ICO) #include "include/codec/SkIcoDecoder.h" #endif @@ -60,8 +64,6 @@ #if defined(SK_CODEC_DECODES_PNG_WITH_LIBPNG) #include "include/codec/SkPngDecoder.h" -#elif defined(SK_CODEC_DECODES_PNG_WITH_RUST) -#include "include/codec/SkPngRustDecoder.h" #endif #if defined(SK_CODEC_DECODES_RAW) @@ -88,8 +90,6 @@ static std::vector<Decoder>* get_decoders_for_editing() { if (decoders->empty()) { #if defined(SK_CODEC_DECODES_PNG_WITH_LIBPNG) decoders->push_back(SkPngDecoder::Decoder()); -#elif defined(SK_CODEC_DECODES_PNG_WITH_RUST) - decoders->push_back(SkPngRustDecoder::Decoder()); #endif #if defined(SK_CODEC_DECODES_JPEG) decoders->push_back(SkJpegDecoder::Decoder()); @@ -119,6 +119,9 @@ static std::vector<Decoder>* get_decoders_for_editing() { #if defined(SK_CODEC_DECODES_JPEGXL) decoders->push_back(SkJpegxlDecoder::Decoder()); #endif +#if defined(SK_HAS_HEIF_LIBRARY) + decoders->push_back(SkHeifDecoder::Decoder()); +#endif #if defined(SK_CODEC_DECODES_RAW) decoders->push_back(SkRawDecoder::Decoder()); #endif @@ -309,15 +312,6 @@ bool SkCodec::conversionSupported(const SkImageInfo& dst, bool srcIsOpaque, bool } } -bool SkCodec::rewindStream() { - // Some codecs do not have a stream. They may hold onto their own data or another codec. - // They must handle rewinding themselves. - if (fStream && !fStream->rewind()) { - return false; - } - return true; -} - bool SkCodec::rewindIfNeeded() { // Store the value of fNeedsRewind so we can update it. Next read will // require a rewind. @@ -332,6 +326,12 @@ bool SkCodec::rewindIfNeeded() { // startIncrementalDecode will need to be called before incrementalDecode. fStartedIncrementalDecode = false; + // Some codecs do not have a stream. They may hold onto their own data or another codec. + // They must handle rewinding themselves. + if (fStream && !fStream->rewind()) { + return false; + } + return this->onRewind(); } @@ -350,12 +350,9 @@ bool zero_rect(const SkImageInfo& dstInfo, void* pixels, size_t rowBytes, if (dimensions != srcDimensions) { SkRect src = SkRect::Make(srcDimensions); SkRect dst = SkRect::Make(dimensions); - auto map = SkMatrix::Rect2Rect(src, dst); - if (!map) { - return false; - } + SkMatrix map = SkMatrix::RectToRect(src, dst); SkRect asRect = SkRect::Make(prevRect); - if (!map->mapRect(&asRect)) { + if (!map.mapRect(&asRect)) { return false; } asRect.roundOut(&prevRect); @@ -524,7 +521,7 @@ SkCodec::Result SkCodec::getPixels(const SkImageInfo& info, void* pixels, size_t // their own. They indicate that all of the memory has been filled by // setting rowsDecoded equal to the height. if ((kIncompleteInput == result || kErrorInInput == result) && rowsDecoded != info.height()) { - // FIXME: (skbug.com/40036982) fillIncompleteImage will fill using the swizzler's width, unless + // FIXME: (skbug.com/5772) fillIncompleteImage will fill using the swizzler's width, unless // there is a subset. In that case, it will use the width of the subset. From here, the // subset will only be non-null in the case of SkWebpCodec, but it treats the subset // differenty from the other codecs, and it needs to use the width specified by the info. @@ -797,9 +794,6 @@ bool SkCodecPriv::SelectXformFormat(SkColorType colorType, case kRGBA_F16_SkColorType: *outFormat = skcms_PixelFormat_RGBA_hhhh; break; - case kRGBA_1010102_SkColorType: - *outFormat = skcms_PixelFormat_RGBA_1010102; - break; case kBGR_101010x_XR_SkColorType: *outFormat = skcms_PixelFormat_BGR_101010x_XR; break; @@ -818,25 +812,22 @@ bool SkCodec::initializeColorXform(const SkImageInfo& dstInfo, SkEncodedInfo::Al bool needsColorXform = false; if (this->usesColorXform()) { if (kRGBA_F16_SkColorType == dstInfo.colorType() || - kRGBA_1010102_SkColorType == dstInfo.colorType() || kBGR_101010x_XR_SkColorType == dstInfo.colorType()) { needsColorXform = true; if (dstInfo.colorSpace()) { - dstInfo.colorSpace()->toProfile(&fDstProfileStorage); - fDstProfile = &fDstProfileStorage; + dstInfo.colorSpace()->toProfile(&fDstProfile); } else { // Use the srcProfile to avoid conversion. const auto* srcProfile = fEncodedInfo.profile(); - fDstProfile = srcProfile ? srcProfile : skcms_sRGB_profile(); + fDstProfile = srcProfile ? *srcProfile : *skcms_sRGB_profile(); } } else if (dstInfo.colorSpace()) { - dstInfo.colorSpace()->toProfile(&fDstProfileStorage); - fDstProfile = &fDstProfileStorage; + dstInfo.colorSpace()->toProfile(&fDstProfile); const auto* srcProfile = fEncodedInfo.profile(); if (!srcProfile) { srcProfile = skcms_sRGB_profile(); } - if (!skcms_ApproximatelyEqualProfiles(srcProfile, fDstProfile) ) { + if (!skcms_ApproximatelyEqualProfiles(srcProfile, &fDstProfile) ) { needsColorXform = true; } } @@ -868,7 +859,7 @@ void SkCodec::applyColorXform(void* dst, const void* src, int count) const { // It is okay for srcProfile to be null. This will use sRGB. const auto* srcProfile = fEncodedInfo.profile(); SkAssertResult(skcms_Transform(src, fSrcXformFormat, skcms_AlphaFormat_Unpremul, srcProfile, - dst, fDstXformFormat, fDstXformAlphaFormat, fDstProfile, + dst, fDstXformFormat, fDstXformAlphaFormat, &fDstProfile, count)); } diff --git a/gfx/skia/skia/src/codec/SkImageGenerator_FromEncoded.cpp b/gfx/skia/skia/src/codec/SkImageGenerator_FromEncoded.cpp @@ -51,7 +51,7 @@ namespace SkImages { sk_sp<SkImage> DeferredFromEncodedData(sk_sp<SkData> encoded, std::optional<SkAlphaType> alphaType) { - if (nullptr == encoded || encoded->empty()) { + if (nullptr == encoded || encoded->isEmpty()) { return nullptr; } return DeferredFromGenerator(SkImageGenerators::MakeFromEncoded(std::move(encoded), alphaType)); diff --git a/gfx/skia/skia/src/core/SkAAClip.cpp b/gfx/skia/skia/src/core/SkAAClip.cpp @@ -21,7 +21,6 @@ #include "src/core/SkBlitter.h" #include "src/core/SkColorData.h" #include "src/core/SkMask.h" -#include "src/core/SkPathPriv.h" #include "src/core/SkScan.h" #include <algorithm> @@ -837,11 +836,10 @@ bool SkAAClip::Builder::blitPath(SkAAClip* target, const SkPath& path, bool doAA Blitter blitter(this); SkRegion clip(fBounds); - const auto raw = SkPathPriv::Raw(path); if (doAA) { - SkScan::AntiFillPath(raw, clip, &blitter, true); + SkScan::AntiFillPath(path, clip, &blitter, true); } else { - SkScan::FillPath(raw, clip, &blitter); + SkScan::FillPath(path, clip, &blitter); } blitter.finish(); diff --git a/gfx/skia/skia/src/core/SkATrace.h b/gfx/skia/skia/src/core/SkATrace.h @@ -42,7 +42,7 @@ public: const uint8_t* getCategoryGroupEnabled(const char* name) override; const char* getCategoryGroupName(const uint8_t* categoryEnabledFlag) override { - static const char* const category = "skiaATrace"; + static const char* category = "skiaATrace"; return category; } diff --git a/gfx/skia/skia/src/core/SkAnalyticEdge.cpp b/gfx/skia/skia/src/core/SkAnalyticEdge.cpp @@ -10,14 +10,13 @@ #include "include/core/SkPoint.h" #include "include/private/base/SkMath.h" #include "include/private/base/SkTo.h" -#include "src/base/SkMathPriv.h" #include "src/core/SkFDot6.h" #include <algorithm> #include <cstddef> #include <iterator> -static constexpr int kInverseTableSize = 1024; // SK_FDot6One * 16 +static const int kInverseTableSize = 1024; // SK_FDot6One * 16 static inline SkFixed quick_inverse(SkFDot6 x) { static const int32_t table[] = { @@ -256,191 +255,38 @@ bool SkAnalyticEdge::updateLine(SkFixed x0, SkFixed y0, SkFixed x1, SkFixed y1, return true; } -bool SkAnalyticEdge::update(SkFixed last_y) { +bool SkAnalyticEdge::update(SkFixed last_y, bool sortY) { SkASSERT(last_y >= fLowerY); // we shouldn't update edge if last_y < fLowerY if (fCurveCount < 0) { - return static_cast<SkAnalyticCubicEdge*>(this)->updateCubic(); + return static_cast<SkAnalyticCubicEdge*>(this)->updateCubic(sortY); } else if (fCurveCount > 0) { return static_cast<SkAnalyticQuadraticEdge*>(this)->updateQuadratic(); } return false; } -/* We store 1<<shift in a (signed) byte, so its maximum value is 1<<6 == 64. - Note that this limits the number of lines we use to approximate a curve. - If we need to increase this, we need to store fCurveCount in something - larger than int8_t. -*/ -#define MAX_COEFF_SHIFT 6 - -static inline SkFDot6 cheap_distance(SkFDot6 dx, SkFDot6 dy) -{ - dx = SkAbs32(dx); - dy = SkAbs32(dy); - // return max + min/2 - if (dx > dy){ - dx += dy >> 1; - } else { - dx = dy + (dx >> 1); - } - return dx; -} - -static inline int diff_to_shift(SkFDot6 dx, SkFDot6 dy, int shiftAA) { - // cheap calc of distance from center of p0-p2 to the center of the curve - SkFDot6 dist = cheap_distance(dx, dy); - - // shift down dist (it is currently in dot6) - // down by 3 should give us 1/8 pixel accuracy (assuming our dist is accurate...) - // this is chosen by heuristic: make it as big as possible (to minimize segments) - // ... but small enough so that our curves still look smooth - // When shift > 0, we're using AA and everything is scaled up so we can - // lower the accuracy. - dist = (dist + (1 << (2 + shiftAA))) >> (3 + shiftAA); - - // each subdivision (shift value) cuts this dist (error) by 1/4 - return (32 - SkCLZ(dist)) >> 1; -} - -/* - In setQuadraticWithoutUpdate, setCubicWithoutUpdate, the first thing we do is to convert - the points into FDot6. This is modulated by the shift parameter, which - will be something like 2 for antialiasing. - - In the float case, we want to turn the float into .6 by saying pt * 64, - or pt * 256 for antialiasing. This is implemented as 1 << (shift + 6). - - In the fixed case, we want to turn the fixed into .6 by saying pt >> 10, - or pt >> 8 for antialiasing. This is implemented as pt >> (10 - shift). -*/ - -static inline SkFixed SkFDot6ToFixedDiv2(SkFDot6 value) { - // we want to return SkFDot6ToFixed(value >> 1), but we don't want to throw - // away data in value, so just perform a modify up-shift - return SkLeftShift(value, 16 - 6 - 1); -} - -bool SkAnalyticQuadraticEdge::setQuadraticWithoutUpdate(const SkPoint pts[3], int shift) { - SkFDot6 x0, y0, x1, y1, x2, y2; - - { -#ifdef SK_RASTERIZE_EVEN_ROUNDING - x0 = SkScalarRoundToFDot6(pts[0].fX, shift); - y0 = SkScalarRoundToFDot6(pts[0].fY, shift); - x1 = SkScalarRoundToFDot6(pts[1].fX, shift); - y1 = SkScalarRoundToFDot6(pts[1].fY, shift); - x2 = SkScalarRoundToFDot6(pts[2].fX, shift); - y2 = SkScalarRoundToFDot6(pts[2].fY, shift); -#else - float scale = float(1 << (shift + 6)); - x0 = int(pts[0].fX * scale); - y0 = int(pts[0].fY * scale); - x1 = int(pts[1].fX * scale); - y1 = int(pts[1].fY * scale); - x2 = int(pts[2].fX * scale); - y2 = int(pts[2].fY * scale); -#endif - } - - Winding winding = Winding::kCW; - if (y0 > y2) - { - using std::swap; - swap(x0, x2); - swap(y0, y2); - winding = Winding::kCCW; - } - SkASSERT(y0 <= y1 && y1 <= y2); - - int top = SkFDot6Round(y0); - int bot = SkFDot6Round(y2); - - // are we a zero-height quad (line)? - if (top == bot) { - return 0; - } - - // compute number of steps needed (1 << shift) - { - SkFDot6 dx = (SkLeftShift(x1, 1) - x0 - x2) >> 2; - SkFDot6 dy = (SkLeftShift(y1, 1) - y0 - y2) >> 2; - // This is a little confusing: - // before this line, shift is the scale up factor for AA; - // after this line, shift is the fCurveShift. - shift = diff_to_shift(dx, dy, shift); - SkASSERT(shift >= 0); - } - // need at least 1 subdivision for our bias trick - if (shift == 0) { - shift = 1; - } else if (shift > MAX_COEFF_SHIFT) { - shift = MAX_COEFF_SHIFT; - } - - fWinding = winding; - //fCubicDShift only set for cubics - fEdgeType = Type::kQuad; - fCurveCount = SkToS8(1 << shift); - - /* - * We want to reformulate into polynomial form, to make it clear how we - * should forward-difference. - * - * p0 (1 - t)^2 + p1 t(1 - t) + p2 t^2 ==> At^2 + Bt + C - * - * A = p0 - 2p1 + p2 - * B = 2(p1 - p0) - * C = p0 - * - * Our caller must have constrained our inputs (p0..p2) to all fit into - * 16.16. However, as seen above, we sometimes compute values that can be - * larger (e.g. B = 2*(p1 - p0)). To guard against overflow, we will store - * A and B at 1/2 of their actual value, and just apply a 2x scale during - * application in updateQuadratic(). Hence we store (shift - 1) in - * fCurveShift. - */ - - fCurveShift = SkToU8(shift - 1); - - SkFixed A = SkFDot6ToFixedDiv2(x0 - x1 - x1 + x2); // 1/2 the real value - SkFixed B = SkFDot6ToFixed(x1 - x0); // 1/2 the real value - - fQx = SkFDot6ToFixed(x0); - fQDx = B + (A >> shift); // biased by shift - fQDDx = A >> (shift - 1); // biased by shift - - A = SkFDot6ToFixedDiv2(y0 - y1 - y1 + y2); // 1/2 the real value - B = SkFDot6ToFixed(y1 - y0); // 1/2 the real value - - fQy = SkFDot6ToFixed(y0); - fQDy = B + (A >> shift); // biased by shift - fQDDy = A >> (shift - 1); // biased by shift - - fQLastX = SkFDot6ToFixed(x2); - fQLastY = SkFDot6ToFixed(y2); - - return true; -} - bool SkAnalyticQuadraticEdge::setQuadratic(const SkPoint pts[3]) { - if (!setQuadraticWithoutUpdate(pts, kDefaultAccuracy)) { + if (!fQEdge.setQuadraticWithoutUpdate(pts, kDefaultAccuracy)) { return false; } - fQx >>= kDefaultAccuracy; - fQy >>= kDefaultAccuracy; - fQDx >>= kDefaultAccuracy; - fQDy >>= kDefaultAccuracy; - fQDDx >>= kDefaultAccuracy; - fQDDy >>= kDefaultAccuracy; - fQLastX >>= kDefaultAccuracy; - fQLastY >>= kDefaultAccuracy; - fQy = SnapY(fQy); - fQLastY = SnapY(fQLastY); - + fQEdge.fQx >>= kDefaultAccuracy; + fQEdge.fQy >>= kDefaultAccuracy; + fQEdge.fQDx >>= kDefaultAccuracy; + fQEdge.fQDy >>= kDefaultAccuracy; + fQEdge.fQDDx >>= kDefaultAccuracy; + fQEdge.fQDDy >>= kDefaultAccuracy; + fQEdge.fQLastX >>= kDefaultAccuracy; + fQEdge.fQLastY >>= kDefaultAccuracy; + fQEdge.fQy = SnapY(fQEdge.fQy); + fQEdge.fQLastY = SnapY(fQEdge.fQLastY); + + fWinding = fQEdge.fWinding; fEdgeType = Type::kQuad; + fCurveCount = fQEdge.fCurveCount; + fCurveShift = fQEdge.fCurveShift; - fSnappedX = fQx; - fSnappedY = fQy; + fSnappedX = fQEdge.fQx; + fSnappedY = fQEdge.fQy; return this->updateQuadratic(); } @@ -448,10 +294,10 @@ bool SkAnalyticQuadraticEdge::setQuadratic(const SkPoint pts[3]) { bool SkAnalyticQuadraticEdge::updateQuadratic() { int success = 0; // initialize to fail! int count = fCurveCount; - SkFixed oldx = fQx; - SkFixed oldy = fQy; - SkFixed dx = fQDx; - SkFixed dy = fQDy; + SkFixed oldx = fQEdge.fQx; + SkFixed oldy = fQEdge.fQy; + SkFixed dx = fQEdge.fQDx; + SkFixed dy = fQEdge.fQDy; SkFixed newx, newy, newSnappedX, newSnappedY; int shift = fCurveShift; @@ -469,22 +315,22 @@ bool SkAnalyticQuadraticEdge::updateQuadratic() { SkFDot6 diffY = SkFixedToFDot6(newy - fSnappedY); slope = diffY ? quick_div(SkFixedToFDot6(newx - fSnappedX), diffY) : SK_MaxS32; - newSnappedY = std::min<SkFixed>(fQLastY, SkFixedRoundToFixed(newy)); + newSnappedY = std::min<SkFixed>(fQEdge.fQLastY, SkFixedRoundToFixed(newy)); newSnappedX = newx - SkFixedMul(slope, newy - newSnappedY); } else { - newSnappedY = std::min(fQLastY, SnapY(newy)); + newSnappedY = std::min(fQEdge.fQLastY, SnapY(newy)); newSnappedX = newx; SkFDot6 diffY = SkFixedToFDot6(newSnappedY - fSnappedY); slope = diffY ? quick_div(SkFixedToFDot6(newx - fSnappedX), diffY) : SK_MaxS32; } - dx += fQDDx; - dy += fQDDy; + dx += fQEdge.fQDDx; + dy += fQEdge.fQDDy; } else // last segment { - newx = fQLastX; - newy = fQLastY; + newx = fQEdge.fQLastX; + newy = fQEdge.fQLastY; newSnappedY = newy; newSnappedX = newx; SkFDot6 diffY = SkFixedToFDot6(newy - fSnappedY); @@ -497,170 +343,52 @@ bool SkAnalyticQuadraticEdge::updateQuadratic() { oldy = newy; } while (count > 0 && !success); - SkASSERT(newSnappedY <= fQLastY); + SkASSERT(newSnappedY <= fQEdge.fQLastY); - fQx = newx; - fQy = newy; - fQDx = dx; - fQDy = dy; + fQEdge.fQx = newx; + fQEdge.fQy = newy; + fQEdge.fQDx = dx; + fQEdge.fQDy = dy; fSnappedX = newSnappedX; fSnappedY = newSnappedY; fCurveCount = SkToS8(count); return success; } -bool SkAnalyticCubicEdge::setCubic(const SkPoint pts[4]) { - if (!setCubicWithoutUpdate(pts, kDefaultAccuracy)) { +bool SkAnalyticCubicEdge::setCubic(const SkPoint pts[4], bool sortY) { + if (!fCEdge.setCubicWithoutUpdate(pts, kDefaultAccuracy, sortY)) { return false; } - fCx >>= kDefaultAccuracy; - fCy >>= kDefaultAccuracy; - fCDx >>= kDefaultAccuracy; - fCDy >>= kDefaultAccuracy; - fCDDx >>= kDefaultAccuracy; - fCDDy >>= kDefaultAccuracy; - fCDDDx >>= kDefaultAccuracy; - fCDDDy >>= kDefaultAccuracy; - fCLastX >>= kDefaultAccuracy; - fCLastY >>= kDefaultAccuracy; - fCy = SnapY(fCy); - fSnappedY = fCy; - fCLastY = SnapY(fCLastY); - + fCEdge.fCx >>= kDefaultAccuracy; + fCEdge.fCy >>= kDefaultAccuracy; + fCEdge.fCDx >>= kDefaultAccuracy; + fCEdge.fCDy >>= kDefaultAccuracy; + fCEdge.fCDDx >>= kDefaultAccuracy; + fCEdge.fCDDy >>= kDefaultAccuracy; + fCEdge.fCDDDx >>= kDefaultAccuracy; + fCEdge.fCDDDy >>= kDefaultAccuracy; + fCEdge.fCLastX >>= kDefaultAccuracy; + fCEdge.fCLastY >>= kDefaultAccuracy; + fCEdge.fCy = SnapY(fCEdge.fCy); + fCEdge.fCLastY = SnapY(fCEdge.fCLastY); + + fWinding = fCEdge.fWinding; fEdgeType = Type::kCubic; + fCurveCount = fCEdge.fCurveCount; + fCurveShift = fCEdge.fCurveShift; + fCubicDShift = fCEdge.fCubicDShift; - return this->updateCubic(); -} - -static inline int SkFDot6UpShift(SkFDot6 x, int upShift) { - SkASSERT((SkLeftShift(x, upShift) >> upShift) == x); - return SkLeftShift(x, upShift); -} - -/* f(1/3) = (8a + 12b + 6c + d) / 27 - f(2/3) = (a + 6b + 12c + 8d) / 27 - - f(1/3)-b = (8a - 15b + 6c + d) / 27 - f(2/3)-c = (a + 6b - 15c + 8d) / 27 - - use 16/512 to approximate 1/27 -*/ -static SkFDot6 cubic_delta_from_line(SkFDot6 a, SkFDot6 b, SkFDot6 c, SkFDot6 d) -{ - // since our parameters may be negative, we don't use << to avoid ASAN warnings - SkFDot6 oneThird = (a*8 - b*15 + 6*c + d) * 19 >> 9; - SkFDot6 twoThird = (a + 6*b - c*15 + d*8) * 19 >> 9; + fSnappedY = fCEdge.fCy; - return std::max(SkAbs32(oneThird), SkAbs32(twoThird)); -} - -bool SkAnalyticCubicEdge::setCubicWithoutUpdate(const SkPoint pts[4], int shift) { - SkFDot6 x0, y0, x1, y1, x2, y2, x3, y3; - - { -#ifdef SK_RASTERIZE_EVEN_ROUNDING - x0 = SkScalarRoundToFDot6(pts[0].fX, shift); - y0 = SkScalarRoundToFDot6(pts[0].fY, shift); - x1 = SkScalarRoundToFDot6(pts[1].fX, shift); - y1 = SkScalarRoundToFDot6(pts[1].fY, shift); - x2 = SkScalarRoundToFDot6(pts[2].fX, shift); - y2 = SkScalarRoundToFDot6(pts[2].fY, shift); - x3 = SkScalarRoundToFDot6(pts[3].fX, shift); - y3 = SkScalarRoundToFDot6(pts[3].fY, shift); -#else - float scale = float(1 << (shift + 6)); - x0 = int(pts[0].fX * scale); - y0 = int(pts[0].fY * scale); - x1 = int(pts[1].fX * scale); - y1 = int(pts[1].fY * scale); - x2 = int(pts[2].fX * scale); - y2 = int(pts[2].fY * scale); - x3 = int(pts[3].fX * scale); - y3 = int(pts[3].fY * scale); -#endif - } - - Winding winding = Winding::kCW; - if (y0 > y3) - { - using std::swap; - swap(x0, x3); - swap(x1, x2); - swap(y0, y3); - swap(y1, y2); - winding = Winding::kCCW; - } - - int top = SkFDot6Round(y0); - int bot = SkFDot6Round(y3); - - // are we a zero-height cubic (line)? - if (top == bot) - return 0; - - // compute number of steps needed (1 << shift) - { - // Can't use (center of curve - center of baseline), since center-of-curve - // need not be the max delta from the baseline (it could even be coincident) - // so we try just looking at the two off-curve points - SkFDot6 dx = cubic_delta_from_line(x0, x1, x2, x3); - SkFDot6 dy = cubic_delta_from_line(y0, y1, y2, y3); - // add 1 (by observation) - shift = diff_to_shift(dx, dy, 2) + 1; - } - // need at least 1 subdivision for our bias trick - SkASSERT(shift > 0); - if (shift > MAX_COEFF_SHIFT) { - shift = MAX_COEFF_SHIFT; - } - - /* Since our in coming data is initially shifted down by 10 (or 8 in - antialias). That means the most we can shift up is 8. However, we - compute coefficients with a 3*, so the safest upshift is really 6 - */ - int upShift = 6; // largest safe value - int downShift = shift + upShift - 10; - if (downShift < 0) { - downShift = 0; - upShift = 10 - shift; - } - - fWinding = winding; - fEdgeType = Type::kCubic; - fCurveCount = SkToS8(SkLeftShift(-1, shift)); - fCurveShift = SkToU8(shift); - fCubicDShift = SkToU8(downShift); - - SkFixed B = SkFDot6UpShift(3 * (x1 - x0), upShift); - SkFixed C = SkFDot6UpShift(3 * (x0 - x1 - x1 + x2), upShift); - SkFixed D = SkFDot6UpShift(x3 + 3 * (x1 - x2) - x0, upShift); - - fCx = SkFDot6ToFixed(x0); - fCDx = B + (C >> shift) + (D >> 2*shift); // biased by shift - fCDDx = 2*C + (3*D >> (shift - 1)); // biased by 2*shift - fCDDDx = 3*D >> (shift - 1); // biased by 2*shift - - B = SkFDot6UpShift(3 * (y1 - y0), upShift); - C = SkFDot6UpShift(3 * (y0 - y1 - y1 + y2), upShift); - D = SkFDot6UpShift(y3 + 3 * (y1 - y2) - y0, upShift); - - fCy = SkFDot6ToFixed(y0); - fCDy = B + (C >> shift) + (D >> 2*shift); // biased by shift - fCDDy = 2*C + (3*D >> (shift - 1)); // biased by 2*shift - fCDDDy = 3*D >> (shift - 1); // biased by 2*shift - - fCLastX = SkFDot6ToFixed(x3); - fCLastY = SkFDot6ToFixed(y3); - - return true; + return this->updateCubic(sortY); } -bool SkAnalyticCubicEdge::updateCubic() { +bool SkAnalyticCubicEdge::updateCubic(bool sortY) { int success; int count = fCurveCount; - SkFixed oldx = fCx; - SkFixed oldy = fCy; + SkFixed oldx = fCEdge.fCx; + SkFixed oldy = fCEdge.fCy; SkFixed newx, newy; const int ddshift = fCurveShift; const int dshift = fCubicDShift; @@ -669,30 +397,30 @@ bool SkAnalyticCubicEdge::updateCubic() { do { if (++count < 0) { - newx = oldx + (fCDx >> dshift); - fCDx += fCDDx >> ddshift; - fCDDx += fCDDDx; + newx = oldx + (fCEdge.fCDx >> dshift); + fCEdge.fCDx += fCEdge.fCDDx >> ddshift; + fCEdge.fCDDx += fCEdge.fCDDDx; - newy = oldy + (fCDy >> dshift); - fCDy += fCDDy >> ddshift; - fCDDy += fCDDDy; + newy = oldy + (fCEdge.fCDy >> dshift); + fCEdge.fCDy += fCEdge.fCDDy >> ddshift; + fCEdge.fCDDy += fCEdge.fCDDDy; } else { // last segment - newx = fCLastX; - newy = fCLastY; + newx = fCEdge.fCLastX; + newy = fCEdge.fCLastY; } // we want to say SkASSERT(oldy <= newy), but our finite fixedpoint // doesn't always achieve that, so we have to explicitly pin it here. - if (newy < oldy) { + if (sortY && newy < oldy) { newy = oldy; } SkFixed newSnappedY = SnapY(newy); - // we want to SkASSERT(snappedNewY <= fCLastY), but our finite fixedpoint + // we want to SkASSERT(snappedNewY <= fCEdge.fCLastY), but our finite fixedpoint // doesn't always achieve that, so we have to explicitly pin it here. - if (fCLastY < newSnappedY) { - newSnappedY = fCLastY; + if (sortY && fCEdge.fCLastY < newSnappedY) { + newSnappedY = fCEdge.fCLastY; count = 0; } @@ -708,8 +436,8 @@ bool SkAnalyticCubicEdge::updateCubic() { fSnappedY = newSnappedY; } while (count < 0 && !success); - fCx = newx; - fCy = newy; + fCEdge.fCx = newx; + fCEdge.fCy = newy; fCurveCount = SkToS8(count); return success; } diff --git a/gfx/skia/skia/src/core/SkAnalyticEdge.h b/gfx/skia/skia/src/core/SkAnalyticEdge.h @@ -12,6 +12,7 @@ #include "include/private/base/SkDebug.h" #include "include/private/base/SkFixed.h" #include "include/private/base/SkSafe32.h" +#include "src/core/SkEdge.h" #include <cstdint> @@ -19,15 +20,8 @@ struct SkPoint; struct SkAnalyticEdge { // Similar to SkEdge, the conic edges will be converted to quadratic edges - enum class Type : int8_t { - kLine, - kQuad, - kCubic, - }; - enum class Winding : int8_t { - kCW = 1, // clockwise - kCCW = -1, // counter clockwise - }; + using Type = SkEdge::Type; + using Winding = SkEdge::Winding; SkAnalyticEdge* fNext; SkAnalyticEdge* fPrev; @@ -45,6 +39,7 @@ struct SkAnalyticEdge { 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 Winding fWinding; static constexpr int kDefaultAccuracy = 2; // default accuracy for snapping @@ -79,7 +74,7 @@ struct SkAnalyticEdge { bool updateLine(SkFixed ax, SkFixed ay, SkFixed bx, SkFixed by, SkFixed slope); // return true if we're NOT done with this edge - bool update(SkFixed last_y); + bool update(SkFixed last_y, bool sortY = true); #ifdef SK_DEBUG void dump() const { @@ -104,15 +99,11 @@ struct SkAnalyticEdge { }; struct SkAnalyticQuadraticEdge : public SkAnalyticEdge { - SkFixed fQx, fQy; - SkFixed fQDx, fQDy; - SkFixed fQDDx, fQDDy; - SkFixed fQLastX, fQLastY; + SkQuadraticEdge fQEdge; // snap y to integer points in the middle of the curve to accelerate AAA path filling SkFixed fSnappedX, fSnappedY; - bool setQuadraticWithoutUpdate(const SkPoint pts[3], int shiftUp); bool setQuadratic(const SkPoint pts[3]); bool updateQuadratic(); inline void keepContinuous() { @@ -126,22 +117,15 @@ struct SkAnalyticQuadraticEdge : public SkAnalyticEdge { }; struct SkAnalyticCubicEdge : public SkAnalyticEdge { - SkFixed fCx, fCy; - SkFixed fCDx, fCDy; - SkFixed fCDDx, fCDDy; - SkFixed fCDDDx, fCDDDy; - SkFixed fCLastX, fCLastY; + SkCubicEdge fCEdge; SkFixed fSnappedY; // to make sure that y is increasing with smooth jump and snapping - uint8_t fCubicDShift; // applied to fCDx and fCDy - - bool setCubicWithoutUpdate(const SkPoint pts[4], int shiftUp); - bool setCubic(const SkPoint pts[4]); - bool updateCubic(); + bool setCubic(const SkPoint pts[4], bool sortY = true); + bool updateCubic(bool sortY = true); inline void keepContinuous() { - SkASSERT(SkAbs32(fX - SkFixedMul(fDX, fY - SnapY(fCy)) - fCx) < SK_Fixed1); - fCx = fX; + SkASSERT(SkAbs32(fX - SkFixedMul(fDX, fY - SnapY(fCEdge.fCy)) - fCEdge.fCx) < SK_Fixed1); + fCEdge.fCx = fX; fSnappedY = fY; } }; diff --git a/gfx/skia/skia/src/core/SkAutoBlitterChoose.h b/gfx/skia/skia/src/core/SkAutoBlitterChoose.h @@ -11,7 +11,7 @@ #include "include/private/base/SkMacros.h" #include "src/base/SkArenaAlloc.h" #include "src/core/SkBlitter.h" -#include "src/core/SkDraw.h" +#include "src/core/SkDrawBase.h" #include "src/core/SkRasterClip.h" #include "src/core/SkSurfacePriv.h" @@ -19,32 +19,22 @@ class SkMatrix; class SkPaint; class SkPixmap; -// 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(). -using SkBlitterSizedArena = SkSTArenaAlloc<2736>; - class SkAutoBlitterChoose : SkNoncopyable { public: SkAutoBlitterChoose() {} - SkAutoBlitterChoose(const skcpu::Draw& draw, + SkAutoBlitterChoose(const SkDrawBase& draw, const SkMatrix* ctm, const SkPaint& paint, - const SkRect& devBounds, SkDrawCoverage drawCoverage = SkDrawCoverage::kNo) { - this->choose(draw, ctm, paint, devBounds, drawCoverage); + this->choose(draw, ctm, paint, drawCoverage); } SkBlitter* operator->() { return fBlitter; } SkBlitter* get() const { return fBlitter; } - SkBlitter* choose(const skcpu::Draw& draw, + SkBlitter* choose(const SkDrawBase& draw, const SkMatrix* ctm, const SkPaint& paint, - const SkRect& devBounds, SkDrawCoverage drawCoverage = SkDrawCoverage::kNo) { SkASSERT(!fBlitter); fBlitter = draw.fBlitterChooser(draw.fDst, @@ -53,8 +43,7 @@ public: &fAlloc, drawCoverage, draw.fRC->clipShader(), - SkSurfacePropsCopyOrDefault(draw.fProps), - devBounds); + SkSurfacePropsCopyOrDefault(draw.fProps)); return fBlitter; } @@ -62,7 +51,14 @@ private: // Owned by fAlloc, which will handle the delete. SkBlitter* fBlitter = nullptr; - SkBlitterSizedArena 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<kStackMemory> fAlloc; }; #endif diff --git a/gfx/skia/skia/src/core/SkBitmap.cpp b/gfx/skia/skia/src/core/SkBitmap.cpp @@ -659,7 +659,7 @@ sk_sp<SkShader> SkBitmap::makeShader(const SkSamplingOptions& sampling, sk_sp<SkShader> SkBitmap::makeShader(SkTileMode tmx, SkTileMode tmy, const SkSamplingOptions& sampling, const SkMatrix& lm) const { - if (!lm.invert()) { + if (!lm.invert(nullptr)) { return nullptr; } return SkImageShader::Make(SkMakeImageFromRasterBitmap(*this, kIfMutable_SkCopyPixelsMode), @@ -669,7 +669,7 @@ sk_sp<SkShader> SkBitmap::makeShader(SkTileMode tmx, SkTileMode tmy, sk_sp<SkShader> SkBitmap::makeShader(SkTileMode tmx, SkTileMode tmy, const SkSamplingOptions& sampling, const SkMatrix* lm) const { - if (lm && !lm->invert()) { + if (lm && !lm->invert(nullptr)) { return nullptr; } return SkImageShader::Make(SkMakeImageFromRasterBitmap(*this, kIfMutable_SkCopyPixelsMode), diff --git a/gfx/skia/skia/src/core/SkBitmapDevice.cpp b/gfx/skia/skia/src/core/SkBitmapDevice.cpp @@ -9,7 +9,6 @@ #include "include/core/SkAlphaType.h" #include "include/core/SkBlender.h" -#include "include/core/SkCPURecorder.h" #include "include/core/SkClipOp.h" #include "include/core/SkColorType.h" #include "include/core/SkImageInfo.h" @@ -29,10 +28,9 @@ #include "include/core/SkTileMode.h" #include "include/private/base/SkAssert.h" #include "include/private/base/SkTo.h" -#include "src/core/SkCPURecorderImpl.h" +#include "src/base/SkTLazy.h" #include "src/core/SkDraw.h" #include "src/core/SkImagePriv.h" -#include "src/core/SkMaskFilterBase.h" #include "src/core/SkMatrixPriv.h" #include "src/core/SkRasterClip.h" #include "src/core/SkSpecialImage.h" @@ -67,12 +65,12 @@ class SkDrawTiler { SkIRect fSrcBounds; // Used for tiling and non-tiling - skcpu::Draw fDraw; + SkDraw fDraw; // fTileMatrix... are only used if fNeedTiling - std::optional<SkMatrix> fTileMatrix; - SkRasterClip fTileRC; - SkIPoint fOrigin; + SkTLazy<SkMatrix> fTileMatrix; + SkRasterClip fTileRC; + SkIPoint fOrigin; bool fDone, fNeedsTiling; @@ -85,13 +83,13 @@ public: fDone = false; // we need fDst to be set, and if we're actually drawing, to dirty the genID - if (!fDevice->accessPixels(&fRootPixmap)) { + if (!dev->accessPixels(&fRootPixmap)) { // NoDrawDevice uses us (why?) so we have to catch this case w/ no pixels - fRootPixmap.reset(fDevice->imageInfo(), nullptr, 0); + fRootPixmap.reset(dev->imageInfo(), nullptr, 0); } // do a quick check, so we don't even have to process "bounds" if there is no need - const SkIRect clipR = fDevice->fRCStack.rc().getBounds(); + const SkIRect clipR = dev->fRCStack.rc().getBounds(); fNeedsTiling = clipR.right() > kMaxDim || clipR.bottom() > kMaxDim; if (fNeedsTiling) { if (bounds) { @@ -107,7 +105,7 @@ public: // fSrcBounds = devBounds.roundOut(); // The problem being that the promotion of clipR to SkRect was unreliable // - fSrcBounds = fDevice->localToDevice().mapRect(*bounds).roundOut(); + fSrcBounds = dev->localToDevice().mapRect(*bounds).roundOut(); if (fSrcBounds.intersect(clipR)) { // Check again, now that we have computed srcbounds. fNeedsTiling = fSrcBounds.right() > kMaxDim || fSrcBounds.bottom() > kMaxDim; @@ -128,20 +126,17 @@ public: } else { // don't reference fSrcBounds, as it may not have been set fDraw.fDst = fRootPixmap; - fDraw.fCTM = &fDevice->localToDevice(); - fDraw.fRC = &fDevice->fRCStack.rc(); + fDraw.fCTM = &dev->localToDevice(); + fDraw.fRC = &dev->fRCStack.rc(); fOrigin.set(0, 0); } fDraw.fProps = &fDevice->surfaceProps(); - if (fDevice->fRecorder) { - fDraw.fCtx = fDevice->fRecorder->ctx(); - } } bool needsTiling() const { return fNeedsTiling; } - const skcpu::Draw* next() { + const SkDraw* next() { if (fDone) { return nullptr; } @@ -183,9 +178,9 @@ private: SkASSERT_RELEASE(success); // now don't use bounds, since fDst has the clipped dimensions. - fTileMatrix = fDevice->localToDevice(); + fTileMatrix.init(fDevice->localToDevice()); fTileMatrix->postTranslate(-fOrigin.x(), -fOrigin.y()); - fDraw.fCTM = &fTileMatrix.value(); + fDraw.fCTM = fTileMatrix.get(); fDevice->fRCStack.rc().translate(-fOrigin.x(), -fOrigin.y(), &fTileRC); fTileRC.op(SkIRect::MakeSize(fDraw.fDst.dimensions()), SkClipOp::kIntersect); } @@ -195,14 +190,14 @@ private: // drawing. If null is passed, the tiler has to visit everywhere. The bounds is expected to be // in local coordinates, as the tiler itself will transform that into device coordinates. // -#define LOOP_TILER(code, boundsPtr) \ - SkDrawTiler priv_tiler(this, boundsPtr); \ - while (const skcpu::Draw* priv_draw = priv_tiler.next()) { \ - priv_draw->code; \ +#define LOOP_TILER(code, boundsPtr) \ + SkDrawTiler priv_tiler(this, boundsPtr); \ + while (const SkDraw* priv_draw = priv_tiler.next()) { \ + priv_draw->code; \ } -// Helper to create an skcpu::Draw from a device -class SkBitmapDevice::BDDraw : public skcpu::Draw { +// Helper to create an SkDraw from a device +class SkBitmapDevice::BDDraw : public SkDraw { public: BDDraw(SkBitmapDevice* dev) { // we need fDst to be set, and if we're actually drawing, to dirty the genID @@ -230,30 +225,18 @@ static bool valid_for_bitmap_device(const SkImageInfo& info, } SkBitmapDevice::SkBitmapDevice(const SkBitmap& bitmap) - : SkBitmapDevice(asRRI(skcpu::Recorder::TODO()), bitmap) {} - -SkBitmapDevice::SkBitmapDevice(const SkBitmap& bitmap, - const SkSurfaceProps& surfaceProps, - SkRasterHandleAllocator::Handle hndl) - : SkBitmapDevice(asRRI(skcpu::Recorder::TODO()), bitmap, surfaceProps, hndl) {} - -SkBitmapDevice::SkBitmapDevice(skcpu::RecorderImpl* recorder, const SkBitmap& bitmap) : SkDevice(bitmap.info(), SkSurfaceProps()) - , fRecorder(recorder) , fBitmap(bitmap) , fRCStack(bitmap.width(), bitmap.height()) , fGlyphPainter(this->surfaceProps(), bitmap.colorType(), bitmap.colorSpace()) { SkASSERT(valid_for_bitmap_device(bitmap.info(), nullptr)); } -SkBitmapDevice::SkBitmapDevice(skcpu::RecorderImpl* recorder, - const SkBitmap& bitmap, - const SkSurfaceProps& surfaceProps, +SkBitmapDevice::SkBitmapDevice(const SkBitmap& bitmap, const SkSurfaceProps& surfaceProps, SkRasterHandleAllocator::Handle hndl) : SkDevice(bitmap.info(), surfaceProps) - , fRasterHandle(hndl) - , fRecorder(recorder) , fBitmap(bitmap) + , fRasterHandle(hndl) , fRCStack(bitmap.width(), bitmap.height()) , fGlyphPainter(this->surfaceProps(), bitmap.colorType(), bitmap.colorSpace()) { SkASSERT(valid_for_bitmap_device(bitmap.info(), nullptr)); @@ -359,9 +342,9 @@ void SkBitmapDevice::drawPaint(const SkPaint& paint) { BDDraw(this).drawPaint(paint); } -void SkBitmapDevice::drawPoints(SkCanvas::PointMode mode, SkSpan<const SkPoint> pts, - const SkPaint& paint) { - LOOP_TILER( drawPoints(mode, pts, paint, nullptr), nullptr) +void SkBitmapDevice::drawPoints(SkCanvas::PointMode mode, size_t count, + const SkPoint pts[], const SkPaint& paint) { + LOOP_TILER( drawPoints(mode, count, pts, paint, nullptr), nullptr) } void SkBitmapDevice::drawRect(const SkRect& r, const SkPaint& paint) { @@ -369,7 +352,7 @@ void SkBitmapDevice::drawRect(const SkRect& r, const SkPaint& paint) { } void SkBitmapDevice::drawOval(const SkRect& oval, const SkPaint& paint) { - LOOP_TILER( drawOval(oval, paint), Bounder(oval, paint)) + this->drawPath(SkPath::Oval(oval), paint, true); } void SkBitmapDevice::drawRRect(const SkRRect& rrect, const SkPaint& paint) { @@ -387,7 +370,7 @@ void SkBitmapDevice::drawPath(const SkPath& path, if (tiler.needsTiling()) { pathIsMutable = false; } - while (const skcpu::Draw* draw = tiler.next()) { + while (const SkDraw* draw = tiler.next()) { draw->drawPath(path, paint, nullptr, pathIsMutable); } } @@ -441,7 +424,7 @@ void SkBitmapDevice::drawImageRect(const SkImage* image, const SkRect* src, cons } else { tmpSrc = bitmapBounds; } - SkMatrix matrix = SkMatrix::RectToRectOrIdentity(tmpSrc, dst); + SkMatrix matrix = SkMatrix::RectToRect(tmpSrc, dst); const SkRect* dstPtr = &dst; const SkBitmap* bitmapPtr = &bitmap; @@ -558,12 +541,18 @@ void SkBitmapDevice::drawMesh(const SkMesh&, sk_sp<SkBlender>, const SkPaint&) { // TODO: Implement, maybe with a subclass of BitmapDevice that has SkSL support. } -void SkBitmapDevice::drawAtlas(SkSpan<const SkRSXform> xform, - SkSpan<const SkRect> tex, - SkSpan<const SkColor> colors, +void SkBitmapDevice::drawAtlas(const SkRSXform xform[], + const SkRect tex[], + const SkColor colors[], + int count, sk_sp<SkBlender> blender, const SkPaint& paint) { - BDDraw(this).drawAtlas(xform, tex, colors, std::move(blender), paint); + // set this to true for performance comparisons with the old drawVertices way + if ((false)) { + this->SkDevice::drawAtlas(xform, tex, colors, count, std::move(blender), paint); + return; + } + BDDraw(this).drawAtlas(xform, tex, colors, count, std::move(blender), paint); } /////////////////////////////////////////////////////////////////////////////// @@ -580,7 +569,7 @@ void SkBitmapDevice::drawSpecial(SkSpecialImage* src, SkBitmap resultBM; if (SkSpecialImages::AsBitmap(src, &resultBM)) { - skcpu::Draw draw; + SkDraw draw; if (!this->accessPixels(&draw.fDst)) { return; // no pixels to draw to so skip it } @@ -590,45 +579,6 @@ void SkBitmapDevice::drawSpecial(SkSpecialImage* src, } } -void SkBitmapDevice::drawCoverageMask(const SkSpecialImage* mask, - const SkMatrix& maskToDevice, - const SkSamplingOptions& sampling, - const SkPaint& paint) { - SkASSERT(!mask->isGaneshBacked()); - SkASSERT(!mask->isGraphiteBacked()); - - SkBitmap maskBM; - if (!SkSpecialImages::AsBitmap(mask, &maskBM)) { - return; - } - - skcpu::Draw draw; - if (!this->accessPixels(&draw.fDst)) { - return; // no pixels to draw to so skip it - } - draw.fRC = &fRCStack.rc(); - draw.fCTM = &maskToDevice; - draw.drawBitmapAsMask(maskBM, sampling, paint, &this->localToDevice()); -} - -// Try to filter as nine patch as a fast path. -bool SkBitmapDevice::drawBlurredRRect(const SkRRect& rrect, const SkPaint& paint, float) { - SkASSERT(paint.getMaskFilter() - && as_MFB(paint.getMaskFilter())->type() == SkMaskFilterBase::Type::kBlur); - - SkDrawTiler tiler(this, Bounder(rrect.getBounds(), paint)); - // Check if the tiler only needs one iteration (common case). If there are multiple - // tiles, we just return false and fall back to the general mask filter path as we - // don't want to be in the scenario where only a subset fail/succeed. - if (!tiler.needsTiling()) { - if (const skcpu::Draw* draw = tiler.next()) { - return draw->drawRRectNinePatch(rrect, paint); - } - } - - return false; -} - sk_sp<SkSpecialImage> SkBitmapDevice::snapSpecial(const SkIRect& bounds, bool forceCopy) { if (forceCopy) { return SkSpecialImages::CopyFromRaster(bounds, fBitmap, this->surfaceProps()); diff --git a/gfx/skia/skia/src/core/SkBitmapDevice.h b/gfx/skia/skia/src/core/SkBitmapDevice.h @@ -14,12 +14,12 @@ #include "include/core/SkRect.h" #include "include/core/SkRefCnt.h" #include "include/core/SkSamplingOptions.h" -#include "include/core/SkSpan.h" -#include "src/core/SkCPURecorderImpl.h" #include "src/core/SkDevice.h" #include "src/core/SkGlyphRunPainter.h" #include "src/core/SkRasterClipStack.h" +#include <cstddef> + class SkBlender; class SkImage; class SkMatrix; @@ -29,7 +29,6 @@ class SkPath; class SkPixmap; class SkRRect; class SkRasterHandleAllocator; -class SkRecorder; class SkRegion; class SkShader; class SkSpecialImage; @@ -37,10 +36,10 @@ class SkSurface; class SkSurfaceProps; class SkVertices; enum class SkClipOp; +namespace sktext { class GlyphRunList; } struct SkImageInfo; struct SkPoint; struct SkRSXform; -namespace sktext { class GlyphRunList; } /////////////////////////////////////////////////////////////////////////////// class SkBitmapDevice final : public SkDevice { @@ -60,17 +59,12 @@ public: SkBitmapDevice(const SkBitmap& bitmap, const SkSurfaceProps& surfaceProps, void* externalHandle = nullptr); - SkBitmapDevice(skcpu::RecorderImpl*, const SkBitmap& bitmap); - SkBitmapDevice(skcpu::RecorderImpl*, - const SkBitmap& bitmap, - const SkSurfaceProps& surfaceProps, - void* externalHandle = nullptr); - static sk_sp<SkBitmapDevice> Create(const SkImageInfo&, const SkSurfaceProps&, SkRasterHandleAllocator* = nullptr); void drawPaint(const SkPaint& paint) override; - void drawPoints(SkCanvas::PointMode, SkSpan<const SkPoint>, const SkPaint&) override; + void drawPoints(SkCanvas::PointMode mode, size_t count, + const SkPoint[], const SkPaint& paint) override; void drawRect(const SkRect& r, const SkPaint& paint) override; void drawOval(const SkRect& oval, const SkPaint& paint) override; void drawRRect(const SkRRect& rr, const SkPaint& paint) override; @@ -85,8 +79,8 @@ public: // Implemented in src/sksl/SkBitmapDevice_mesh.cpp void drawMesh(const SkMesh&, sk_sp<SkBlender>, const SkPaint&) override; - void drawAtlas(SkSpan<const SkRSXform>, SkSpan<const SkRect>, SkSpan<const SkColor>, - sk_sp<SkBlender>, const SkPaint&) override; + void drawAtlas(const SkRSXform[], const SkRect[], const SkColor[], int count, sk_sp<SkBlender>, + const SkPaint&) override; /////////////////////////////////////////////////////////////////////////// @@ -109,11 +103,6 @@ public: void drawSpecial(SkSpecialImage*, const SkMatrix&, const SkSamplingOptions&, const SkPaint&, SkCanvas::SrcRectConstraint) override; - void drawCoverageMask(const SkSpecialImage*, const SkMatrix&, const SkSamplingOptions&, - const SkPaint&) override; - - bool drawBlurredRRect(const SkRRect&, const SkPaint&, float) override; - sk_sp<SkSpecialImage> snapSpecial(const SkIRect&, bool forceCopy = false) override; sk_sp<SkDevice> createDevice(const CreateInfo&, const SkPaint*) override; @@ -124,9 +113,9 @@ public: void* getRasterHandle() const override { return fRasterHandle; } - SkRecorder* baseRecorder() const override { return fRecorder; } - private: + friend class SkDraw; + friend class SkDrawBase; friend class SkDrawTiler; friend class SkSurface_Raster; @@ -148,11 +137,10 @@ private: void drawBitmap(const SkBitmap&, const SkMatrix&, const SkRect* dstOrNull, const SkSamplingOptions&, const SkPaint&); - void* fRasterHandle = nullptr; - skcpu::RecorderImpl* fRecorder = nullptr; - SkBitmap fBitmap; - SkRasterClipStack fRCStack; - skcpu::GlyphRunListPainter fGlyphPainter; + SkBitmap fBitmap; + void* fRasterHandle = nullptr; + SkRasterClipStack fRCStack; + SkGlyphRunListPainterCPU fGlyphPainter; }; #endif // SkBitmapDevice_DEFINED diff --git a/gfx/skia/skia/src/core/SkBitmapProcState.cpp b/gfx/skia/skia/src/core/SkBitmapProcState.cpp @@ -14,7 +14,6 @@ #include "include/private/base/SkMacros.h" #include "include/private/base/SkTPin.h" #include "src/core/SkColorPriv.h" -#include "src/core/SkMatrixPriv.h" #include "src/core/SkMemset.h" #include "src/core/SkMipmapAccessor.h" @@ -38,29 +37,29 @@ static void Clamp_S32_opaque_D32_nofilter_DX_shaderproc(const void* sIn, int x, SkASSERT(s.fAlphaScale == 256); const unsigned maxX = s.fPixmap.width() - 1; - SkFixed3232 fx; + SkFractionalInt fx; int dstY; { const SkBitmapProcStateAutoMapper mapper(s, x, y); const unsigned maxY = s.fPixmap.height() - 1; dstY = SkTPin<int>(mapper.intY(), 0, maxY); - fx = mapper.fixed3232X(); + fx = mapper.fractionalIntX(); } const SkPMColor* src = s.fPixmap.addr32(0, dstY); - const SkFixed3232 dx = s.fInvSx; + const SkFractionalInt dx = s.fInvSxFractionalInt; // Check if we're safely inside [0...maxX] so no need to clamp each computed index. // - if ((uint64_t)SkFixed3232ToInt(fx) <= maxX && - (uint64_t)SkFixed3232ToInt(fx + dx * (count - 1)) <= maxX) + if ((uint64_t)SkFractionalIntToInt(fx) <= maxX && + (uint64_t)SkFractionalIntToInt(fx + dx * (count - 1)) <= maxX) { int count4 = count >> 2; for (int i = 0; i < count4; ++i) { - SkPMColor src0 = src[SkFixed3232ToInt(fx)]; fx += dx; - SkPMColor src1 = src[SkFixed3232ToInt(fx)]; fx += dx; - SkPMColor src2 = src[SkFixed3232ToInt(fx)]; fx += dx; - SkPMColor src3 = src[SkFixed3232ToInt(fx)]; fx += dx; + SkPMColor src0 = src[SkFractionalIntToInt(fx)]; fx += dx; + SkPMColor src1 = src[SkFractionalIntToInt(fx)]; fx += dx; + SkPMColor src2 = src[SkFractionalIntToInt(fx)]; fx += dx; + SkPMColor src3 = src[SkFractionalIntToInt(fx)]; fx += dx; dst[0] = src0; dst[1] = src1; dst[2] = src2; @@ -68,14 +67,14 @@ static void Clamp_S32_opaque_D32_nofilter_DX_shaderproc(const void* sIn, int x, dst += 4; } for (int i = (count4 << 2); i < count; ++i) { - unsigned index = SkFixed3232ToInt(fx); + unsigned index = SkFractionalIntToInt(fx); SkASSERT(index <= maxX); *dst++ = src[index]; fx += dx; } } else { for (int i = 0; i < count; ++i) { - dst[i] = src[SkTPin<int>(SkFixed3232ToInt(fx), 0, maxX)]; + dst[i] = src[SkTPin<int>(SkFractionalIntToInt(fx), 0, maxX)]; fx += dx; } } @@ -231,10 +230,9 @@ bool SkBitmapProcState::init(const SkMatrix& inv, SkAlpha paintAlpha, // if it's already pure translate then we won't do this inversion. if (matrix_only_scale_translate(fInvMatrix)) { - if (auto forward = fInvMatrix.invert()) { - if (just_trans_general(*forward)) { - fInvMatrix.setTranslate(-forward->getTranslateX(), -forward->getTranslateY()); - } + SkMatrix forward; + if (fInvMatrix.invert(&forward) && just_trans_general(forward)) { + fInvMatrix.setTranslate(-forward.getTranslateX(), -forward.getTranslateY()); } } @@ -269,8 +267,9 @@ bool SkBitmapProcState::chooseProcs() { SkASSERT(fTileModeX != SkTileMode::kDecal); - fInvSx = SkScalarToFixed3232(fInvMatrix.getScaleX()); - fInvKy = SkScalarToFixed3232(fInvMatrix.getSkewY ()); + fInvProc = SkMatrixPriv::GetMapXYProc(fInvMatrix); + fInvSxFractionalInt = SkScalarToFractionalInt(fInvMatrix.getScaleX()); + fInvKyFractionalInt = SkScalarToFractionalInt(fInvMatrix.getSkewY ()); fAlphaScale = SkAlpha255To256(fPaintAlpha); @@ -450,7 +449,7 @@ static void S32_D32_constX_shaderproc(const void* sIn, // bitmap's width and height. Since this method is going to do // its own tiling and sampling we need to undo that here. if (SkTileMode::kClamp != s.fTileModeX || SkTileMode::kClamp != s.fTileModeY) { - yTemp = SkFixed3232ToInt(mapper.fixed3232Y() * s.fPixmap.height()); + yTemp = SkFractionalIntToInt(mapper.fractionalIntY() * s.fPixmap.height()); } else { yTemp = mapper.intY(); } @@ -477,7 +476,7 @@ static void S32_D32_constX_shaderproc(const void* sIn, if (!s.fInvMatrix.isTranslate() && (SkTileMode::kClamp != s.fTileModeX || SkTileMode::kClamp != s.fTileModeY)) { - iY2 = SkFixed3232ToInt(mapper.fixed3232Y() * s.fPixmap.height()); + iY2 = SkFractionalIntToInt(mapper.fractionalIntY() * s.fPixmap.height()); } else { iY2 = mapper.intY(); } diff --git a/gfx/skia/skia/src/core/SkBitmapProcState.h b/gfx/skia/skia/src/core/SkBitmapProcState.h @@ -19,6 +19,7 @@ #include "include/private/base/SkDebug.h" #include "include/private/base/SkFixed.h" #include "src/base/SkArenaAlloc.h" +#include "src/core/SkMatrixPriv.h" #include <cstddef> #include <cstdint> @@ -26,6 +27,12 @@ class SkImage_Base; enum class SkTileMode; +typedef SkFixed3232 SkFractionalInt; +#define SkScalarToFractionalInt(x) SkScalarToFixed3232(x) +#define SkFractionalIntToFixed(x) SkFixed3232ToFixed(x) +#define SkFixedToFractionalInt(x) SkFixedToFixed3232(x) +#define SkFractionalIntToInt(x) SkFixed3232ToInt(x) + struct SkBitmapProcState { SkBitmapProcState(const SkImage_Base* image, SkTileMode tmx, SkTileMode tmy); @@ -34,17 +41,17 @@ struct SkBitmapProcState { && this->chooseProcs(); } - using ShaderProc32 = void (*)(const void* ctx, int x, int y, SkPMColor[], int count); + typedef void (*ShaderProc32)(const void* ctx, int x, int y, SkPMColor[], int count); - using MatrixProc = void (*)(const SkBitmapProcState&, - uint32_t bitmapXY[], - int count, - int x, int y); + typedef void (*MatrixProc)(const SkBitmapProcState&, + uint32_t bitmapXY[], + int count, + int x, int y); - using SampleProc32 = void (*)(const SkBitmapProcState&, - const uint32_t[], - int count, - SkPMColor colors[]); + typedef void (*SampleProc32)(const SkBitmapProcState&, + const uint32_t[], + int count, + SkPMColor colors[]); const SkImage_Base* fImage; @@ -55,13 +62,14 @@ struct SkBitmapProcState { SkTileMode fTileModeY; bool fBilerp; - SkFixed3232 fInvSx; - SkFixed3232 fInvKy; + SkMatrixPriv::MapXYProc fInvProc; // chooseProcs + SkFractionalInt fInvSxFractionalInt; + SkFractionalInt fInvKyFractionalInt; - SkFixed fFilterOneX; - SkFixed fFilterOneY; + SkFixed fFilterOneX; + SkFixed fFilterOneY; - uint16_t fAlphaScale; // chooseProcs + uint16_t fAlphaScale; // chooseProcs /** Given the byte size of the index buffer to be passed to the matrix proc, return the maximum number of resulting pixels that can be computed @@ -86,8 +94,9 @@ struct SkBitmapProcState { SampleProc32 getSampleProc32() const { return fSampleProc32; } private: - // found by inspection. If this is too small, we will allocate on the heap. - static constexpr size_t kBMStateSize = 136; + enum { + kBMStateSize = 136 // found by inspection. if too small, we will call new/delete + }; SkSTArenaAlloc<kBMStateSize> fAlloc; ShaderProc32 fShaderProc32; // chooseProcs @@ -133,7 +142,7 @@ private: #define pack_two_shorts(pri, sec) PACK_TWO_SHORTS(pri, sec) #endif -// Helper class for mapping the middle of pixel (x, y) into SkFixed3232 bitmap space. +// Helper class for mapping the middle of pixel (x, y) into SkFractionalInt bitmap space. // Discussion: // Overall, this code takes a point in destination space, and uses the center of the pixel // at (x, y) to determine the sample point in source space. It then adjusts the pixel by different @@ -153,10 +162,10 @@ class SkBitmapProcStateAutoMapper { public: SkBitmapProcStateAutoMapper(const SkBitmapProcState& s, int x, int y, SkPoint* scalarPoint = nullptr) { - SkPoint pt = s.fInvMatrix.mapPoint({ - SkIntToScalar(x) + SK_ScalarHalf, - SkIntToScalar(y) + SK_ScalarHalf, - }); + SkPoint pt; + s.fInvProc(s.fInvMatrix, + SkIntToScalar(x) + SK_ScalarHalf, + SkIntToScalar(y) + SK_ScalarHalf, &pt); SkFixed biasX = 0, biasY = 0; if (s.fBilerp) { @@ -172,10 +181,10 @@ public: } // punt to unsigned for defined underflow behavior - fX = (SkFixed3232)((uint64_t)SkScalarToFixed3232(pt.x()) - - (uint64_t)SkFixedToFixed3232(biasX)); - fY = (SkFixed3232)((uint64_t)SkScalarToFixed3232(pt.y()) - - (uint64_t)SkFixedToFixed3232(biasY)); + fX = (SkFractionalInt)((uint64_t)SkScalarToFractionalInt(pt.x()) - + (uint64_t)SkFixedToFractionalInt(biasX)); + fY = (SkFractionalInt)((uint64_t)SkScalarToFractionalInt(pt.y()) - + (uint64_t)SkFixedToFractionalInt(biasY)); if (scalarPoint) { scalarPoint->set(pt.x() - SkFixedToScalar(biasX), @@ -183,17 +192,17 @@ public: } } - SkFixed3232 fixed3232X() const { return fX; } - SkFixed3232 fixed3232Y() const { return fY; } + SkFractionalInt fractionalIntX() const { return fX; } + SkFractionalInt fractionalIntY() const { return fY; } - SkFixed fixedX() const { return SkFixed3232ToFixed(fX); } - SkFixed fixedY() const { return SkFixed3232ToFixed(fY); } + SkFixed fixedX() const { return SkFractionalIntToFixed(fX); } + SkFixed fixedY() const { return SkFractionalIntToFixed(fY); } - int intX() const { return SkFixed3232ToInt(fX); } - int intY() const { return SkFixed3232ToInt(fY); } + int intX() const { return SkFractionalIntToInt(fX); } + int intY() const { return SkFractionalIntToInt(fY); } private: - SkFixed3232 fX, fY; + SkFractionalInt fX, fY; }; namespace sktests { diff --git a/gfx/skia/skia/src/core/SkBitmapProcState_matrixProcs.cpp b/gfx/skia/skia/src/core/SkBitmapProcState_matrixProcs.cpp @@ -26,7 +26,7 @@ * 1. dx > 0 * 2. [fx, fx+dx, fx+2dx, fx+3dx, ... fx+(count-1)dx] are all <= maxX * - * In addition, we use SkFixed3232 to keep more precision than + * In addition, we use SkFractionalInt to keep more fractional precision than * just SkFixed, so we will abort the decal_ call if dx is very small, since * the decal_ function just operates on SkFixed. If that were changed, we could * skip the very_small test here. @@ -36,7 +36,7 @@ static inline bool can_truncate_to_fixed_for_decal(SkFixed fx, int count, unsigned max) { SkASSERT(count > 0); - // if decal_ kept SkFixed3232 precision, this would just be dx <= 0 + // if decal_ kept SkFractionalInt precision, this would just be dx <= 0 // I just made up the 1/256. Just don't want to perceive accumulated error // if we truncate frDx and lose its low bits. if (dx <= SK_Fixed1 / 256) { @@ -80,20 +80,18 @@ static void decal_nofilter_scale(uint32_t dst[], SkFixed fx, SkFixed dx, int cou } } -using TileProc = unsigned (*)(SkFixed, int); - // A generic implementation for unfiltered scale+translate, templated on tiling method. -template <TileProc tilex, TileProc tiley, bool tryDecal> +template <unsigned (*tilex)(SkFixed, int), unsigned (*tiley)(SkFixed, int), bool tryDecal> static void nofilter_scale(const SkBitmapProcState& s, uint32_t xy[], int count, int x, int y) { SkASSERT(s.fInvMatrix.isScaleTranslate()); // Write out our 32-bit y, and get our intial fx. - SkFixed3232 fx; + SkFractionalInt fx; { const SkBitmapProcStateAutoMapper mapper(s, x, y); *xy++ = tiley(mapper.fixedY(), s.fPixmap.height() - 1); - fx = mapper.fixed3232X(); + fx = mapper.fractionalIntX(); } const unsigned maxX = s.fPixmap.width() - 1; @@ -103,11 +101,11 @@ static void nofilter_scale(const SkBitmapProcState& s, return; } - const SkFixed3232 dx = s.fInvSx; + const SkFractionalInt dx = s.fInvSxFractionalInt; if (tryDecal) { - const SkFixed fixedFx = SkFixed3232ToFixed(fx); - const SkFixed fixedDx = SkFixed3232ToFixed(dx); + const SkFixed fixedFx = SkFractionalIntToFixed(fx); + const SkFixed fixedDx = SkFractionalIntToFixed(dx); if (can_truncate_to_fixed_for_decal(fixedFx, fixedDx, count, maxX)) { decal_nofilter_scale(xy, fixedFx, fixedDx, count); @@ -117,35 +115,35 @@ static void nofilter_scale(const SkBitmapProcState& s, // Remember, each x-coordinate is 16-bit. for (; count >= 2; count -= 2) { - *xy++ = pack_two_shorts(tilex(SkFixed3232ToFixed(fx ), maxX), - tilex(SkFixed3232ToFixed(fx + dx), maxX)); + *xy++ = pack_two_shorts(tilex(SkFractionalIntToFixed(fx ), maxX), + tilex(SkFractionalIntToFixed(fx + dx), maxX)); fx += dx+dx; } auto xx = (uint16_t*)xy; while (count --> 0) { - *xx++ = tilex(SkFixed3232ToFixed(fx), maxX); + *xx++ = tilex(SkFractionalIntToFixed(fx), maxX); fx += dx; } } -template <TileProc tilex, TileProc tiley> +template <unsigned (*tilex)(SkFixed, int), unsigned (*tiley)(SkFixed, int)> static void nofilter_affine(const SkBitmapProcState& s, uint32_t xy[], int count, int x, int y) { SkASSERT(!s.fInvMatrix.hasPerspective()); const SkBitmapProcStateAutoMapper mapper(s, x, y); - SkFixed3232 fx = mapper.fixed3232X(), - fy = mapper.fixed3232Y(), - dx = s.fInvSx, - dy = s.fInvKy; + SkFractionalInt fx = mapper.fractionalIntX(), + fy = mapper.fractionalIntY(), + dx = s.fInvSxFractionalInt, + dy = s.fInvKyFractionalInt; int maxX = s.fPixmap.width () - 1, maxY = s.fPixmap.height() - 1; while (count --> 0) { - *xy++ = (tiley(SkFixed3232ToFixed(fy), maxY) << 16) - | (tilex(SkFixed3232ToFixed(fx), maxX) ); + *xy++ = (tiley(SkFractionalIntToFixed(fy), maxY) << 16) + | (tilex(SkFractionalIntToFixed(fx), maxX) ); fx += dx; fy += dy; } @@ -180,7 +178,7 @@ static unsigned extract_low_bits_general(SkFixed fx, int max) { // distances are already normalized to between 0 and 1.0. // // See also SK_OPTS_NS::decode_packed_coordinates_and_weight for unpacking this value. -template <TileProc tile, TileProc extract_low_bits> +template <unsigned (*tile)(SkFixed, int), unsigned (*extract_low_bits)(SkFixed, int)> SK_NO_SANITIZE("signed-integer-overflow") static uint32_t pack(SkFixed f, unsigned max, SkFixed one) { uint32_t packed = tile(f, max); // low coordinate in high bits @@ -189,42 +187,30 @@ static uint32_t pack(SkFixed f, unsigned max, SkFixed one) { return packed; } -static constexpr int32_t max_hi = INT16_MAX; -static constexpr int32_t min_hi = INT16_MIN; -static constexpr SkFixed3232 max_fx = SkIntToFixed3232(INT16_MAX); -static constexpr SkFixed3232 min_fx = SkIntToFixed3232(0xFFFF8000ULL); - -static constexpr SkFixed sk_fixed3232_saturate2fixed(SkFixed3232 x) { - x = (x >> 32) < max_hi ? x : max_fx; - x = (x >> 32) > min_hi ? x : min_fx; - return SkFixed3232ToFixed(x); -} - -template <TileProc tilex, TileProc tiley, TileProc extract_low_bits, bool tryDecal> +template <unsigned (*tilex)(SkFixed, int), unsigned (*tiley)(SkFixed, int), unsigned (*extract_low_bits)(SkFixed, int), bool tryDecal> static void filter_scale(const SkBitmapProcState& s, uint32_t xy[], int count, int x, int y) { SkASSERT(s.fInvMatrix.isScaleTranslate()); const unsigned maxX = s.fPixmap.width() - 1; - const SkFixed3232 dx = s.fInvSx; - SkFixed3232 fx; + const SkFractionalInt dx = s.fInvSxFractionalInt; + SkFractionalInt fx; { const SkBitmapProcStateAutoMapper mapper(s, x, y); const unsigned maxY = s.fPixmap.height() - 1; // compute our two Y values up front - *xy++ = pack<tiley, extract_low_bits>( - sk_fixed3232_saturate2fixed(mapper.fixed3232Y()), maxY, s.fFilterOneY); + *xy++ = pack<tiley, extract_low_bits>(mapper.fixedY(), maxY, s.fFilterOneY); // now initialize fx - fx = mapper.fixed3232X(); + fx = mapper.fractionalIntX(); } // For historical reasons we check both ends are < maxX rather than <= maxX. // TODO: try changing this? See also can_truncate_to_fixed_for_decal(). if (tryDecal && - (unsigned)SkFixed3232ToInt(fx ) < maxX && - (unsigned)SkFixed3232ToInt(fx + dx*(count-1)) < maxX) { + (unsigned)SkFractionalIntToInt(fx ) < maxX && + (unsigned)SkFractionalIntToInt(fx + dx*(count-1)) < maxX) { while (count --> 0) { - SkFixed fixedFx = sk_fixed3232_saturate2fixed(fx); + SkFixed fixedFx = SkFractionalIntToFixed(fx); SkASSERT((fixedFx >> (16 + 14)) == 0); *xy++ = (fixedFx >> 12 << 14) | ((fixedFx >> 16) + 1); fx += dx; @@ -233,13 +219,12 @@ static void filter_scale(const SkBitmapProcState& s, } while (count --> 0) { - *xy++ = pack<tilex, extract_low_bits>( - sk_fixed3232_saturate2fixed(fx), maxX, s.fFilterOneX); + *xy++ = pack<tilex, extract_low_bits>(SkFractionalIntToFixed(fx), maxX, s.fFilterOneX); fx += dx; } } -template <TileProc tilex, TileProc tiley, TileProc extract_low_bits> +template <unsigned (*tilex)(SkFixed, int), unsigned (*tiley)(SkFixed, int), unsigned (*extract_low_bits)(SkFixed, int)> static void filter_affine(const SkBitmapProcState& s, uint32_t xy[], int count, int x, int y) { SkASSERT(!s.fInvMatrix.hasPerspective()); @@ -249,15 +234,15 @@ static void filter_affine(const SkBitmapProcState& s, SkFixed oneX = s.fFilterOneX, oneY = s.fFilterOneY; - SkFixed3232 fx = mapper.fixed3232X(), - fy = mapper.fixed3232Y(), - dx = s.fInvSx, - dy = s.fInvKy; + SkFractionalInt fx = mapper.fractionalIntX(), + fy = mapper.fractionalIntY(), + dx = s.fInvSxFractionalInt, + dy = s.fInvKyFractionalInt; unsigned maxX = s.fPixmap.width () - 1, maxY = s.fPixmap.height() - 1; while (count --> 0) { - *xy++ = pack<tiley, extract_low_bits>(SkFixed3232ToFixed(fy), maxY, oneY); - *xy++ = pack<tilex, extract_low_bits>(SkFixed3232ToFixed(fx), maxX, oneX); + *xy++ = pack<tiley, extract_low_bits>(SkFractionalIntToFixed(fy), maxY, oneY); + *xy++ = pack<tilex, extract_low_bits>(SkFractionalIntToFixed(fx), maxX, oneX); fy += dy; fx += dx; diff --git a/gfx/skia/skia/src/core/SkBlitter.cpp b/gfx/skia/skia/src/core/SkBlitter.cpp @@ -55,7 +55,7 @@ void SkBlitter::blitFatAntiRect(const SkRect& rect) { SkIRect bounds = rect.roundOut(); SkASSERT(bounds.width() >= 3); - // skbug.com/40039068 + // skbug.com/7813 // To ensure consistency of the threaded backend (a rect that's considered fat in the init-once // phase must also be considered fat in the draw phase), we have to deal with rects with small // heights because the horizontal tiling in the threaded backend may change the height. @@ -267,6 +267,8 @@ void SkBlitter::blitMask(const SkMask& mask, const SkIRect& clip) { } /////////////////////// these are not virtual, just helpers + +#if defined(SK_SUPPORT_LEGACY_ALPHA_BITMAP_AS_COVERAGE) void SkBlitter::blitMaskRegion(const SkMask& mask, const SkRegion& clip) { if (clip.quickReject(mask.fBounds)) { return; @@ -280,6 +282,7 @@ void SkBlitter::blitMaskRegion(const SkMask& mask, const SkRegion& clip) { clipper.next(); } } +#endif void SkBlitter::blitRectRegion(const SkIRect& rect, const SkRegion& clip) { SkRegion::Cliperator clipper(clip, rect); @@ -657,8 +660,7 @@ SkBlitter* SkBlitter::Choose(const SkPixmap& device, SkArenaAlloc* alloc, SkDrawCoverage drawCoverage, sk_sp<SkShader> clipShader, - const SkSurfaceProps& props, - const SkRect& devBounds) { + const SkSurfaceProps& props) { SkASSERT(alloc); if (kUnknown_SkColorType == device.colorType()) { @@ -711,8 +713,7 @@ SkBlitter* SkBlitter::Choose(const SkPixmap& device, } auto CreateSkRPBlitter = [&]() -> SkBlitter* { - auto blitter = SkCreateRasterPipelineBlitter( - device, *paint, ctm, alloc, clipShader, props, devBounds); + auto blitter = SkCreateRasterPipelineBlitter(device, *paint, ctm, alloc, clipShader, props); return blitter ? blitter : alloc->make<SkNullBlitter>(); }; diff --git a/gfx/skia/skia/src/core/SkBlitter.h b/gfx/skia/skia/src/core/SkBlitter.h @@ -9,7 +9,6 @@ #define SkBlitter_DEFINED #include "include/core/SkColor.h" -#include "include/core/SkPixmap.h" #include "include/core/SkRect.h" #include "include/core/SkRefCnt.h" #include "include/core/SkRegion.h" @@ -21,11 +20,11 @@ #include <cstddef> #include <cstdint> -#include <optional> class SkArenaAlloc; class SkMatrix; class SkPaint; +class SkPixmap; class SkShader; class SkSurfaceProps; struct SkMask; @@ -121,13 +120,6 @@ public: */ virtual int requestRowsPreserved() const { return 1; } - - struct DirectBlit { - SkPixmap pm; - uint64_t value; // low bits match pixmap's bitdepth - }; - virtual std::optional<DirectBlit> canDirectBlit() { return {}; } - /** * This function allocates memory for the blitter that the blitter then owns. * The memory can be used by the calling function at will, but it will be @@ -139,7 +131,9 @@ public: } ///@name non-virtual helpers +#if defined(SK_SUPPORT_LEGACY_ALPHA_BITMAP_AS_COVERAGE) void blitMaskRegion(const SkMask& mask, const SkRegion& clip); +#endif void blitRectRegion(const SkIRect& rect, const SkRegion& clip); void blitRegion(const SkRegion& clip); ///@} @@ -153,8 +147,7 @@ public: SkArenaAlloc*, SkDrawCoverage, sk_sp<SkShader> clipShader, - const SkSurfaceProps& props, - const SkRect& devBounds); + const SkSurfaceProps& props); static SkBlitter* ChooseSprite(const SkPixmap& dst, const SkPaint&, diff --git a/gfx/skia/skia/src/core/SkBlitter_A8.cpp b/gfx/skia/skia/src/core/SkBlitter_A8.cpp @@ -284,12 +284,13 @@ void SkA8_Blitter::blitMask(const SkMask& mask, const SkIRect& clip) { ////////////////// -SkBlitter* SkChooseA8Blitter(const SkPixmap& dst, - const SkMatrix& ctm, - const SkPaint& paint, - SkArenaAlloc* alloc, - SkDrawCoverage drawCoverage, - sk_sp<SkShader> clipShader) { +SkBlitter* SkA8Blitter_Choose(const SkPixmap& dst, + const SkMatrix& ctm, + const SkPaint& paint, + SkArenaAlloc* alloc, + SkDrawCoverage drawCoverage, + sk_sp<SkShader> clipShader, + const SkSurfaceProps&) { if (dst.colorType() != SkColorType::kAlpha_8_SkColorType) { return nullptr; } @@ -311,14 +312,3 @@ SkBlitter* SkChooseA8Blitter(const SkPixmap& dst, } return nullptr; } - -SkBlitter* SkA8Blitter_Choose(const SkPixmap& dst, - const SkMatrix& ctm, - const SkPaint& paint, - SkArenaAlloc* alloc, - SkDrawCoverage coverage, - sk_sp<SkShader> clipShader, - const SkSurfaceProps&, - const SkRect&) { - return SkChooseA8Blitter(dst, ctm, paint, alloc, coverage, clipShader); -} diff --git a/gfx/skia/skia/src/core/SkBlitter_A8.h b/gfx/skia/skia/src/core/SkBlitter_A8.h @@ -37,21 +37,12 @@ private: const SkPixmap fDevice; }; -SkBlitter* SkChooseA8Blitter(const SkPixmap& dst, - const SkMatrix& ctm, - const SkPaint&, - SkArenaAlloc*, - SkDrawCoverage, - sk_sp<SkShader> clipShader); - -// signature compatible with color version SkBlitter* SkA8Blitter_Choose(const SkPixmap& dst, const SkMatrix& ctm, const SkPaint& paint, - SkArenaAlloc* alloc, - SkDrawCoverage coverage, + SkArenaAlloc*, + SkDrawCoverage, sk_sp<SkShader> clipShader, - const SkSurfaceProps& /*ignored*/, - const SkRect& /*ignored*/); + const SkSurfaceProps&); #endif // SkBlitter_A8_DEFINED diff --git a/gfx/skia/skia/src/core/SkBlitter_ARGB32.cpp b/gfx/skia/skia/src/core/SkBlitter_ARGB32.cpp @@ -20,7 +20,6 @@ #include "src/base/SkVx.h" #include "src/core/SkBlitMask.h" #include "src/core/SkBlitRow.h" -#include "src/core/SkBlitter.h" #include "src/core/SkColorData.h" #include "src/core/SkColorPriv.h" #include "src/core/SkCoreBlitters.h" @@ -31,7 +30,6 @@ #include <algorithm> #include <cstddef> #include <cstdint> -#include <optional> static inline int upscale_31_to_32(int value) { SkASSERT((unsigned)value <= 31); @@ -1638,10 +1636,6 @@ void SkARGB32_Opaque_Blitter::blitAntiV2(int x, int y, U8CPU a0, U8CPU a1) { device[0] = SkFastFourByteInterp(fPMColor, device[0], a1); } -std::optional<SkBlitter::DirectBlit> SkARGB32_Opaque_Blitter::canDirectBlit() { - return {{ fDevice, fPMColor }}; -} - /////////////////////////////////////////////////////////////////////////////// void SkARGB32_Blitter::blitV(int x, int y, int height, SkAlpha alpha) { diff --git a/gfx/skia/skia/src/core/SkBlurMaskFilterImpl.cpp b/gfx/skia/skia/src/core/SkBlurMaskFilterImpl.cpp @@ -12,7 +12,6 @@ #include "include/core/SkFlattenable.h" #include "include/core/SkImageFilter.h" #include "include/core/SkImageInfo.h" -#include "include/core/SkM44.h" #include "include/core/SkMaskFilter.h" #include "include/core/SkMatrix.h" #include "include/core/SkPaint.h" @@ -34,7 +33,7 @@ #include "src/core/SkBlitter_A8.h" #include "src/core/SkBlurMask.h" #include "src/core/SkCachedData.h" -#include "src/core/SkDraw.h" +#include "src/core/SkDrawBase.h" #include "src/core/SkMask.h" #include "src/core/SkMaskCache.h" #include "src/core/SkMaskFilterBase.h" @@ -72,38 +71,31 @@ bool SkBlurMaskFilterImpl::asABlur(BlurRec* rec) const { return true; } -std::pair<sk_sp<SkImageFilter>, bool> SkBlurMaskFilterImpl::asImageFilter(const SkMatrix& ctm, - const SkPaint&) const { - // Mask filters apply a uniform blur in either local or device space. Depending on the scale - // factors of the `ctm`, the actual blur radii can end up non-uniform. - SkV2 sigma = {fSigma, fSigma}; +sk_sp<SkImageFilter> SkBlurMaskFilterImpl::asImageFilter(const SkMatrix& ctm) const { + float sigma = fSigma; if (this->ignoreXform()) { // This is analogous to computeXformedSigma(), but it might be more correct to wrap the // blur image filter in a local matrix with ctm^-1, or to control the skif::Mapping when - // the mask filter layer is restored. It calculates new blur radii such that transforming - // these to the layer space of the image filter will match the original device-space fSigma. - // This can be inaccurate when 'ctm' has skew or perspective. A full fix requires layers - // having flexible operating coordinate spaces (e.g. parent or root canvas). - const float xScaleFactor = fSigma / ctm.mapVector(fSigma, 0.f).length(); - const float yScaleFactor = fSigma / ctm.mapVector(0.f, fSigma).length(); - sigma = {fSigma * xScaleFactor, fSigma * yScaleFactor}; + // the mask filter layer is restored. This is inaccurate when 'ctm' has skew or perspective + const float ctmScaleFactor = fSigma / ctm.mapRadius(fSigma); + sigma *= ctmScaleFactor; } // The null input image filter will be bound to the original coverage mask. - sk_sp<SkImageFilter> filter = SkImageFilters::Blur(sigma.x, sigma.y, nullptr); + sk_sp<SkImageFilter> filter = SkImageFilters::Blur(sigma, sigma, nullptr); // Combine the original coverage mask (src) and the blurred coverage mask (dst) switch(fBlurStyle) { case kInner_SkBlurStyle: // dst = dst * src // = 0 * src + src * dst - return {SkImageFilters::Blend(SkBlendMode::kDstIn, std::move(filter), nullptr), false}; + return SkImageFilters::Blend(SkBlendMode::kDstIn, std::move(filter), nullptr); case kSolid_SkBlurStyle: // dst = src + dst - src * dst // = 1 * src + (1 - src) * dst - return {SkImageFilters::Blend(SkBlendMode::kSrcOver, std::move(filter), nullptr), false}; + return SkImageFilters::Blend(SkBlendMode::kSrcOver, std::move(filter), nullptr); case kOuter_SkBlurStyle: // dst = dst * (1 - src) // = 0 * src + (1 - src) * dst - return {SkImageFilters::Blend(SkBlendMode::kDstOut, std::move(filter), nullptr), false}; + return SkImageFilters::Blend(SkBlendMode::kDstOut, std::move(filter), nullptr); case kNormal_SkBlurStyle: - return {filter, false}; + return filter; } SkUNREACHABLE; } @@ -163,7 +155,7 @@ template <typename Proc> bool draw_into_mask(SkMaskBuilder* mask, const SkRect& SkMatrix ctm = SkMatrix::Translate(-SkIntToScalar(dx), -SkIntToScalar(dy)); - skcpu::Draw draw; + SkDrawBase draw; draw.fBlitterChooser = SkA8Blitter_Choose; draw.fCTM = &ctm; draw.fDst = pm; @@ -178,7 +170,7 @@ template <typename Proc> bool draw_into_mask(SkMaskBuilder* mask, const SkRect& static bool draw_rects_into_mask(SkSpan<const SkRect> rects, SkMaskBuilder* mask) { SkASSERT(rects.size() == 1 || rects.size() == 2); - return draw_into_mask(mask, rects[0], [&](skcpu::Draw& draw, const SkPaint& paint) { + return draw_into_mask(mask, rects[0], [&](SkDrawBase& draw, const SkPaint& paint) { if (rects.size() == 1) { draw.drawRect(rects[0], paint); } else { @@ -193,7 +185,7 @@ static bool draw_rects_into_mask(SkSpan<const SkRect> rects, SkMaskBuilder* mask } static bool draw_rrect_into_mask(const SkRRect& rrect, SkMaskBuilder* mask) { - return draw_into_mask(mask, rrect.rect(), [&](skcpu::Draw& draw, const SkPaint& paint) { + return draw_into_mask(mask, rrect.rect(), [&](SkDrawBase& draw, const SkPaint& paint) { draw.drawRRect(rrect, paint); }); } @@ -203,14 +195,9 @@ static bool rect_exceeds(const SkRect& r, SkScalar v) { r.width() > v || r.height() > v; } -static SkCachedData* copy_mask_to_cacheddata(SkMaskBuilder* mask, SkResourceCache* cache) { +static SkCachedData* copy_mask_to_cacheddata(SkMaskBuilder* mask) { const size_t size = mask->computeTotalImageSize(); - SkCachedData* data; - if (cache) { - data = cache->newCachedData(size); - } else { - data = SkResourceCache::NewCachedData(size); - } + SkCachedData* data = SkResourceCache::NewCachedData(size); if (data) { memcpy(data->writable_data(), mask->fImage, size); SkMaskBuilder::FreeImage(mask->image()); @@ -219,51 +206,41 @@ static SkCachedData* copy_mask_to_cacheddata(SkMaskBuilder* mask, SkResourceCach return data; } -static SkCachedData* find_cached_rrect(SkTLazy<SkMask>* mask, - SkScalar sigma, - SkBlurStyle style, - const SkRRect& rrect, - SkResourceCache* cache) { - return SkMaskCache::FindAndRef(sigma, style, rrect, mask, cache); +static SkCachedData* find_cached_rrect(SkTLazy<SkMask>* mask, SkScalar sigma, SkBlurStyle style, + const SkRRect& rrect) { + return SkMaskCache::FindAndRef(sigma, style, rrect, mask); } -static SkCachedData* add_cached_rrect(SkMaskBuilder* mask, - SkScalar sigma, - SkBlurStyle style, - const SkRRect& rrect, - SkResourceCache* cache) { - SkCachedData* cached = copy_mask_to_cacheddata(mask, cache); - if (cached) { - SkMaskCache::Add(sigma, style, rrect, *mask, cached, cache); +static SkCachedData* add_cached_rrect(SkMaskBuilder* mask, SkScalar sigma, SkBlurStyle style, + const SkRRect& rrect) { + SkCachedData* cache = copy_mask_to_cacheddata(mask); + if (cache) { + SkMaskCache::Add(sigma, style, rrect, *mask, cache); } - return cached; + return cache; } static SkCachedData* find_cached_rects(SkTLazy<SkMask>* mask, SkScalar sigma, SkBlurStyle style, - SkSpan<const SkRect> rects, - SkResourceCache* cache) { - return SkMaskCache::FindAndRef(sigma, style, rects, mask, cache); + SkSpan<const SkRect> rects) { + return SkMaskCache::FindAndRef(sigma, style, rects, mask); } static SkCachedData* add_cached_rects(SkMaskBuilder* mask, SkScalar sigma, SkBlurStyle style, - SkSpan<const SkRect> rects, - SkResourceCache* cache) { - SkCachedData* cached = copy_mask_to_cacheddata(mask, cache); - if (cached) { - SkMaskCache::Add(sigma, style, rects, *mask, cached, cache); + SkSpan<const SkRect> rects) { + SkCachedData* cache = copy_mask_to_cacheddata(mask); + if (cache) { + SkMaskCache::Add(sigma, style, rects, *mask, cache); } - return cached; + return cache; } -std::optional<SkMaskFilterBase::NinePatch> SkBlurMaskFilterImpl::filterRRectToNine( - const SkRRect& rrect, - const SkMatrix& matrix, - const SkIRect& clipBounds, - SkResourceCache* cache) const { +std::optional<SkMaskFilterBase::NinePatch> +SkBlurMaskFilterImpl::filterRRectToNine(const SkRRect& rrect, const SkMatrix& matrix, + const SkIRect& clipBounds) const { switch (rrect.getType()) { case SkRRect::kEmpty_Type: // Nothing to draw. @@ -381,8 +358,8 @@ std::optional<SkMaskFilterBase::NinePatch> SkBlurMaskFilterImpl::filterRRectToNi 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<SkMask> cachedMask; - SkCachedData* cached = find_cached_rrect(&cachedMask, sigma, fBlurStyle, smallRR, cache); - if (!cached) { + SkCachedData* cache = find_cached_rrect(&cachedMask, sigma, fBlurStyle, smallRR); + if (!cache) { // Blit the small rrect into a buffer (9x7) // 4BFFFFFB4 // CFFFFFFFC @@ -414,7 +391,7 @@ std::optional<SkMaskFilterBase::NinePatch> SkBlurMaskFilterImpl::filterRRectToNi SkASSERT(filterM.fBounds.width() == (srcM.fBounds.width() + 2*margin.fX)); SkASSERT(filterM.fBounds.height() == (srcM.fBounds.height() + 2*margin.fY)); - cached = add_cached_rrect(&filterM, sigma, fBlurStyle, smallRR, cache); + cache = add_cached_rrect(&filterM, sigma, fBlurStyle, smallRR); cachedMask.init(filterM); } @@ -432,15 +409,14 @@ std::optional<SkMaskFilterBase::NinePatch> SkBlurMaskFilterImpl::filterRRectToNi SkMask{cachedMask->fImage, bounds, cachedMask->fRowBytes, cachedMask->fFormat}, dstM.fBounds, center, - cached); // transfer ownership to patch + cache); // transfer ownership to patch } -SkMaskFilterBase::FilterReturn SkBlurMaskFilterImpl::filterRectsToNine( - SkSpan<const SkRect> rects, - const SkMatrix& matrix, - const SkIRect& clipBounds, - std::optional<NinePatch>* patch, - SkResourceCache* cache) const { +SkMaskFilterBase::FilterReturn +SkBlurMaskFilterImpl::filterRectsToNine(SkSpan<const SkRect> rects, + const SkMatrix& matrix, + const SkIRect& clipBounds, + std::optional<NinePatch>* patch) const { SkASSERT(patch != nullptr); SkASSERT(rects.size() == 1 || rects.size() == 2); @@ -535,8 +511,8 @@ SkMaskFilterBase::FilterReturn SkBlurMaskFilterImpl::filterRectsToNine( const SkScalar sigma = this->computeXformedSigma(matrix); SkTLazy<SkMask> cachedMask; SkSpan<const SkRect> smallRects = SkSpan(smallR, rectCount); - SkCachedData* cached = find_cached_rects(&cachedMask, sigma, fBlurStyle, smallRects, cache); - if (!cached) { + SkCachedData* cache = find_cached_rects(&cachedMask, sigma, fBlurStyle, smallRects); + if (!cache) { SkMaskBuilder filterM; if (rectCount == 2) { if (!draw_rects_into_mask(smallRects, &srcM)) { @@ -554,7 +530,7 @@ SkMaskFilterBase::FilterReturn SkBlurMaskFilterImpl::filterRectsToNine( return FilterReturn::kFalse; } } - cached = add_cached_rects(&filterM, sigma, fBlurStyle, smallRects, cache); + cache = add_cached_rects(&filterM, sigma, fBlurStyle, smallRects); cachedMask.init(filterM); } SkIRect bounds = cachedMask->fBounds; @@ -562,7 +538,7 @@ SkMaskFilterBase::FilterReturn SkBlurMaskFilterImpl::filterRectsToNine( patch->emplace(SkMask{cachedMask->fImage, bounds, cachedMask->fRowBytes, cachedMask->fFormat}, dstM.fBounds, center, - cached); // transfer ownership to patch + cache); // transfer ownership to patch return FilterReturn::kTrue; } diff --git a/gfx/skia/skia/src/core/SkBlurMaskFilterImpl.h b/gfx/skia/skia/src/core/SkBlurMaskFilterImpl.h @@ -9,26 +9,23 @@ #define SkBlurMaskFilterImpl_DEFINED #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 <optional> -#include <utility> class SkImageFilter; class SkMatrix; -class SkPaint; class SkRRect; class SkReadBuffer; -class SkResourceCache; class SkWriteBuffer; enum SkBlurStyle : int; struct SkIPoint; struct SkIRect; struct SkRect; -template <typename T> class sk_sp; class SkBlurMaskFilterImpl : public SkMaskFilterBase { public: @@ -42,8 +39,8 @@ public: void computeFastBounds(const SkRect&, SkRect*) const override; bool asABlur(BlurRec*) const override; - std::pair<sk_sp<SkImageFilter>, bool> asImageFilter(const SkMatrix& ctm, - const SkPaint&) const override; + sk_sp<SkImageFilter> asImageFilter(const SkMatrix& ctm) const override; + SkScalar computeXformedSigma(const SkMatrix& ctm) const; SkBlurStyle blurStyle() const {return fBlurStyle;} @@ -54,13 +51,11 @@ private: FilterReturn filterRectsToNine(SkSpan<const SkRect>, const SkMatrix&, const SkIRect& clipBounds, - std::optional<NinePatch>*, - SkResourceCache*) const override; + std::optional<NinePatch>*) const override; std::optional<NinePatch> filterRRectToNine(const SkRRect&, const SkMatrix&, - const SkIRect& clipBounds, - SkResourceCache*) const override; + const SkIRect& clipBounds) const override; bool filterRectMask(SkMaskBuilder* dstM, const SkRect& r, diff --git a/gfx/skia/skia/src/core/SkCPUContext.cpp b/gfx/skia/skia/src/core/SkCPUContext.cpp @@ -1,32 +0,0 @@ -/* - * 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 "include/core/SkCPUContext.h" - -#include "include/core/SkTypes.h" -#include "src/core/SkCPUContextImpl.h" -#include "src/core/SkCPURecorderImpl.h" - -namespace skcpu { - -std::unique_ptr<const Context> Context::Make(const Context::Options& opts) { - return std::make_unique<ContextImpl>(); -} - -std::unique_ptr<const Context> Context::Make() { - return Context::Make({}); -} - -std::unique_ptr<Recorder> Context::makeRecorder() const { - return std::make_unique<RecorderImpl>(static_cast<const ContextImpl*>(this)); -} - -const ContextImpl* ContextImpl::TODO() { - static const ContextImpl* gContext = static_cast<const ContextImpl*>(Context::Make().release()); - return gContext; -} - -} // namespace skcpu diff --git a/gfx/skia/skia/src/core/SkCPUContextImpl.h b/gfx/skia/skia/src/core/SkCPUContextImpl.h @@ -1,23 +0,0 @@ -/* - * 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 skcpu_ContextImpl_DEFINED -#define skcpu_ContextImpl_DEFINED - -#include "include/core/SkCPUContext.h" -#include "include/core/SkSurfaceProps.h" -#include "src/core/SkResourceCache.h" - -namespace skcpu { -class ContextImpl final : public Context { -public: - ContextImpl() = default; - - static const ContextImpl* TODO(); -}; -} // namespace skcpu - -#endif diff --git a/gfx/skia/skia/src/core/SkCPURecorder.cpp b/gfx/skia/skia/src/core/SkCPURecorder.cpp @@ -1,21 +0,0 @@ -/* - * 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 "include/core/SkCPURecorder.h" - -#include "include/core/SkTypes.h" -#include "src/core/SkCPUContextImpl.h" - -#include <memory> - -namespace skcpu { - -Recorder* Recorder::TODO() { - static Recorder* gRecorder = ContextImpl::TODO()->makeRecorder().release(); - return gRecorder; -} - -} // namespace skcpu diff --git a/gfx/skia/skia/src/core/SkCPURecorderImpl.h b/gfx/skia/skia/src/core/SkCPURecorderImpl.h @@ -1,32 +0,0 @@ -/* - * 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 skcpu_RecorderImpl_DEFINED -#define skcpu_RecorderImpl_DEFINED - -#include "include/core/SkCPURecorder.h" -#include "src/core/SkCPUContextImpl.h" - -namespace skcpu { - -class RecorderImpl final : public skcpu::Recorder { -public: - RecorderImpl(const ContextImpl* ctx) : fCtx(ctx) {} - - const ContextImpl* ctx() const { return fCtx; } - -private: - const ContextImpl* const fCtx; -}; - -} // namespace skcpu - -inline skcpu::RecorderImpl* asRRI(skcpu::Recorder* rr) { - return static_cast<skcpu::RecorderImpl*>(rr); -} - -#endif diff --git a/gfx/skia/skia/src/core/SkCanvas.cpp b/gfx/skia/skia/src/core/SkCanvas.cpp @@ -899,7 +899,7 @@ void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, restorePaint.setImageFilter(nullptr); // the image filter is held separately // Smooth non-axis-aligned layer edges; this automatically downgrades to non-AA for aligned // layer restores. This is done to match legacy behavior where the post-applied MatrixTransform - // bilerp also smoothed cropped edges. See skbug.com/40042614 + // bilerp also smoothed cropped edges. See skbug.com/11252 restorePaint.setAntiAlias(true); sk_sp<SkImageFilter> paintFilter = rec.fPaint ? rec.fPaint->refImageFilter() : nullptr; @@ -1401,7 +1401,7 @@ void SkCanvas::androidFramework_setDeviceClipRestriction(const SkIRect& rect) { // of renderable pixels, so once set, the restriction will be respected until the canvas // save stack is restored past the point this function was invoked. Unfortunately, the current // implementation relies on the clip stack of the underyling SkDevices, which leads to some - // awkward behavioral interactions (see skbug.com/40043342). + // awkward behavioral interactions (see skbug.com/12252). // // Namely, a canvas restore() could undo the clip restriction's rect, and if // setDeviceClipRestriction were called at a nested save level, there's no way to undo just the @@ -1610,16 +1610,19 @@ SkRect SkCanvas::getLocalClipBounds() const { return SkRect::MakeEmpty(); } - auto inverse = fMCRec->fMatrix.asM33().invert(); + SkMatrix inverse; // if we can't invert the CTM, we can't return local clip bounds - if (!inverse) { + if (!fMCRec->fMatrix.asM33().invert(&inverse)) { return SkRect::MakeEmpty(); } + SkRect bounds; // adjust it outwards in case we are antialiasing const int margin = 1; - return inverse->mapRect(SkRect::Make(ibounds.makeOutset(margin, margin))); + SkRect r = SkRect::Make(ibounds.makeOutset(margin, margin)); + inverse.mapRect(&bounds, r); + return bounds; } SkIRect SkCanvas::getDeviceClipBounds() const { @@ -1660,10 +1663,6 @@ skgpu::graphite::Recorder* SkCanvas::recorder() const { return this->topDevice()->recorder(); } -SkRecorder* SkCanvas::baseRecorder() const { - return this->topDevice()->baseRecorder(); -} - void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) { TRACE_EVENT0("skia", TRACE_FUNC); @@ -1729,11 +1728,9 @@ void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) { this->onDrawRRect(rrect, paint); } -void SkCanvas::drawPoints(PointMode mode, SkSpan<const SkPoint> pts, const SkPaint& paint) { +void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) { TRACE_EVENT0("skia", TRACE_FUNC); - if (!pts.empty()) { - this->onDrawPoints(mode, pts.size(), pts.data(), paint); - } + this->onDrawPoints(mode, count, pts, paint); } void SkCanvas::drawVertices(const sk_sp<SkVertices>& vertices, SkBlendMode mode, @@ -1831,21 +1828,18 @@ void SkCanvas::drawImageLattice(const SkImage* image, const Lattice& lattice, co } } -void SkCanvas::drawAtlas(const SkImage* atlas, SkSpan<const SkRSXform> xform, - SkSpan<const SkRect> tex, SkSpan<const SkColor> colors, SkBlendMode mode, +void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[], + const SkColor colors[], int count, SkBlendMode mode, const SkSamplingOptions& sampling, const SkRect* cull, const SkPaint* paint) { TRACE_EVENT0("skia", TRACE_FUNC); RETURN_ON_NULL(atlas); - size_t count = std::min(xform.size(), tex.size()); - if (!colors.empty()) { - count = std::min(count, colors.size()); - } - if (count == 0) { + if (count <= 0) { return; } - this->onDrawAtlas2(atlas, xform.data(), tex.data(), colors.data(), count, mode, sampling, - cull, paint); + SkASSERT(atlas); + SkASSERT(tex); + this->onDrawAtlas2(atlas, xform, tex, colors, count, mode, sampling, cull, paint); } void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) { @@ -1866,7 +1860,7 @@ void SkCanvas::onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) { if (!this->predrawNotify()) { return; } - this->topDevice()->drawShadow(this, path, rec); + this->topDevice()->drawShadow(path, rec); } void SkCanvas::experimental_DrawEdgeAAQuad(const SkRect& rect, const SkPoint clip[4], @@ -1886,7 +1880,7 @@ void SkCanvas::experimental_DrawEdgeAAImageSet(const ImageSetEntry imageSet[], i TRACE_EVENT0("skia", TRACE_FUNC); // Route single, rectangular quads to drawImageRect() to take advantage of image filter // optimizations that avoid a layer. - if (paint && (paint->getImageFilter() || paint->getMaskFilter()) && cnt == 1) { + if (paint && paint->getImageFilter() && cnt == 1) { const auto& entry = imageSet[0]; // If the preViewMatrix is skipped or a positive-scale + translate matrix, we can apply it // to the entry's dstRect w/o changing output behavior. @@ -1942,36 +1936,24 @@ void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[], } SkASSERT(pts != nullptr); + SkRect bounds; + // Compute bounds from points (common for drawing a single line) + if (count == 2) { + bounds.set(pts[0], pts[1]); + } else { + bounds.setBounds(pts, SkToInt(count)); + } + // Enforce paint style matches implicit behavior of drawPoints SkPaint strokePaint = paint; strokePaint.setStyle(SkPaint::kStroke_Style); - - SkRect boundsStorage; - const SkRect* boundsPtr = nullptr; - - /* - * Computing the bounds can actually slow us down (since we check inside). - * But if there is a filter, then it is useful to limit the size of - * its offscreen, hence we only compute it in those cases. - * - * Note: it would be "correct" to never compute this, it is just considered - * an optimization opportunity. - */ - if (paint.getImageFilter() || paint.getMaskFilter()) { - auto bounds = SkRect::Bounds({pts, count}); - if (!bounds) { - return; - } - if (this->internalQuickReject(bounds.value(), strokePaint)) { - return; - } - boundsStorage = bounds.value(); - boundsPtr = &boundsStorage; + if (this->internalQuickReject(bounds, strokePaint)) { + return; } - auto layer = this->aboutToDraw(strokePaint, boundsPtr); + auto layer = this->aboutToDraw(strokePaint, &bounds); if (layer) { - this->topDevice()->drawPoints(mode, {pts, count}, layer->paint()); + this->topDevice()->drawPoints(mode, count, pts, layer->paint()); } } @@ -2227,7 +2209,7 @@ void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) { } } -// Clean-up the paint to match the drawing semantics for drawImage et al. (skbug.com/40039059). +// Clean-up the paint to match the drawing semantics for drawImage et al. (skbug.com/7804). static SkPaint clean_paint_for_drawImage(const SkPaint* paint) { SkPaint cleaned; if (paint) { @@ -2344,6 +2326,8 @@ void SkCanvas::onDrawImageRect2(const SkImage* image, const SkRect& src, const S return; } + // When there's a alpha-only image that must be colorized or a mask filter to apply, go through + // the regular auto-layer-for-imagefilter process if (realPaint.getMaskFilter() && this->topDevice()->useDrawCoverageMaskForMaskFilters()) { // Route mask-filtered drawImages to drawRect() to use the auto-layer for mask filters, // which require all shading to be encoded in the paint. @@ -2488,17 +2472,17 @@ void SkCanvas::drawSimpleText(const void* text, size_t byteLength, SkTextEncodin } } -void SkCanvas::drawGlyphs(SkSpan<const SkGlyphID> glyphs, SkSpan<const SkPoint> positions, - SkSpan<const uint32_t> clusters, SkSpan<const char> utf8text, +void SkCanvas::drawGlyphs(int count, const SkGlyphID* glyphs, const SkPoint* positions, + const uint32_t* clusters, int textByteCount, const char* utf8text, SkPoint origin, const SkFont& font, const SkPaint& paint) { - if (glyphs.empty()) { return; } + if (count <= 0) { return; } sktext::GlyphRun glyphRun { font, - positions, - glyphs, - utf8text, - clusters, + SkSpan(positions, count), + SkSpan(glyphs, count), + SkSpan(utf8text, textByteCount), + SkSpan(clusters, count), SkSpan<SkVector>() }; @@ -2507,14 +2491,14 @@ void SkCanvas::drawGlyphs(SkSpan<const SkGlyphID> glyphs, SkSpan<const SkPoint> this->onDrawGlyphRunList(glyphRunList, paint); } -void SkCanvas::drawGlyphs(SkSpan<const SkGlyphID> glyphs, SkSpan<const SkPoint> positions, +void SkCanvas::drawGlyphs(int count, const SkGlyphID glyphs[], const SkPoint positions[], SkPoint origin, const SkFont& font, const SkPaint& paint) { - if (glyphs.empty()) { return; } + if (count <= 0) { return; } sktext::GlyphRun glyphRun { font, - positions, - glyphs, + SkSpan(positions, count), + SkSpan(glyphs, count), SkSpan<const char>(), SkSpan<const uint32_t>(), SkSpan<SkVector>() @@ -2525,17 +2509,17 @@ void SkCanvas::drawGlyphs(SkSpan<const SkGlyphID> glyphs, SkSpan<const SkPoint> this->onDrawGlyphRunList(glyphRunList, paint); } -void SkCanvas::drawGlyphsRSXform(SkSpan<const SkGlyphID> glyphs, SkSpan<const SkRSXform> xforms, - SkPoint origin, const SkFont& font, const SkPaint& paint) { - if (glyphs.empty()) { return; } +void SkCanvas::drawGlyphs(int count, const SkGlyphID glyphs[], const SkRSXform xforms[], + SkPoint origin, const SkFont& font, const SkPaint& paint) { + if (count <= 0) { return; } auto [positions, rotateScales] = - fScratchGlyphRunBuilder->convertRSXForm(xforms); + fScratchGlyphRunBuilder->convertRSXForm(SkSpan(xforms, count)); sktext::GlyphRun glyphRun { font, positions, - glyphs, + SkSpan(glyphs, count), SkSpan<const char>(), SkSpan<const uint32_t>(), rotateScales @@ -2604,22 +2588,18 @@ void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4], void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4], const SkPoint texCoords[4], SkBlendMode bmode, const SkPaint& paint) { - auto bounds = SkRect::Bounds({cubics, (size_t)SkPatchUtils::kNumCtrlPts}); - if (!bounds) { - return; // we don't draw if the bounds are not finite - } - // drawPatch has the same behavior restrictions as drawVertices SkPaint simplePaint = clean_paint_for_drawVertices(paint); // Since a patch is always within the convex hull of the control points, we discard it when its // bounding rectangle is completely outside the current clip. - if (this->internalQuickReject(bounds.value(), simplePaint)) { + SkRect bounds; + bounds.setBounds(cubics, SkPatchUtils::kNumCtrlPts); + if (this->internalQuickReject(bounds, simplePaint)) { return; } - auto r = bounds.value(); - auto layer = this->aboutToDraw(simplePaint, &r); + auto layer = this->aboutToDraw(simplePaint, &bounds); if (layer) { this->topDevice()->drawPatch(cubics, colors, texCoords, SkBlender::Mode(bmode), layer->paint()); @@ -2675,8 +2655,8 @@ void SkCanvas::onDrawAtlas2(const SkImage* atlas, const SkRSXform xform[], const SkASSERT(!realPaint.getMaskFilter()); auto layer = this->aboutToDraw(realPaint); if (layer) { - this->topDevice()->drawAtlas({xform, count}, {tex, count}, {colors, colors ? count : 0}, - SkBlender::Mode(bmode), layer->paint()); + this->topDevice()->drawAtlas(xform, tex, colors, count, SkBlender::Mode(bmode), + layer->paint()); } } @@ -2719,7 +2699,7 @@ void SkCanvas::onDrawEdgeAAImageSet2(const ImageSetEntry imageSet[], int count, // individual entries and Chromium's occlusion culling already makes it likely that at least one // entry will be visible. So, we only calculate the draw bounds when it's trivial (count == 1), // or we need it for the autolooper (since it greatly improves image filter perf). - bool needsAutoLayer = SkToBool(realPaint.getImageFilter() || realPaint.getMaskFilter()); + bool needsAutoLayer = SkToBool(realPaint.getImageFilter()); bool setBoundsValid = count == 1 || needsAutoLayer; SkRect setBounds = imageSet[0].fDstRect; if (imageSet[0].fMatrixIndex >= 0) { @@ -2741,46 +2721,6 @@ void SkCanvas::onDrawEdgeAAImageSet2(const ImageSetEntry imageSet[], int count, return; } - if (realPaint.getMaskFilter() && this->topDevice()->useDrawCoverageMaskForMaskFilters()) { - // Route mask-filtered drawEdgeAAImageSets to drawEdgeAAQuad() or drawImageRect() - // to use the auto-layer for mask filters, which require all shading to be encoded in - // the paint. - int dstClipIndex = 0; - for (int i = 0; i < count; ++i) { - SkPaint imagePaint = realPaint; - SkRect drawDst = SkModifyPaintAndDstForDrawImageRect( - imageSet[i].fImage.get(), sampling, - imageSet[i].fSrcRect, imageSet[i].fDstRect, - constraint == kStrict_SrcRectConstraint, &imagePaint); - if (drawDst.isEmpty()) { - return; - } - - auto layer = this->aboutToDraw(imagePaint, &drawDst); - if (layer) { - // Since we can't call mapRect to apply any preview matrix and drawEdgeAAQuad - // doesn't take an optional matrix, we can modify the local-to-device matrix - // of the layers top device. - if (imageSet[i].fMatrixIndex >= 0) { - this->topDevice()->setLocalToDevice( - this->topDevice()->localToDevice44() * - SkM44(preViewMatrices[imageSet[i].fMatrixIndex])); - } - - // Call drawEdgeAAImageSet on each image one at a time, to correctly - // paint the image. - this->topDevice()->drawEdgeAAQuad(drawDst, - imageSet[i].fHasClip ? dstClips + dstClipIndex - : nullptr, - (QuadAAFlags)imageSet[i].fAAFlags, - layer->paint().getColor4f(), - SkBlendMode::kSrcOver); - } - dstClipIndex += 4 * imageSet[i].fHasClip; - } - return; - } - auto layer = this->aboutToDraw(realPaint, setBoundsValid ? &setBounds : nullptr); if (layer) { this->topDevice()->drawEdgeAAImageSet(imageSet, count, dstClips, preViewMatrices, @@ -2801,15 +2741,15 @@ void SkCanvas::drawColor(const SkColor4f& c, SkBlendMode mode) { } void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) { - const SkPoint pt[1] = {{ x, y }}; - this->drawPoints(kPoints_PointMode, pt, paint); + const SkPoint pt = { x, y }; + this->drawPoints(kPoints_PointMode, 1, &pt, paint); } void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1, const SkPaint& paint) { SkPoint pts[2]; pts[0].set(x0, y0); pts[1].set(x1, y1); - this->drawPoints(kLines_PointMode, pts, paint); + this->drawPoints(kLines_PointMode, 2, pts, paint); } void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius, const SkPaint& paint) { diff --git a/gfx/skia/skia/src/core/SkCanvasPriv.cpp b/gfx/skia/skia/src/core/SkCanvasPriv.cpp @@ -247,8 +247,8 @@ void AutoLayerForImageFilter::addMaskFilterLayer(const SkRect* drawBounds) { SkASSERT(!fPaint.getImageFilter()); // TODO: Eventually all SkMaskFilters will implement this method so this can switch to an assert - auto [maskFilterAsImageFilter, appliesShading] = as_MFB( - fPaint.getMaskFilter())->asImageFilter(fCanvas->getTotalMatrix(), fPaint); + sk_sp<SkImageFilter> maskFilterAsImageFilter = + as_MFB(fPaint.getMaskFilter())->asImageFilter(fCanvas->getTotalMatrix()); if (!maskFilterAsImageFilter) { // This is a legacy mask filter that can be handled by raster and Ganesh directly, but will // be ignored by Graphite. Return now, leaving the paint with the mask filter so that the @@ -259,16 +259,12 @@ void AutoLayerForImageFilter::addMaskFilterLayer(const SkRect* drawBounds) { // The restore paint for the coverage layer takes over all shading effects that had been on the // original paint, which will be applied to the alpha-only output image from the mask filter // converted to an image filter. - // If we know our mask filter will affect shading, we don't want to add the original shading - // into the restore paint. SkPaint restorePaint; - if (!appliesShading) { - restorePaint.setColor4f(fPaint.getColor4f()); - restorePaint.setShader(fPaint.refShader()); - restorePaint.setColorFilter(fPaint.refColorFilter()); - restorePaint.setDither(fPaint.isDither()); - } + restorePaint.setColor4f(fPaint.getColor4f()); + restorePaint.setShader(fPaint.refShader()); + restorePaint.setColorFilter(fPaint.refColorFilter()); restorePaint.setBlender(fPaint.refBlender()); + restorePaint.setDither(fPaint.isDither()); restorePaint.setImageFilter(maskFilterAsImageFilter); // Remove all shading effects from the "working" paint so that the layer's alpha channel @@ -281,7 +277,7 @@ void AutoLayerForImageFilter::addMaskFilterLayer(const SkRect* drawBounds) { fPaint.setDither(false); fPaint.setBlendMode(SkBlendMode::kSrcOver); - this->addLayer(restorePaint, drawBounds, /*coverageOnly=*/!appliesShading); + this->addLayer(restorePaint, drawBounds, /*coverageOnly=*/true); } void AutoLayerForImageFilter::addLayer(const SkPaint& restorePaint, diff --git a/gfx/skia/skia/src/core/SkClipStack.cpp b/gfx/skia/skia/src/core/SkClipStack.cpp @@ -9,10 +9,11 @@ #include "include/core/SkBlendMode.h" #include "include/core/SkPath.h" -#include "include/core/SkPathBuilder.h" +#include "include/core/SkPathTypes.h" #include "include/core/SkScalar.h" #include "include/private/base/SkDebug.h" #include "src/core/SkRectPriv.h" +#include "src/shaders/SkShaderBase.h" #include <array> #include <atomic> @@ -33,7 +34,7 @@ SkClipStack::Element::Element(const Element& that) { break; case DeviceSpaceType::kPath: fShader.reset(); - fDeviceSpacePath = that.getDeviceSpacePath(); + fDeviceSpacePath.set(that.getDeviceSpacePath()); break; case DeviceSpaceType::kShader: fDeviceSpacePath.reset(); @@ -137,6 +138,32 @@ bool SkClipStack::Element::contains(const SkRRect& rrect) const { } } +void SkClipStack::Element::invertShapeFillType() { + switch (fDeviceSpaceType) { + case DeviceSpaceType::kRect: + fDeviceSpacePath.init(); + fDeviceSpacePath->addRect(this->getDeviceSpaceRect()); + fDeviceSpacePath->setFillType(SkPathFillType::kInverseEvenOdd); + fDeviceSpaceType = DeviceSpaceType::kPath; + break; + case DeviceSpaceType::kRRect: + fDeviceSpacePath.init(); + fDeviceSpacePath->addRRect(fDeviceSpaceRRect); + fDeviceSpacePath->setFillType(SkPathFillType::kInverseEvenOdd); + fDeviceSpaceType = DeviceSpaceType::kPath; + break; + case DeviceSpaceType::kPath: + fDeviceSpacePath->toggleInverseFillType(); + break; + case DeviceSpaceType::kShader: + fShader = as_SB(fShader)->makeInvertAlpha(); + break; + case DeviceSpaceType::kEmpty: + // Should this set to an empty, inverse filled path? + break; + } +} + void SkClipStack::Element::initCommon(int saveCount, SkClipOp op, bool doAA) { fSaveCount = saveCount; fOp = op; @@ -160,13 +187,15 @@ void SkClipStack::Element::initRect(int saveCount, const SkRect& rect, const SkM this->initCommon(saveCount, op, doAA); return; } - this->initAsPath(saveCount, SkPath::Rect(rect), m, op, doAA); + SkPath path; + path.addRect(rect); + path.setIsVolatile(true); + this->initAsPath(saveCount, path, m, op, doAA); } void SkClipStack::Element::initRRect(int saveCount, const SkRRect& rrect, const SkMatrix& m, SkClipOp op, bool doAA) { - if (auto rr = rrect.transform(m)) { - fDeviceSpaceRRect = *rr; + if (rrect.transform(m, &fDeviceSpaceRRect)) { SkRRect::Type type = fDeviceSpaceRRect.getType(); if (SkRRect::kRect_Type == type || SkRRect::kEmpty_Type == type) { fDeviceSpaceType = DeviceSpaceType::kRect; @@ -176,7 +205,10 @@ void SkClipStack::Element::initRRect(int saveCount, const SkRRect& rrect, const this->initCommon(saveCount, op, doAA); return; } - this->initAsPath(saveCount, SkPath::RRect(rrect), m, op, doAA); + SkPath path; + path.addRRect(rrect); + path.setIsVolatile(true); + this->initAsPath(saveCount, path, m, op, doAA); } void SkClipStack::Element::initPath(int saveCount, const SkPath& path, const SkMatrix& m, @@ -200,11 +232,8 @@ void SkClipStack::Element::initPath(int saveCount, const SkPath& path, const SkM void SkClipStack::Element::initAsPath(int saveCount, const SkPath& path, const SkMatrix& m, SkClipOp op, bool doAA) { - SkPathBuilder builder(path); - builder.transform(m); - builder.setIsVolatile(true); - fDeviceSpacePath = builder.detach(); - + path.transform(m, fDeviceSpacePath.init()); + fDeviceSpacePath->setIsVolatile(true); fDeviceSpaceType = DeviceSpaceType::kPath; this->initCommon(saveCount, op, doAA); } @@ -223,24 +252,28 @@ void SkClipStack::Element::initReplaceRect(int saveCount, const SkRect& rect, bo fIsReplace = true; } -SkPath SkClipStack::Element::asDeviceSpacePath() const { - SkPathBuilder builder; +void SkClipStack::Element::asDeviceSpacePath(SkPath* path) const { switch (fDeviceSpaceType) { case DeviceSpaceType::kEmpty: + path->reset(); break; case DeviceSpaceType::kRect: - builder.addRect(this->getDeviceSpaceRect()); + path->reset(); + path->addRect(this->getDeviceSpaceRect()); break; case DeviceSpaceType::kRRect: - builder.addRRect(fDeviceSpaceRRect); + path->reset(); + path->addRRect(fDeviceSpaceRRect); break; case DeviceSpaceType::kPath: - return *fDeviceSpacePath; + *path = *fDeviceSpacePath; + break; case DeviceSpaceType::kShader: - builder.addRect(SkRectPriv::MakeLargeS32()); + path->reset(); + path->addRect(SkRectPriv::MakeLargeS32()); break; } - return builder.detach(); + path->setIsVolatile(true); } void SkClipStack::Element::setEmpty() { @@ -261,7 +294,7 @@ void SkClipStack::Element::checkEmpty() const { SkASSERT(!fIsIntersectionOfRects); SkASSERT(kEmptyGenID == fGenID); SkASSERT(fDeviceSpaceRRect.isEmpty()); - SkASSERT(!fDeviceSpacePath.has_value()); + SkASSERT(!fDeviceSpacePath.isValid()); SkASSERT(!fShader); } diff --git a/gfx/skia/skia/src/core/SkClipStack.h b/gfx/skia/skia/src/core/SkClipStack.h @@ -18,10 +18,10 @@ #include "include/private/base/SkAssert.h" #include "include/private/base/SkDebug.h" #include "include/private/base/SkDeque.h" +#include "src/base/SkTLazy.h" #include <cstddef> #include <cstdint> -#include <optional> #include <utility> // Because a single save/restore state can have multiple clips, this class @@ -138,7 +138,7 @@ public: bool isReplaceOp() const { return fIsReplace; } //!< Call to get the element as a path, regardless of its type. - SkPath asDeviceSpacePath() const; + void asDeviceSpacePath(SkPath* path) const; //!< Call if getType() is not kPath to get the element as a round rect. const SkRRect& asDeviceSpaceRRect() const { @@ -150,6 +150,9 @@ public: when it is rasterized. */ bool isAA() const { return fDoAA; } + //!< Inverts the fill of the clip shape. Note that a kEmpty element remains kEmpty. + void invertShapeFillType(); + /** The GenID can be used by clip stack clients to cache representations of the clip. The ID corresponds to the set of clip elements up to and including this element within the stack not to the element itself. That is the same clip path in different stacks will @@ -189,7 +192,7 @@ public: private: friend class SkClipStack; - std::optional<SkPath> fDeviceSpacePath; + SkTLazy<SkPath> fDeviceSpacePath; SkRRect fDeviceSpaceRRect; sk_sp<SkShader> fShader; int fSaveCount; // save count of stack when this element was added. diff --git a/gfx/skia/skia/src/core/SkClipStackDevice.cpp b/gfx/skia/skia/src/core/SkClipStackDevice.cpp @@ -10,7 +10,6 @@ #include "include/core/SkImageInfo.h" #include "include/core/SkMatrix.h" #include "include/core/SkPath.h" -#include "include/core/SkPathBuilder.h" #include "include/core/SkPoint.h" #include "include/core/SkRegion.h" #include "include/core/SkShader.h" @@ -59,10 +58,10 @@ void SkClipStackDevice::onClipShader(sk_sp<SkShader> shader) { void SkClipStackDevice::clipRegion(const SkRegion& rgn, SkClipOp op) { SkIPoint origin = this->getOrigin(); SkRegion tmp; - SkPathBuilder builder; - rgn.addBoundaryPath(&builder); - builder.transform(SkMatrix::Translate(-origin)); - fClipStack.clipPath(builder.detach(), SkMatrix::I(), op, false); + SkPath path; + rgn.getBoundaryPath(&path); + path.transform(SkMatrix::Translate(-origin)); + fClipStack.clipPath(path, SkMatrix::I(), op, false); } void SkClipStackDevice::replaceClip(const SkIRect& rect) { @@ -113,11 +112,13 @@ void SkClipStackDevice::android_utils_clipAsRgn(SkRegion* rgn) const { rgn->setRect(bounds.round()); } else { SkRegion boundsRgn({0, 0, this->width(), this->height()}); + SkPath tmpPath; *rgn = boundsRgn; SkClipStack::B2TIter iter(fClipStack); while (auto elem = iter.next()) { - SkPath tmpPath = elem->asDeviceSpacePath(); + tmpPath.rewind(); + elem->asDeviceSpacePath(&tmpPath); SkRegion tmpRgn; tmpRgn.setPath(tmpPath, boundsRgn); if (elem->isReplaceOp()) { diff --git a/gfx/skia/skia/src/core/SkColorSpace.cpp b/gfx/skia/skia/src/core/SkColorSpace.cpp @@ -15,61 +15,47 @@ #include <cmath> #include <cstring> -static bool xyz_almost_equal(const skcms_Matrix3x3& mA, const skcms_Matrix3x3& mB) { - for (int r = 0; r < 3; ++r) { - for (int c = 0; c < 3; ++c) { - if (!color_space_almost_equal(mA.vals[r][c], mB.vals[r][c])) { - return false; - } - } - } - - return true; -} - namespace SkNamedPrimaries { -// Rec. ITU-T H.273, Table 2. -struct TableEntry { - CicpId cicp_id; - SkColorSpacePrimaries sk_primaries; -}; -const static TableEntry cicp_table[] = { - { CicpId::kRec709, kRec709 }, - { CicpId::kRec470SystemM, kRec470SystemM }, - { CicpId::kRec470SystemBG, kRec470SystemBG }, - { CicpId::kRec601, kRec601 }, - { CicpId::kSMPTE_ST_240, kSMPTE_ST_240 }, - { CicpId::kGenericFilm, kGenericFilm }, - { CicpId::kRec2020, kRec2020 }, - { CicpId::kSMPTE_ST_428_1, kSMPTE_ST_428_1 }, - { CicpId::kSMPTE_RP_431_2, kSMPTE_RP_431_2 }, - { CicpId::kSMPTE_EG_432_1, kSMPTE_EG_432_1 }, - { CicpId::kITU_T_H273_Value22, kITU_T_H273_Value22 }, -}; -// Value 2 indicates "characteristics are unknown or are determined by the application". In -// practice, this means we will delegate the primaries to the toXYZD50 tags. -uint8_t kCicpIdApplicationDefined = 2; - bool GetCicp(CicpId primaries, SkColorSpacePrimaries& sk_primaries) { - for (const auto& table_entry : cicp_table) { - if (primaries == table_entry.cicp_id) { - sk_primaries = table_entry.sk_primaries; + // Rec. ITU-T H.273, Table 2. + switch (primaries) { + case CicpId::kRec709: + sk_primaries = kRec709; return true; - } - } - return false; -} - -bool GetCicpFromMatrix(const skcms_Matrix3x3& m, CicpId& primaries) { - for (const auto& table_entry : cicp_table) { - skcms_Matrix3x3 table_entry_m; - if (table_entry.sk_primaries.toXYZD50(&table_entry_m)) { - if (xyz_almost_equal(m, table_entry_m)) { - primaries = table_entry.cicp_id; - 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; } @@ -78,36 +64,51 @@ bool GetCicpFromMatrix(const skcms_Matrix3x3& m, CicpId& primaries) { namespace SkNamedTransferFn { -// Rec. ITU-T H.273, Table 3. -struct TableEntry { - CicpId cicp_id; - skcms_TransferFunction trfn; -}; -const static TableEntry cicp_table[] = { - { SkNamedTransferFn::CicpId::kRec709, kRec709 }, - { SkNamedTransferFn::CicpId::kRec470SystemM, kRec470SystemM }, - { SkNamedTransferFn::CicpId::kRec470SystemBG, kRec470SystemBG }, - { SkNamedTransferFn::CicpId::kRec601, kRec601 }, - { SkNamedTransferFn::CicpId::kSMPTE_ST_240, kSMPTE_ST_240 }, - { SkNamedTransferFn::CicpId::kLinear, SkNamedTransferFn::kLinear }, - { SkNamedTransferFn::CicpId::kIEC61966_2_4, kIEC61966_2_4 }, - { SkNamedTransferFn::CicpId::kIEC61966_2_1, SkNamedTransferFn::kIEC61966_2_1 }, - { SkNamedTransferFn::CicpId::kRec2020_10bit, kRec2020_10bit }, - { SkNamedTransferFn::CicpId::kRec2020_12bit, kRec2020_12bit }, - { SkNamedTransferFn::CicpId::kPQ, SkNamedTransferFn::kPQ }, - { SkNamedTransferFn::CicpId::kSMPTE_ST_428_1, kSMPTE_ST_428_1 }, - { SkNamedTransferFn::CicpId::kHLG, SkNamedTransferFn::kHLG }, -}; -// Value 2 indicates "characteristics are unknown or are determined by the application". In -// practice, this means we will delegate the primaries to the trc tags. -uint8_t kCicpIdApplicationDefined = 2; - bool GetCicp(SkNamedTransferFn::CicpId transfer_characteristics, skcms_TransferFunction& trfn) { - for (const auto& table_entry : cicp_table) { - if (transfer_characteristics == table_entry.cicp_id) { - trfn = table_entry.trfn; - return true; - } + // 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; } @@ -126,6 +127,18 @@ SkColorSpace::SkColorSpace(const skcms_TransferFunction& transferFn, fToXYZD50Hash = SkChecksum::Hash32(&fToXYZD50, 9*sizeof(float)); } +static bool xyz_almost_equal(const skcms_Matrix3x3& mA, const skcms_Matrix3x3& mB) { + for (int r = 0; r < 3; ++r) { + for (int c = 0; c < 3; ++c) { + if (!color_space_almost_equal(mA.vals[r][c], mB.vals[r][c])) { + return false; + } + } + } + + return true; +} + sk_sp<SkColorSpace> SkColorSpace::MakeRGB(const skcms_TransferFunction& transferFn, const skcms_Matrix3x3& toXYZ) { if (skcms_TransferFunction_getType(&transferFn) == skcms_TFType_Invalid) { @@ -293,118 +306,43 @@ sk_sp<SkColorSpace> SkColorSpace::makeColorSpin() const { void SkColorSpace::toProfile(skcms_ICCProfile* profile) const { skcms_Init (profile); - // TODO(https://issues.skia.org/issues/420956739): This value should only be - // set for sRGB-ish transfer functions. All other values are invalid. skcms_SetTransferFunction(profile, &fTransferFn); skcms_SetXYZD50 (profile, &fToXYZD50); - - switch (skcms_TransferFunction_getType(&fTransferFn)) { - case skcms_TFType_PQ: - case skcms_TFType_PQish: - profile->has_CICP = true; - profile->CICP.transfer_characteristics = - static_cast<uint8_t>(SkNamedTransferFn::CicpId::kPQ); - break; - case skcms_TFType_HLG: - case skcms_TFType_HLGish: - profile->has_CICP = true; - profile->CICP.transfer_characteristics = - static_cast<uint8_t>(SkNamedTransferFn::CicpId::kHLG); - break; - default: - break; - } - if (profile->has_CICP) { - profile->CICP.matrix_coefficients = 0; - profile->CICP.video_full_range_flag = 1; - SkNamedPrimaries::CicpId primaries_id = SkNamedPrimaries::CicpId::kRec709; - if (SkNamedPrimaries::GetCicpFromMatrix(fToXYZD50, primaries_id)) { - profile->CICP.color_primaries = static_cast<uint8_t>(primaries_id); - } else { - profile->CICP.color_primaries = SkNamedPrimaries::kCicpIdApplicationDefined; - } - } } sk_sp<SkColorSpace> SkColorSpace::Make(const skcms_ICCProfile& profile) { - // The CICP values are only valid for full-range, with no matrix. - bool use_cicp = profile.has_CICP && - profile.CICP.matrix_coefficients == 0 && - profile.CICP.video_full_range_flag == 1; - auto cicp_color_primaries = static_cast<SkNamedPrimaries::CicpId>(profile.CICP.color_primaries); - auto cicp_transfer_characteristics = - static_cast<SkNamedTransferFn::CicpId>(profile.CICP.transfer_characteristics); - - // Early checks for exact sRGB matches. - if (use_cicp && - cicp_color_primaries == SkNamedPrimaries::CicpId::kRec709 && - cicp_transfer_characteristics == SkNamedTransferFn::CicpId::kIEC61966_2_4) { - return SkColorSpace::MakeSRGB(); - } else if (skcms_ApproximatelyEqualProfiles(&profile, skcms_sRGB_profile())) { - return SkColorSpace::MakeSRGB(); + // TODO: move below ≈sRGB test? + if (!profile.has_toXYZD50 || !profile.has_trc) { + return nullptr; } - // Set the toXYZD50 matrix, preferring CICP over the matrix itself. - skcms_Matrix3x3 toXYZD50; - bool hasSetToXYZD50 = false; - if (use_cicp) { - SkColorSpacePrimaries primaries; - if (SkNamedPrimaries::GetCicp(cicp_color_primaries, primaries)) { - if (primaries.toXYZD50(&toXYZD50)) { - hasSetToXYZD50 = true; - } - } else if (profile.CICP.color_primaries != SkNamedPrimaries::kCicpIdApplicationDefined) { - return nullptr; - } - } - if (profile.has_toXYZD50 && !hasSetToXYZD50) { - // TODO: can we save this work and skip lazily inverting the matrix later? - skcms_Matrix3x3 inv; - toXYZD50 = profile.toXYZD50; - if (skcms_Matrix3x3_invert(&toXYZD50, &inv)) { - hasSetToXYZD50 = true; - } + if (skcms_ApproximatelyEqualProfiles(&profile, skcms_sRGB_profile())) { + return SkColorSpace::MakeSRGB(); } - if (!hasSetToXYZD50) { + + // TODO: can we save this work and skip lazily inverting the matrix later? + skcms_Matrix3x3 inv; + if (!skcms_Matrix3x3_invert(&profile.toXYZD50, &inv)) { return nullptr; } - // Set the transfer function, preferring CICP over the curves. - skcms_TransferFunction trfn; - bool hasSetTrfn = false; - if (use_cicp) { - if (SkNamedTransferFn::GetCicp(cicp_transfer_characteristics, trfn)) { - hasSetTrfn = true; - } else if (profile.CICP.transfer_characteristics != - SkNamedTransferFn::kCicpIdApplicationDefined) { - return nullptr; + // We can't work with tables or mismatched parametric curves, + // but if they all look close enough to sRGB, that's fine. + // TODO: should we maybe do this unconditionally to snap near-sRGB parametrics to sRGB? + const skcms_Curve* trc = profile.trc; + if (trc[0].table_entries != 0 || + trc[1].table_entries != 0 || + trc[2].table_entries != 0 || + 0 != memcmp(&trc[0].parametric, &trc[1].parametric, sizeof(trc[0].parametric)) || + 0 != memcmp(&trc[0].parametric, &trc[2].parametric, sizeof(trc[0].parametric))) + { + if (skcms_TRCs_AreApproximateInverse(&profile, skcms_sRGB_Inverse_TransferFunction())) { + return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, profile.toXYZD50); } - } - if (profile.has_trc && !hasSetTrfn) { - // We can't work with tables or mismatched parametric curves. - const skcms_Curve* trc = profile.trc; - if (trc[0].table_entries == 0 && - trc[1].table_entries == 0 && - trc[2].table_entries == 0 && - 0 == memcmp(&trc[0].parametric, &trc[1].parametric, sizeof(trc[0].parametric)) && - 0 == memcmp(&trc[0].parametric, &trc[2].parametric, sizeof(trc[0].parametric))) - { - trfn = profile.trc[0].parametric; - hasSetTrfn = true; - } else { - // If all curves look close enough to sRGB, that's fine. - // TODO: should we maybe do this unconditionally to snap near-sRGB parametrics to sRGB? - if (skcms_TRCs_AreApproximateInverse(&profile, skcms_sRGB_Inverse_TransferFunction())) { - trfn = SkNamedTransferFn::kSRGB; - hasSetTrfn = true; - } - } - } - if (!hasSetTrfn) { return nullptr; } - return SkColorSpace::MakeRGB(trfn, toXYZD50); + return SkColorSpace::MakeRGB(profile.trc[0].parametric, profile.toXYZD50); } /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/gfx/skia/skia/src/core/SkColorSpaceXformSteps.cpp b/gfx/skia/skia/src/core/SkColorSpaceXformSteps.cpp @@ -9,7 +9,6 @@ #include "include/core/SkAlphaType.h" #include "include/core/SkColorSpace.h" -#include "include/core/SkRefCnt.h" #include "include/core/SkTypes.h" #include "include/private/base/SkFloatingPoint.h" #include "modules/skcms/skcms.h" @@ -17,27 +16,10 @@ #include "src/core/SkRasterPipeline.h" #include "src/core/SkRasterPipelineOpList.h" -#include <cmath> #include <cstring> // See skia.org/docs/user/color (== site/docs/user/color.md). -// Compute the Y vector for the HLG OOTF in the primaries of a specified color space. The value -// is specified in Rec2020 primaries in ITU-R BT.2100. -static void set_ootf_Y(const SkColorSpace* cs, float* Y) { - skcms_Matrix3x3 m; - cs->gamutTransformTo( - SkColorSpace::MakeRGB(SkNamedTransferFn::kLinear, SkNamedGamut::kRec2020).get(), - &m); - constexpr float Y_rec2020[3] = {0.262700f, 0.678000f, 0.059300f}; - for (int i = 0; i < 3; ++i) { - Y[i] = 0.f; - for (int j = 0; j < 3; ++j) { - Y[i] += m.vals[j][i] * Y_rec2020[j]; - } - } -} - SkColorSpaceXformSteps::SkColorSpaceXformSteps(const SkColorSpace* src, SkAlphaType srcAT, const SkColorSpace* dst, SkAlphaType dstAT) { // Opaque outputs are treated as the same alpha type as the source input. @@ -56,104 +38,27 @@ SkColorSpaceXformSteps::SkColorSpaceXformSteps(const SkColorSpace* src, SkAlphaT return; } - skcms_TransferFunction srcTrfn; - src->transferFn(&srcTrfn); - skcms_TransferFunction dstTrfn; - dst->transferFn(&dstTrfn); - - // The scale factor is the amount that values in linear space will be scaled to accommodate - // peak luminance and HDR reference white luminance. - float scaleFactor = 1.f; - - // TODO(https://issues.skia.org/issues/420956739): Inline the constants for PQ and HLG transfer - // functions when the PQish and HLGish transfer functions are no longer in use. - static constexpr skcms_TransferFunction kPQish = - {-2.0f, -107/128.0f, 1.0f, 32/2523.0f, 2413/128.0f, -2392/128.0f, 8192/1305.0f }; - static constexpr skcms_TransferFunction kHLGish = - {-3.0f, 2.0f, 2.0f, 1/0.17883277f, 0.28466892f, 0.55991073f, 0.0f }; - skcms_TFType srcTfType = skcms_TransferFunction_getType(&srcTrfn); - switch (srcTfType) { - case skcms_TFType_PQ: - // PQ is always scaled by a peak luminance of 10,000 nits, then divided by the HDR - // reference white luminance (a). - scaleFactor *= 10000.f / srcTrfn.a; - // Use the default PQish transfer function. - this->fSrcTF = kPQish; - this->fFlags.linearize = true; - break; - case skcms_TFType_HLG: - // HLG is scaled by the peak luminance (b), then divided by the HDR reference white - // luminance (a). - scaleFactor *= srcTrfn.b / srcTrfn.a; - this->fFlags.linearize = true; - // Use the HLGish transfer function scaled by 1/12. - this->fSrcTF = kHLGish; - this->fSrcTF.f = 1/12.f - 1.f; - // If the system gamma is not 1.0, then compute the parameters for the OOTF. - if (srcTrfn.c != 1.f) { - this->fFlags.src_ootf = true; - this->fSrcOotf[3] = srcTrfn.c - 1.f; - set_ootf_Y(src, this->fSrcOotf); - } - break; - default: - this->fFlags.linearize = memcmp(&srcTrfn, &SkNamedTransferFn::kLinear, sizeof(srcTrfn)) != 0; - if (this->fFlags.linearize) { - src->transferFn(&this->fSrcTF); - } - break; - } - - skcms_TFType dstTfType = skcms_TransferFunction_getType(&dstTrfn); - switch (dstTfType) { - case skcms_TFType_PQ: - // This is the inverse of the treatment of source PQ. - scaleFactor /= 10000.f / dstTrfn.a; - this->fFlags.encode = true; - this->fDstTFInv = kPQish; - skcms_TransferFunction_invert(&this->fDstTFInv, &this->fDstTFInv); - break; - case skcms_TFType_HLG: - // This is the inverse of the treatment of source HLG. - scaleFactor /= dstTrfn.b / dstTrfn.a; - this->fFlags.encode = true; - this->fDstTFInv = kHLGish; - this->fDstTFInv.f = 1/12.f - 1.f; - skcms_TransferFunction_invert(&this->fDstTFInv, &this->fDstTFInv); - if (dstTrfn.c != 1.f) { - this->fFlags.dst_ootf = true; - this->fDstOotf[3] = 1/dstTrfn.c - 1.f; - set_ootf_Y(dst, this->fDstOotf); - } - break; - default: - this->fFlags.encode = memcmp(&dstTrfn, &SkNamedTransferFn::kLinear, sizeof(dstTrfn)) != 0; - if (this->fFlags.encode) { - dst->invTransferFn(&this->fDstTFInv); - } - break; - } - this->fFlags.unpremul = srcAT == kPremul_SkAlphaType; - this->fFlags.gamut_transform = src->toXYZD50Hash() != dst->toXYZD50Hash() || - scaleFactor != 1.f; + 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->fFlags.gamut_transform) { skcms_Matrix3x3 src_to_dst; // TODO: switch fSrcToDstMatrix to row-major src->gamutTransformTo(dst, &src_to_dst); - this->fSrcToDstMatrix[0] = src_to_dst.vals[0][0] * scaleFactor; - this->fSrcToDstMatrix[1] = src_to_dst.vals[1][0] * scaleFactor; - this->fSrcToDstMatrix[2] = src_to_dst.vals[2][0] * scaleFactor; + 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->fSrcToDstMatrix[3] = src_to_dst.vals[0][1] * scaleFactor; - this->fSrcToDstMatrix[4] = src_to_dst.vals[1][1] * scaleFactor; - this->fSrcToDstMatrix[5] = src_to_dst.vals[2][1] * scaleFactor; + 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->fSrcToDstMatrix[6] = src_to_dst.vals[0][2] * scaleFactor; - this->fSrcToDstMatrix[7] = src_to_dst.vals[1][2] * scaleFactor; - this->fSrcToDstMatrix[8] = src_to_dst.vals[2][2] * scaleFactor; + 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; @@ -163,36 +68,21 @@ SkColorSpaceXformSteps::SkColorSpaceXformSteps(const SkColorSpace* src, SkAlphaT #endif } - // If the source and destination OOTFs cancel each other out, skip both. - if ( this->fFlags.src_ootf && - !this->fFlags.gamut_transform && - this->fFlags.dst_ootf) { - // If there is no gamut transform, then the r,g,b coefficients for the - // OOTFs must be the same. - SkASSERT(0 == memcmp(&this->fSrcOotf, &this->fDstOotf, 3*sizeof(float))); - // If the gammas cancel out, then remove the steps. - if ((this->fSrcOotf[3] + 1.f) * (this->fDstOotf[3] + 1.f) == 1.f) { - this->fFlags.src_ootf = false; - this->fFlags.dst_ootf = false; - } - } + // Fill out all the transfer functions we'll use. + src-> transferFn(&this->fSrcTF ); + dst->invTransferFn(&this->fDstTFInv); // If we linearize then immediately reencode with the same transfer function, skip both. if ( this->fFlags.linearize && - !this->fFlags.src_ootf && !this->fFlags.gamut_transform && - !this->fFlags.dst_ootf && this->fFlags.encode && src->transferFnHash() == dst->transferFnHash()) { #ifdef SK_DEBUG - // PQ and HLG types use PQish and HLGish for fSrcTF, so this check is not valid for them. - if (srcTfType != skcms_TFType_PQ && srcTfType != skcms_TFType_HLG) { - skcms_TransferFunction dstTF; - dst->transferFn(&dstTF); - for (int i = 0; i < 7; i++) { - SkASSERT( (&fSrcTF.g)[i] == (&dstTF.g)[i] && "Hash collision" ); - } + skcms_TransferFunction dstTF; + dst->transferFn(&dstTF); + for (int i = 0; i < 7; i++) { + SkASSERT( (&fSrcTF.g)[i] == (&dstTF.g)[i] && "Hash collision" ); } #endif this->fFlags.linearize = false; @@ -226,15 +116,6 @@ void SkColorSpaceXformSteps::apply(float* rgba) const { rgba[1] = skcms_TransferFunction_eval(&fSrcTF, rgba[1]); rgba[2] = skcms_TransferFunction_eval(&fSrcTF, rgba[2]); } - if (this->fFlags.src_ootf) { - const float Y = fSrcOotf[0] * rgba[0] + - fSrcOotf[1] * rgba[1] + - fSrcOotf[2] * rgba[2]; - const float Y_to_gamma_minus_1 = std::pow(Y, fSrcOotf[3]); - rgba[0] *= Y_to_gamma_minus_1; - rgba[1] *= Y_to_gamma_minus_1; - rgba[2] *= Y_to_gamma_minus_1; - } if (this->fFlags.gamut_transform) { float temp[3] = { rgba[0], rgba[1], rgba[2] }; for (int i = 0; i < 3; ++i) { @@ -243,15 +124,6 @@ void SkColorSpaceXformSteps::apply(float* rgba) const { fSrcToDstMatrix[6 + i] * temp[2]; } } - if (this->fFlags.dst_ootf) { - const float Y = fDstOotf[0] * rgba[0] + - fDstOotf[1] * rgba[1] + - fDstOotf[2] * rgba[2]; - const float Y_to_gamma_minus_1 = std::pow(Y, fDstOotf[3]); - rgba[0] *= Y_to_gamma_minus_1; - rgba[1] *= Y_to_gamma_minus_1; - rgba[2] *= Y_to_gamma_minus_1; - } if (this->fFlags.encode) { rgba[0] = skcms_TransferFunction_eval(&fDstTFInv, rgba[0]); rgba[1] = skcms_TransferFunction_eval(&fDstTFInv, rgba[1]); @@ -267,9 +139,7 @@ void SkColorSpaceXformSteps::apply(float* rgba) const { void SkColorSpaceXformSteps::apply(SkRasterPipeline* p) const { if (this->fFlags.unpremul) { p->append(SkRasterPipelineOp::unpremul); } if (this->fFlags.linearize) { p->appendTransferFunction(fSrcTF); } - if (this->fFlags.src_ootf) { p->append(SkRasterPipelineOp::ootf, fSrcOotf); } if (this->fFlags.gamut_transform) { p->append(SkRasterPipelineOp::matrix_3x3, &fSrcToDstMatrix); } - if (this->fFlags.dst_ootf) { p->append(SkRasterPipelineOp::ootf, fDstOotf); } 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 @@ -21,18 +21,14 @@ struct SkColorSpaceXformSteps { struct Flags { bool unpremul = false; bool linearize = false; - bool src_ootf = false; bool gamut_transform = false; - bool dst_ootf = false; bool encode = false; bool premul = false; constexpr uint32_t mask() const { return (unpremul ? 1 : 0) | (linearize ? 2 : 0) - | (src_ootf ? 32 : 0) | (gamut_transform ? 4 : 0) - | (dst_ootf ? 64 : 0) | (encode ? 8 : 0) | (premul ? 16 : 0); } @@ -55,10 +51,6 @@ struct SkColorSpaceXformSteps { skcms_TransferFunction fSrcTF, // Apply for linearize. fDstTFInv; // Apply for encode. float fSrcToDstMatrix[9]; // Apply this 3x3 *column*-major matrix for gamut_transform. - float fSrcOotf[4]; // Apply ootf with these r,g,b coefficients and gamma before - // gamut_transform. - float fDstOotf[4]; // Apply ootf with these r,g,b coefficients and gamma after - // gamut_transform. }; #endif//SkColorSpaceXformSteps_DEFINED diff --git a/gfx/skia/skia/src/core/SkCompressedDataUtils.cpp b/gfx/skia/skia/src/core/SkCompressedDataUtils.cpp @@ -257,7 +257,7 @@ size_t SkCompressedDataSize(SkTextureCompressionType type, SkISize dimensions, int numMipLevels = 1; if (mipmapped) { - numMipLevels = SkMipmap::ComputeLevelCount(dimensions) + 1; + numMipLevels = SkMipmap::ComputeLevelCount(dimensions.width(), dimensions.height()) + 1; } size_t totalSize = 0; diff --git a/gfx/skia/skia/src/core/SkContourMeasure.cpp b/gfx/skia/skia/src/core/SkContourMeasure.cpp @@ -9,8 +9,8 @@ #include "include/core/SkMatrix.h" #include "include/core/SkPath.h" -#include "include/core/SkPathBuilder.h" #include "include/core/SkPathTypes.h" +#include "include/private/base/SkAssert.h" #include "include/private/base/SkDebug.h" #include "include/private/base/SkFloatingPoint.h" #include "include/private/base/SkTo.h" @@ -21,7 +21,6 @@ #include <algorithm> #include <array> #include <cstddef> -#include <optional> #include <utility> #define kMaxTValue 0x3FFFFFFF @@ -41,7 +40,7 @@ SkScalar SkContourMeasure::Segment::getScalarT() const { } void SkContourMeasure_segTo(const SkPoint pts[], unsigned segType, - SkScalar startT, SkScalar stopT, SkPathBuilder* dst) { + SkScalar startT, SkScalar stopT, SkPath* dst) { SkASSERT(startT >= 0 && startT <= SK_Scalar1); SkASSERT(stopT >= 0 && stopT <= SK_Scalar1); SkASSERT(startT <= stopT); @@ -50,9 +49,9 @@ void SkContourMeasure_segTo(const SkPoint pts[], unsigned segType, if (!dst->isEmpty()) { /* if the dash as a zero-length on segment, add a corresponding zero-length line. The stroke code will add end caps to zero length lines as appropriate */ - auto lastPtOptional = dst->getLastPt(); - SkAssertResult(lastPtOptional.has_value()); - dst->lineTo(lastPtOptional.value()); + SkPoint lastPt; + SkAssertResult(dst->getLastPt(&lastPt)); + dst->lineTo(lastPt); } return; } @@ -656,7 +655,7 @@ bool SkContourMeasure::getMatrix(SkScalar distance, SkMatrix* matrix, MatrixFlag return false; } -bool SkContourMeasure::getSegment(SkScalar startD, SkScalar stopD, SkPathBuilder* dst, +bool SkContourMeasure::getSegment(SkScalar startD, SkScalar stopD, SkPath* dst, bool startWithMoveTo) const { SkASSERT(dst); @@ -733,15 +732,3 @@ SkContourMeasure::VerbMeasure SkContourMeasure::ForwardVerbIterator::operator*() SkSpan(fPts.data() + fSegments.front().fPtIndex, seg_pt_count[fSegments.front().fType]), }; } - -#ifdef SK_SUPPORT_MUTABLE_PATHEFFECT -bool SkContourMeasure::getSegment(SkScalar startD, SkScalar stopD, SkPath* dst, - bool startWithMoveTo) const { - SkPathBuilder builder; - if (this->getSegment(startD, stopD, &builder, startWithMoveTo)) { - *dst = builder.detach(); - return true; - } - return false; -} -#endif diff --git a/gfx/skia/skia/src/core/SkCoreBlitters.h b/gfx/skia/skia/src/core/SkCoreBlitters.h @@ -19,7 +19,6 @@ #include "src/shaders/SkShaderBase.h" #include <cstdint> -#include <optional> class SkArenaAlloc; class SkMatrix; @@ -82,7 +81,6 @@ public: 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; - std::optional<DirectBlit> canDirectBlit() override; }; class SkARGB32_Black_Blitter : public SkARGB32_Opaque_Blitter { @@ -121,8 +119,7 @@ SkBlitter* SkCreateRasterPipelineBlitter(const SkPixmap&, const SkMatrix& ctm, SkArenaAlloc*, sk_sp<SkShader> clipShader, - const SkSurfaceProps& props, - const SkRect& devBounds); + const SkSurfaceProps& props); // Use this if you've pre-baked a shader pipeline, including modulating with paint alpha. SkBlitter* SkCreateRasterPipelineBlitter(const SkPixmap&, const SkPaint&, const SkRasterPipeline& shaderPipeline, diff --git a/gfx/skia/skia/src/core/SkData.cpp b/gfx/skia/skia/src/core/SkData.cpp @@ -6,7 +6,6 @@ */ #include "include/core/SkData.h" -#include "include/core/SkSpan.h" #include "include/core/SkStream.h" #include "include/private/base/SkAssert.h" @@ -15,14 +14,14 @@ #include "src/core/SkOSFile.h" #include "src/core/SkStreamPriv.h" -#include <cstddef> #include <cstring> #include <new> -SkData::SkData(SkSpan<std::byte> span, ReleaseProc proc, void* context) +SkData::SkData(const void* ptr, size_t size, ReleaseProc proc, void* context) : fReleaseProc(proc) , fReleaseProcContext(context) - , fSpan(span) + , fPtr(ptr) + , fSize(size) {} /** This constructor means we are inline with our fPtr's contents. @@ -31,24 +30,28 @@ SkData::SkData(SkSpan<std::byte> span, ReleaseProc proc, void* context) SkData::SkData(size_t size) : fReleaseProc(nullptr) , fReleaseProcContext(nullptr) - , fSpan{(std::byte*)(this + 1), size} + , fPtr((const char*)(this + 1)) + , fSize(size) {} SkData::~SkData() { if (fReleaseProc) { - fReleaseProc(fSpan.data(), fReleaseProcContext); + fReleaseProc(fPtr, fReleaseProcContext); } } -bool SkData::operator==(const SkData& other) const { - if (this == &other) { +bool SkData::equals(const SkData* other) const { + if (this == other) { return true; } - return size() == other.size() && !sk_careful_memcmp(data(), other.data(), size()); + if (nullptr == other) { + return false; + } + return fSize == other->fSize && !sk_careful_memcmp(fPtr, other->fPtr, fSize); } size_t SkData::copyRange(size_t offset, size_t length, void* buffer) const { - size_t available = this->size(); + size_t available = fSize; if (offset >= available || 0 == length) { return 0; } @@ -64,42 +67,6 @@ size_t SkData::copyRange(size_t offset, size_t length, void* buffer) const { return length; } - -#define RETURN_EMPTY_FOR_ZERO_SIZE(size) \ - do { \ - if ((size) == 0) { \ - return SkData::MakeEmpty(); \ - } \ - } while (false) - -#define VALIDATE_SUBSET(size, offset, length) \ - do { \ - if (offset > size || length > size - offset) { \ - return nullptr; \ - } \ - } while (0) - -sk_sp<SkData> SkData::shareSubset(size_t offset, size_t length) { - VALIDATE_SUBSET(this->size(), offset, length); - - if (offset == 0 && length == this->size()) { - return sk_ref_sp(this); - } - - RETURN_EMPTY_FOR_ZERO_SIZE(length); - - this->ref(); - return SkData::MakeWithProc(this->bytes() + offset, length, [](const void*, void* ctx) { - ((SkData*)ctx)->unref(); - }, this); -} - -sk_sp<SkData> SkData::copySubset(size_t offset, size_t length) const { - VALIDATE_SUBSET(this->size(), offset, length); - - return SkData::MakeWithCopy(this->bytes() + offset, length); -} - void SkData::operator delete(void* p) { ::operator delete(p); } @@ -128,7 +95,7 @@ sk_sp<SkData> SkData::MakeEmpty() { static SkOnce once; static SkData* empty; - once([]{ empty = new SkData({}, nullptr, nullptr); }); + once([]{ empty = new SkData(nullptr, 0, nullptr, nullptr); }); return sk_ref_sp(empty); } @@ -138,8 +105,7 @@ static void sk_free_releaseproc(const void* ptr, void*) { } sk_sp<SkData> SkData::MakeFromMalloc(const void* data, size_t length) { - std::byte* ptr = static_cast<std::byte*>(const_cast<void*>(data)); - return sk_sp<SkData>(new SkData({ptr, length}, sk_free_releaseproc, nullptr)); + return sk_sp<SkData>(new SkData(data, length, sk_free_releaseproc, nullptr)); } sk_sp<SkData> SkData::MakeWithCopy(const void* src, size_t length) { @@ -159,9 +125,8 @@ sk_sp<SkData> SkData::MakeZeroInitialized(size_t length) { return data; } -sk_sp<SkData> SkData::MakeWithProc(const void* data, size_t length, ReleaseProc proc, void* ctx) { - std::byte* ptr = static_cast<std::byte*>(const_cast<void*>(data)); - return sk_sp<SkData>(new SkData({ptr, length}, proc, ctx)); +sk_sp<SkData> SkData::MakeWithProc(const void* ptr, size_t length, ReleaseProc proc, void* ctx) { + return sk_sp<SkData>(new SkData(ptr, length, proc, ctx)); } // assumes fPtr was allocated with sk_fmmap @@ -199,6 +164,34 @@ sk_sp<SkData> SkData::MakeFromFD(int fd) { return SkData::MakeWithProc(addr, size, sk_mmap_releaseproc, reinterpret_cast<void*>(size)); } +// assumes context is a SkData +static void sk_dataref_releaseproc(const void*, void* context) { + SkData* src = reinterpret_cast<SkData*>(context); + src->unref(); +} + +sk_sp<SkData> SkData::MakeSubset(const SkData* src, size_t offset, size_t length) { + /* + We could, if we wanted/need to, just make a deep copy of src's data, + rather than referencing it. This would duplicate the storage (of the + subset amount) but would possibly allow src to go out of scope sooner. + */ + + size_t available = src->size(); + if (offset >= available || 0 == length) { + return SkData::MakeEmpty(); + } + available -= offset; + if (length > available) { + length = available; + } + SkASSERT(length > 0); + + src->ref(); // this will be balanced in sk_dataref_releaseproc + return sk_sp<SkData>(new SkData(src->bytes() + offset, length, sk_dataref_releaseproc, + const_cast<SkData*>(src))); +} + sk_sp<SkData> SkData::MakeWithCString(const char cstr[]) { size_t size; if (nullptr == cstr) { diff --git a/gfx/skia/skia/src/core/SkDevice.cpp b/gfx/skia/skia/src/core/SkDevice.cpp @@ -14,7 +14,6 @@ #include "include/core/SkImage.h" #include "include/core/SkPaint.h" #include "include/core/SkPath.h" -#include "include/core/SkPathBuilder.h" #include "include/core/SkPathTypes.h" #include "include/core/SkPixmap.h" #include "include/core/SkRRect.h" @@ -41,10 +40,8 @@ #include "src/text/GlyphRun.h" #include "src/utils/SkPatchUtils.h" -#include <cstddef> #include <cstdint> - SkDevice::SkDevice(const SkImageInfo& info, const SkSurfaceProps& surfaceProps) : fInfo(info) , fSurfaceProps(surfaceProps) { @@ -121,10 +118,10 @@ void SkDevice::drawRegion(const SkRegion& region, const SkPaint& paint) { bool antiAlias = paint.isAntiAlias() && (!is_int(localToDevice.getTranslateX()) || !is_int(localToDevice.getTranslateY())); if (isNonTranslate || complexPaint || antiAlias) { - SkPathBuilder builder; - region.addBoundaryPath(&builder); - builder.setIsVolatile(true); - return this->drawPath(builder.detach(), paint, true); + SkPath path; + region.getBoundaryPath(&path); + path.setIsVolatile(true); + return this->drawPath(path, paint, true); } SkRegion::Iterator it(region); @@ -135,20 +132,21 @@ void SkDevice::drawRegion(const SkRegion& region, const SkPaint& paint) { } void SkDevice::drawArc(const SkArc& arc, const SkPaint& paint) { + SkPath path; bool isFillNoPathEffect = SkPaint::kFill_Style == paint.getStyle() && !paint.getPathEffect(); - SkPath path = SkPathPriv::CreateDrawArcPath(arc, isFillNoPathEffect); + SkPathPriv::CreateDrawArcPath(&path, arc, isFillNoPathEffect); this->drawPath(path, paint, true); } void SkDevice::drawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) { - SkPathBuilder builder; - builder.addRRect(outer); - builder.addRRect(inner); - builder.setFillType(SkPathFillType::kEvenOdd); - builder.setIsVolatile(true); + SkPath path; + path.addRRect(outer); + path.addRRect(inner); + path.setFillType(SkPathFillType::kEvenOdd); + path.setIsVolatile(true); - this->drawPath(builder.detach(), paint, true); + this->drawPath(path, paint, true); } void SkDevice::drawPatch(const SkPoint cubics[12], const SkColor colors[4], @@ -190,9 +188,7 @@ void SkDevice::drawImageLattice(const SkImage* image, const SkCanvas::Lattice& l } } -static SkPoint* quad_to_tris(SkPoint tris[6], SkSpan<const SkPoint> quad) { - SkASSERT(quad.size() == 4); - +static SkPoint* quad_to_tris(SkPoint tris[6], const SkPoint quad[4]) { tris[0] = quad[0]; tris[1] = quad[1]; tris[2] = quad[2]; @@ -204,16 +200,16 @@ static SkPoint* quad_to_tris(SkPoint tris[6], SkSpan<const SkPoint> quad) { return tris + 6; } -void SkDevice::drawAtlas(SkSpan<const SkRSXform> xform, - SkSpan<const SkRect> tex, - SkSpan<const SkColor> colors, +void SkDevice::drawAtlas(const SkRSXform xform[], + const SkRect tex[], + const SkColor colors[], + int quadCount, sk_sp<SkBlender> blender, const SkPaint& paint) { - const size_t quadCount = xform.size(); - const size_t triCount = quadCount << 1; - const size_t vertexCount = triCount * 3; + const int triCount = quadCount << 1; + const int vertexCount = triCount * 3; uint32_t flags = SkVertices::kHasTexCoords_BuilderFlag; - if (!colors.empty()) { + if (colors) { flags |= SkVertices::kHasColors_BuilderFlag; } SkVertices::Builder builder(SkVertices::kTriangles_VertexMode, vertexCount, 0, flags); @@ -221,14 +217,15 @@ void SkDevice::drawAtlas(SkSpan<const SkRSXform> xform, SkPoint* vPos = builder.positions(); SkPoint* vTex = builder.texCoords(); SkColor* vCol = builder.colors(); - for (size_t i = 0; i < quadCount; ++i) { + for (int i = 0; i < quadCount; ++i) { SkPoint tmp[4]; xform[i].toQuad(tex[i].width(), tex[i].height(), tmp); vPos = quad_to_tris(vPos, tmp); - vTex = quad_to_tris(vTex, tex[i].toQuad()); + tex[i].toQuad(tmp); + vTex = quad_to_tris(vTex, tmp); - if (!colors.empty()) { + if (colors) { SkOpts::memset32(vCol, colors[i], 6); vCol += 6; } @@ -245,7 +242,9 @@ void SkDevice::drawEdgeAAQuad(const SkRect& r, const SkPoint clip[4], SkCanvas:: if (clip) { // Draw the clip directly as a quad since it's a filled color with no local coords - this->drawPath(SkPath::Polygon({clip, 4}, true), paint, true); + SkPath clipPath; + clipPath.addPoly(clip, 4, true); + this->drawPath(clipPath, paint, true); } else { this->drawRect(r, paint); } @@ -278,7 +277,8 @@ void SkDevice::drawEdgeAAImageSet(const SkCanvas::ImageSetEntry images[], int co if (images[i].fHasClip) { // Since drawImageRect requires a srcRect, the dst clip is implemented as a true clip this->pushClipStack(); - SkPath clipPath = SkPath::Polygon({dstClips + clipIndex, 4}, true); + SkPath clipPath; + clipPath.addPoly(dstClips + clipIndex, 4, true); this->clipPath(clipPath, SkClipOp::kIntersect, entryPaint.isAntiAlias()); clipIndex += 4; } @@ -397,8 +397,8 @@ bool SkDevice::peekPixels(SkPixmap* pmap) { ////////////////////////////////////////////////////////////////////////////////////////// static sk_sp<SkShader> make_post_inverse_lm(const SkShader* shader, const SkMatrix& lm) { - auto inverse_lm = lm.invert(); - if (!shader || !inverse_lm) { + SkMatrix inverse_lm; + if (!shader || !lm.invert(&inverse_lm)) { return nullptr; } @@ -415,10 +415,10 @@ static sk_sp<SkShader> make_post_inverse_lm(const SkShader* shader, const SkMatr shader = nested_shader.get(); } - return shader->makeWithLocalMatrix(*inverse_lm * prev_local_matrix); + return shader->makeWithLocalMatrix(inverse_lm * prev_local_matrix); #endif - return shader->makeWithLocalMatrix(*inverse_lm); + return shader->makeWithLocalMatrix(inverse_lm); } void SkDevice::drawGlyphRunList(SkCanvas* canvas, diff --git a/gfx/skia/skia/src/core/SkDevice.h b/gfx/skia/skia/src/core/SkDevice.h @@ -22,7 +22,6 @@ #include "include/core/SkSamplingOptions.h" #include "include/core/SkShader.h" #include "include/core/SkSize.h" -#include "include/core/SkSpan.h" #include "include/core/SkSurfaceProps.h" #include "include/private/base/SkAssert.h" #include "include/private/base/SkNoncopyable.h" @@ -30,6 +29,7 @@ #include "src/core/SkMatrixPriv.h" #include "src/shaders/SkShaderBase.h" +#include <cstddef> #include <cstdint> #include <utility> @@ -48,7 +48,6 @@ class SkPaint; class SkPath; class SkPixmap; class SkRRect; -class SkRecorder; class SkSurface; class SkVertices; enum SkColorType : int; @@ -283,7 +282,6 @@ public: virtual GrRecordingContext* recordingContext() const { return nullptr; } virtual skgpu::graphite::Recorder* recorder() const { return nullptr; } - virtual SkRecorder* baseRecorder() const { return nullptr; } virtual skgpu::ganesh::Device* asGaneshDevice() { return nullptr; } virtual skgpu::graphite::Device* asGraphiteDevice() { return nullptr; } @@ -334,7 +332,8 @@ public: virtual void drawSlug(SkCanvas*, const sktext::gpu::Slug* slug, const SkPaint& paint); virtual void drawPaint(const SkPaint& paint) = 0; - virtual void drawPoints(SkCanvas::PointMode, SkSpan<const SkPoint>, const SkPaint&) = 0; + virtual void drawPoints(SkCanvas::PointMode mode, size_t count, + const SkPoint[], const SkPaint& paint) = 0; virtual void drawRect(const SkRect& r, const SkPaint& paint) = 0; virtual void drawRegion(const SkRegion& r, @@ -386,14 +385,14 @@ public: const SkPaint&, bool skipColorXform = false) = 0; virtual void drawMesh(const SkMesh& mesh, sk_sp<SkBlender>, const SkPaint&) = 0; - virtual void drawShadow(SkCanvas*, const SkPath&, const SkDrawShadowRec&); + virtual void drawShadow(const SkPath&, const SkDrawShadowRec&); // default implementation calls drawVertices virtual void drawPatch(const SkPoint cubics[12], const SkColor colors[4], const SkPoint texCoords[4], sk_sp<SkBlender>, const SkPaint& paint); // default implementation calls drawVertices - virtual void drawAtlas(SkSpan<const SkRSXform>, SkSpan<const SkRect>, SkSpan<const SkColor>, + virtual void drawAtlas(const SkRSXform[], const SkRect[], const SkColor[], int count, sk_sp<SkBlender>, const SkPaint&); virtual void drawAnnotation(const SkRect&, const char[], SkData*) {} @@ -594,7 +593,7 @@ public: protected: void drawPaint(const SkPaint& paint) override {} - void drawPoints(SkCanvas::PointMode, SkSpan<const SkPoint>, const SkPaint&) override {} + void drawPoints(SkCanvas::PointMode, size_t, const SkPoint[], const SkPaint&) override {} void drawImageRect(const SkImage*, const SkRect*, const SkRect&, const SkSamplingOptions&, const SkPaint&, SkCanvas::SrcRectConstraint) override {} diff --git a/gfx/skia/skia/src/core/SkDraw.cpp b/gfx/skia/skia/src/core/SkDraw.cpp @@ -9,25 +9,15 @@ #include "include/core/SkBitmap.h" #include "include/core/SkColorType.h" -#include "include/core/SkImageInfo.h" #include "include/core/SkMatrix.h" #include "include/core/SkPaint.h" -#include "include/core/SkPath.h" -#include "include/core/SkPathBuilder.h" -#include "include/core/SkPathEffect.h" -#include "include/core/SkPathUtils.h" #include "include/core/SkPixmap.h" #include "include/core/SkPoint.h" -#include "include/core/SkRRect.h" #include "include/core/SkRect.h" #include "include/core/SkRegion.h" #include "include/core/SkScalar.h" -#include "include/core/SkSpan.h" -#include "include/core/SkStrokeRec.h" #include "include/core/SkTileMode.h" -#include "include/private/base/SkAlign.h" #include "include/private/base/SkAssert.h" -#include "include/private/base/SkCPUTypes.h" #include "include/private/base/SkDebug.h" #include "include/private/base/SkFixed.h" #include "include/private/base/SkFloatingPoint.h" @@ -35,37 +25,22 @@ #include "include/private/base/SkTo.h" #include "src/base/SkArenaAlloc.h" #include "src/base/SkTLazy.h" -#include "src/base/SkZip.h" #include "src/core/SkAutoBlitterChoose.h" -#include "src/core/SkBlendModePriv.h" #include "src/core/SkBlitter.h" -#include "src/core/SkBlitter_A8.h" -#include "src/core/SkDevice.h" -#include "src/core/SkDrawProcs.h" #include "src/core/SkDrawTypes.h" #include "src/core/SkImageInfoPriv.h" #include "src/core/SkImagePriv.h" -#include "src/core/SkMask.h" -#include "src/core/SkMaskFilterBase.h" #include "src/core/SkMatrixUtils.h" -#include "src/core/SkPathEffectBase.h" -#include "src/core/SkPathPriv.h" #include "src/core/SkRasterClip.h" #include "src/core/SkRectPriv.h" #include "src/core/SkScan.h" -#include <algorithm> -#include <cstddef> -#include <cstdint> -#include <optional> -#include <string> - -class SkResourceCache; +#if defined(SK_SUPPORT_LEGACY_ALPHA_BITMAP_AS_COVERAGE) +#include "src/core/SkMaskFilterBase.h" +#endif using namespace skia_private; -namespace skcpu { - static SkPaint make_paint_with_image(const SkPaint& origPaint, const SkBitmap& bitmap, const SkSamplingOptions& sampling, SkMatrix* matrix = nullptr) { @@ -76,7 +51,9 @@ static SkPaint make_paint_with_image(const SkPaint& origPaint, const SkBitmap& b return paint; } -Draw::Draw() { fBlitterChooser = SkBlitter::Choose; } +SkDraw::SkDraw() { + fBlitterChooser = SkBlitter::Choose; +} struct PtProcRec { SkCanvas::PointMode fMode; @@ -88,7 +65,8 @@ struct PtProcRec { SkRect fClipBounds; SkScalar fRadius; - typedef void (*Proc)(const PtProcRec&, SkSpan<const SkPoint> devPts, SkBlitter*); + typedef void (*Proc)(const PtProcRec&, const SkPoint devPts[], int count, + SkBlitter*); bool init(SkCanvas::PointMode, const SkPaint&, const SkMatrix* matrix, const SkRasterClip*); @@ -98,67 +76,41 @@ private: SkAAClipBlitterWrapper fWrapper; }; -#define DIRECT_BLIT_LOOP(writable_method, value) \ - do { \ - for (auto p : devPts) { \ - int x = SkScalarFloorToInt(p.fX); \ - int y = SkScalarFloorToInt(p.fY); \ - if (cr.contains(x, y)) { \ - *pm.writable_method(x, y) = value; \ - } \ - } \ - } while (0) - - -static void bw_pt_hair_proc(const PtProcRec& rec, SkSpan<const SkPoint> devPts, - SkBlitter* blitter) { - const auto direct = blitter->canDirectBlit(); - if (direct && rec.fClip->isRect()) { - const SkIRect cr = rec.fClip->getBounds(); - auto pm = direct->pm; - const auto v = direct->value; - switch (pm.info().bytesPerPixel()) { - case 1: DIRECT_BLIT_LOOP(writable_addr8, v); break; - case 2: DIRECT_BLIT_LOOP(writable_addr16, v); break; - case 4: DIRECT_BLIT_LOOP(writable_addr32, v); break; - case 8: DIRECT_BLIT_LOOP(writable_addr64, v); break; - default: SkASSERT(false); - } - } else { - for (auto p : devPts) { - int x = SkScalarFloorToInt(p.fX); - int y = SkScalarFloorToInt(p.fY); - if (rec.fClip->contains(x, y)) { - blitter->blitH(x, y, 1); - } +static void bw_pt_hair_proc(const PtProcRec& rec, const SkPoint devPts[], + int count, SkBlitter* blitter) { + for (int i = 0; i < count; i++) { + int x = SkScalarFloorToInt(devPts[i].fX); + int y = SkScalarFloorToInt(devPts[i].fY); + if (rec.fClip->contains(x, y)) { + blitter->blitH(x, y, 1); } } } -static void bw_line_hair_proc(const PtProcRec& rec, SkSpan<const SkPoint> devPts, - SkBlitter* blitter) { - for (size_t i = 0; i < devPts.size(); i += 2) { +static void bw_line_hair_proc(const PtProcRec& rec, const SkPoint devPts[], + int count, SkBlitter* blitter) { + for (int i = 0; i < count; i += 2) { SkScan::HairLine(&devPts[i], 2, *rec.fRC, blitter); } } -static void bw_poly_hair_proc(const PtProcRec& rec, SkSpan<const SkPoint> devPts, - SkBlitter* blitter) { - SkScan::HairLine(devPts.data(), SkToInt(devPts.size()), *rec.fRC, blitter); +static void bw_poly_hair_proc(const PtProcRec& rec, const SkPoint devPts[], + int count, SkBlitter* blitter) { + SkScan::HairLine(devPts, count, *rec.fRC, blitter); } // aa versions -static void aa_line_hair_proc(const PtProcRec& rec, SkSpan<const SkPoint> devPts, - SkBlitter* blitter) { - for (size_t i = 0; i < devPts.size(); i += 2) { +static void aa_line_hair_proc(const PtProcRec& rec, const SkPoint devPts[], + int count, SkBlitter* blitter) { + for (int i = 0; i < count; i += 2) { SkScan::AntiHairLine(&devPts[i], 2, *rec.fRC, blitter); } } -static void aa_poly_hair_proc(const PtProcRec& rec, SkSpan<const SkPoint> devPts, - SkBlitter* blitter) { - SkScan::AntiHairLine(devPts.data(), SkToInt(devPts.size()), *rec.fRC, blitter); +static void aa_poly_hair_proc(const PtProcRec& rec, const SkPoint devPts[], + int count, SkBlitter* blitter) { + SkScan::AntiHairLine(devPts, count, *rec.fRC, blitter); } // square procs (strokeWidth > 0 but matrix is square-scale (sx == sy) @@ -178,20 +130,20 @@ static SkXRect make_xrect(const SkRect& r) { }; } -static void bw_square_proc(const PtProcRec& rec, SkSpan<const SkPoint> devPts, - SkBlitter* blitter) { - for (auto p : devPts) { - SkRect r = make_square_rad(p, rec.fRadius); +static void bw_square_proc(const PtProcRec& rec, const SkPoint devPts[], + int count, SkBlitter* blitter) { + for (int i = 0; i < count; i++) { + SkRect r = make_square_rad(devPts[i], rec.fRadius); if (r.intersect(rec.fClipBounds)) { SkScan::FillXRect(make_xrect(r), *rec.fRC, blitter); } } } -static void aa_square_proc(const PtProcRec& rec, SkSpan<const SkPoint> devPts, - SkBlitter* blitter) { - for (auto p : devPts) { - SkRect r = make_square_rad(p, rec.fRadius); +static void aa_square_proc(const PtProcRec& rec, const SkPoint devPts[], + int count, SkBlitter* blitter) { + for (int i = 0; i < count; i++) { + SkRect r = make_square_rad(devPts[i], rec.fRadius); if (r.intersect(rec.fClipBounds)) { SkScan::AntiFillXRect(make_xrect(r), *rec.fRC, blitter); } @@ -284,26 +236,25 @@ PtProcRec::Proc PtProcRec::chooseProc(SkBlitter** blitterPtr) { // must be even for lines/polygon to work #define MAX_DEV_PTS 32 -void Draw::drawPoints(SkCanvas::PointMode mode, - SkSpan<const SkPoint> points, - const SkPaint& paint, - SkDevice* device) const { +void SkDraw::drawPoints(SkCanvas::PointMode mode, size_t count, + const SkPoint pts[], const SkPaint& paint, + SkDevice* device) const { // if we're in lines mode, force count to be even if (SkCanvas::kLines_PointMode == mode) { - points = points.first(points.size() & ~1); // force it to be even + count &= ~(size_t)1; } + SkASSERT(pts != nullptr); SkDEBUGCODE(this->validate();) // nothing to draw - if (points.empty() || fRC->isEmpty()) { + if (!count || fRC->isEmpty()) { return; } PtProcRec rec; if (!device && rec.init(mode, paint, fCTM, fRC)) { - // Can't easily get bounds of points so don't try. - SkAutoBlitterChoose blitter(*this, nullptr, paint, SkRect::MakeEmpty()); + SkAutoBlitterChoose blitter(*this, nullptr, paint); SkPoint devPts[MAX_DEV_PTS]; SkBlitter* bltr = blitter.get(); @@ -311,18 +262,16 @@ void Draw::drawPoints(SkCanvas::PointMode mode, // we have to back up subsequent passes if we're in polygon mode const size_t backup = (SkCanvas::kPolygon_PointMode == mode); - auto count = points.size(); - auto pts = points.data(); do { int n = SkToInt(count); if (n > MAX_DEV_PTS) { n = MAX_DEV_PTS; } - fCTM->mapPoints({devPts, n}, {pts, n}); + fCTM->mapPoints(devPts, pts, n); if (!SkIsFinite(&devPts[0].fX, n * 2)) { return; } - proc(rec, {devPts, n}, bltr); + proc(rec, devPts, n, bltr); pts += n - backup; SkASSERT(SkToInt(count) >= n); count -= n; @@ -331,7 +280,7 @@ void Draw::drawPoints(SkCanvas::PointMode mode, } } while (count != 0); } else { - this->drawDevicePoints(mode, points, paint, device); + this->drawDevicePoints(mode, count, pts, paint, device); } } @@ -353,11 +302,9 @@ static bool clipHandlesSprite(const SkRasterClip& clip, int x, int y, const SkPi return clip.isBW() || clip.quickContains(SkIRect::MakeXYWH(x, y, pmap.width(), pmap.height())); } -void Draw::drawBitmap(const SkBitmap& bitmap, - const SkMatrix& prematrix, - const SkRect* dstBounds, - const SkSamplingOptions& sampling, - const SkPaint& origPaint) const { +void SkDraw::drawBitmap(const SkBitmap& bitmap, const SkMatrix& prematrix, + const SkRect* dstBounds, const SkSamplingOptions& sampling, + const SkPaint& origPaint) const { SkDEBUGCODE(this->validate();) // nothing to draw @@ -405,15 +352,16 @@ void Draw::drawBitmap(const SkBitmap& bitmap, } // now make a temp draw on the stack, and use it - Draw draw(*this); + // + SkDraw draw(*this); draw.fCTM = &matrix; // For a long time, the CPU backend treated A8 bitmaps as coverage, rather than alpha. This was - // inconsistent with the GPU backend (skbug.com/40041022). When this was fixed, it altered behavior + // inconsistent with the GPU backend (skbug.com/9692). When this was fixed, it altered behavior // for some Android apps (b/231400686). Thus: keep the old behavior in the framework. #if defined(SK_SUPPORT_LEGACY_ALPHA_BITMAP_AS_COVERAGE) if (bitmap.colorType() == kAlpha_8_SkColorType && !paint->getColorFilter()) { - draw.drawBitmapAsMask(bitmap, sampling, *paint, nullptr); + draw.drawBitmapAsMask(bitmap, sampling, *paint); return; } #endif @@ -427,7 +375,7 @@ void Draw::drawBitmap(const SkBitmap& bitmap, } } -void Draw::drawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint& origPaint) const { +void SkDraw::drawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint& origPaint) const { SkDEBUGCODE(this->validate();) // nothing to draw @@ -471,15 +419,14 @@ void Draw::drawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint& origP // create shader with offset matrix.setTranslate(r.fLeft, r.fTop); SkPaint paintWithShader = make_paint_with_image(paint, bitmap, SkSamplingOptions(), &matrix); - Draw draw(*this); + SkDraw draw(*this); draw.fCTM = &SkMatrix::I(); // call ourself with a rect draw.drawRect(r, paintWithShader); } -void Draw::drawDevMask(const SkMask& srcM, - const SkPaint& paint, - const SkMatrix* paintMatrix) const { +#if defined(SK_SUPPORT_LEGACY_ALPHA_BITMAP_AS_COVERAGE) +void SkDraw::drawDevMask(const SkMask& srcM, const SkPaint& paint) const { if (srcM.fBounds.isEmpty()) { return; } @@ -493,7 +440,7 @@ void Draw::drawDevMask(const SkMask& srcM, } SkAutoMaskFreeImage ami(dstM.image()); - SkAutoBlitterChoose blitterChooser(*this, paintMatrix, paint, SkRect::Make(dstM.bounds())); + SkAutoBlitterChoose blitterChooser(*this, nullptr, paint); SkBlitter* blitter = blitterChooser.get(); SkAAClipBlitterWrapper wrapper; @@ -509,10 +456,8 @@ void Draw::drawDevMask(const SkMask& srcM, blitter->blitMaskRegion(*mask, *clipRgn); } -void Draw::drawBitmapAsMask(const SkBitmap& bitmap, - const SkSamplingOptions& sampling, - const SkPaint& paint, - const SkMatrix* paintMatrix) const { +void SkDraw::drawBitmapAsMask(const SkBitmap& bitmap, const SkSamplingOptions& sampling, + const SkPaint& paint) const { SkASSERT(bitmap.colorType() == kAlpha_8_SkColorType); // nothing to draw @@ -534,7 +479,7 @@ void Draw::drawBitmapAsMask(const SkBitmap& bitmap, SkToU32(pmap.rowBytes()), SkMask::kA8_Format); - this->drawDevMask(mask, paint, paintMatrix); + this->drawDevMask(mask, paint); } else { // need to xform the bitmap first SkRect r; SkMaskBuilder mask; @@ -590,771 +535,7 @@ void Draw::drawBitmapAsMask(const SkBitmap& bitmap, rr.setIWH(bitmap.width(), bitmap.height()); c.drawRect(rr, paintWithShader); } - this->drawDevMask(mask, paint, paintMatrix); + this->drawDevMask(mask, paint); } } - -/////////////////////////////////////////////////////////////////////////////// - -bool Draw::computeConservativeLocalClipBounds(SkRect* localBounds) const { - if (fRC->isEmpty()) { - return false; - } - - if (auto inverse = fCTM->invert()) { - SkIRect devBounds = fRC->getBounds(); - // outset to have slop for antialasing and hairlines - devBounds.outset(1, 1); - inverse->mapRect(localBounds, SkRect::Make(devBounds)); - return true; - } - return false; -} - -/////////////////////////////////////////////////////////////////////////////// - -void Draw::drawPaint(const SkPaint& paint) const { - SkDEBUGCODE(this->validate();) - - if (fRC->isEmpty()) { - return; - } - - SkIRect devRect; - devRect.setWH(fDst.width(), fDst.height()); - - SkAutoBlitterChoose blitter(*this, nullptr, paint, SkRect::Make(devRect)); - SkScan::FillIRect(devRect, *fRC, blitter.get()); -} - -/////////////////////////////////////////////////////////////////////////////// - -static inline SkPoint compute_stroke_size(const SkPaint& paint, const SkMatrix& matrix) { - SkASSERT(matrix.rectStaysRect()); - SkASSERT(SkPaint::kFill_Style != paint.getStyle()); - - SkVector size = matrix.mapVector({paint.getStrokeWidth(), paint.getStrokeWidth()}); - return SkPoint::Make(SkScalarAbs(size.fX), SkScalarAbs(size.fY)); -} - -static bool easy_rect_join(const SkRect& rect, - const SkPaint& paint, - const SkMatrix& matrix, - SkPoint* strokeSize) { - if (rect.isEmpty() || SkPaint::kMiter_Join != paint.getStrokeJoin() || - paint.getStrokeMiter() < SK_ScalarSqrt2) { - return false; - } - - *strokeSize = compute_stroke_size(paint, matrix); - return true; -} - -Draw::RectType Draw::ComputeRectType(const SkRect& rect, - const SkPaint& paint, - const SkMatrix& matrix, - SkPoint* strokeSize) { - const SkScalar width = paint.getStrokeWidth(); - const bool zeroWidth = (0 == width); - SkPaint::Style style = paint.getStyle(); - - if ((SkPaint::kStrokeAndFill_Style == style) && zeroWidth) { - style = SkPaint::kFill_Style; - } - - if (paint.getPathEffect() || paint.getMaskFilter() || !matrix.rectStaysRect() || - SkPaint::kStrokeAndFill_Style == style) { - return RectType::kPath; - } - if (SkPaint::kFill_Style == style) { - return RectType::kFill; - } - if (zeroWidth) { - return RectType::kHair; - } - if (easy_rect_join(rect, paint, matrix, strokeSize)) { - return RectType::kStroke; - } - return RectType::kPath; -} - -static SkSpan<const SkPoint> rect_points(const SkRect& r) { - return {reinterpret_cast<const SkPoint*>(&r), 2}; -} - - -static SkSpan<SkPoint> rect_points(SkRect& r) { return {reinterpret_cast<SkPoint*>(&r), 2}; } - -static void draw_rect_as_path(const Draw& orig, - const SkRect& prePaintRect, - const SkPaint& paint, - const SkMatrix& ctm) { - Draw draw(orig); - draw.fCTM = &ctm; - draw.drawPath(SkPath::Rect(prePaintRect), paint, nullptr, true); -} - -void Draw::drawRect(const SkRect& prePaintRect, - const SkPaint& paint, - const SkMatrix* paintMatrix, - const SkRect* postPaintRect) const { - SkDEBUGCODE(this->validate();) - - // nothing to draw - if (fRC->isEmpty()) { - return; - } - - SkTCopyOnFirstWrite<SkMatrix> matrix(fCTM); - if (paintMatrix) { - SkASSERT(postPaintRect); - matrix.writable()->preConcat(*paintMatrix); - } else { - SkASSERT(!postPaintRect); - } - - SkPoint strokeSize; - RectType rtype = ComputeRectType(prePaintRect, paint, *fCTM, &strokeSize); - - if (RectType::kPath == rtype) { - draw_rect_as_path(*this, prePaintRect, paint, *matrix); - return; - } - - SkRect devRect; - const SkRect& paintRect = paintMatrix ? *postPaintRect : prePaintRect; - // skip the paintMatrix when transforming the rect by the CTM - fCTM->mapPoints(rect_points(devRect), rect_points(paintRect)); - devRect.sort(); - - // look for the quick exit, before we build a blitter - SkRect bbox = devRect; - if (paint.getStyle() != SkPaint::kFill_Style) { - // extra space for hairlines - if (paint.getStrokeWidth() == 0) { - bbox.outset(1, 1); - } else { - // For RectType::kStroke, strokeSize is already computed. - const SkPoint& ssize = (RectType::kStroke == rtype) - ? strokeSize - : compute_stroke_size(paint, *fCTM); - bbox.outset(SkScalarHalf(ssize.x()), SkScalarHalf(ssize.y())); - } - } - if (SkPathPriv::TooBigForMath(bbox)) { - return; - } - - if (!SkRectPriv::FitsInFixed(bbox) && rtype != RectType::kHair) { - draw_rect_as_path(*this, prePaintRect, paint, *matrix); - return; - } - - SkIRect ir = bbox.roundOut(); - if (fRC->quickReject(ir)) { - return; - } - - SkAutoBlitterChoose blitterStorage(*this, matrix, paint, devRect); - const SkRasterClip& clip = *fRC; - SkBlitter* blitter = blitterStorage.get(); - - // we want to "fill" if we are kFill or kStrokeAndFill, since in the latter - // case we are also hairline (if we've gotten to here), which devolves to - // effectively just kFill - switch (rtype) { - case RectType::kFill: - if (paint.isAntiAlias()) { - SkScan::AntiFillRect(devRect, clip, blitter); - } else { - SkScan::FillRect(devRect, clip, blitter); - } - break; - case RectType::kStroke: - if (paint.isAntiAlias()) { - SkScan::AntiFrameRect(devRect, strokeSize, clip, blitter); - } else { - SkScan::FrameRect(devRect, strokeSize, clip, blitter); - } - break; - case RectType::kHair: - if (paint.isAntiAlias()) { - SkScan::AntiHairRect(devRect, clip, blitter); - } else { - SkScan::HairRect(devRect, clip, blitter); - } - break; - default: - SkDEBUGFAIL("bad rtype"); - } -} - -static SkScalar fast_len(const SkVector& vec) { - SkScalar x = SkScalarAbs(vec.fX); - SkScalar y = SkScalarAbs(vec.fY); - if (x < y) { - using std::swap; - swap(x, y); - } - return x + SkScalarHalf(y); -} - -bool DrawTreatAAStrokeAsHairline(SkScalar strokeWidth, const SkMatrix& matrix, SkScalar* coverage) { - SkASSERT(strokeWidth > 0); - // We need to try to fake a thick-stroke with a modulated hairline. - - if (matrix.hasPerspective()) { - return false; - } - - SkVector src[2], dst[2]; - src[0].set(strokeWidth, 0); - src[1].set(0, strokeWidth); - matrix.mapVectors(dst, src); - SkScalar len0 = fast_len(dst[0]); - SkScalar len1 = fast_len(dst[1]); - if (len0 <= SK_Scalar1 && len1 <= SK_Scalar1) { - if (coverage) { - *coverage = SkScalarAve(len0, len1); - } - return true; - } - return false; -} - -void Draw::drawOval(const SkRect& oval, const SkPaint& paint) const { - SkDEBUGCODE(this->validate();) - - if (fRC->isEmpty()) { - return; - } - - this->drawPath(SkPath::Oval(oval), paint, nullptr, true); -} - -void Draw::drawRRect(const SkRRect& rrect, const SkPaint& paint) const { - SkDEBUGCODE(this->validate();) - - if (fRC->isEmpty()) { - return; - } - - { - // TODO: Investigate optimizing these options. They are in the same - // order as skcpu::Draw::drawPath, which handles each case. It may be - // that there is no way to optimize for these using the SkRRect path. - SkScalar coverage; - if (skcpu::DrawTreatAsHairline(paint, *fCTM, &coverage)) { - goto DRAW_PATH; - } - - if (paint.getPathEffect() || paint.getStyle() != SkPaint::kFill_Style) { - goto DRAW_PATH; - } - } - - if (paint.getMaskFilter()) { - if (this->drawRRectNinePatch(rrect, paint)) { - return; - } - } - -DRAW_PATH: - // Now fall back to the default case of using a path. - this->drawPath(SkPath::RRect(rrect), paint, nullptr, true); -} - -bool Draw::drawRRectNinePatch(const SkRRect& rrect, const SkPaint& paint) const { - SkASSERT(paint.getMaskFilter()); - - if (auto rr = rrect.transform(*fCTM)) { - SkAutoBlitterChoose blitter(*this, nullptr, paint, rrect.getBounds()); - SkResourceCache* cache = nullptr; // TODO(kjlubick) get this from fCtx - const SkMaskFilterBase* maskFilter = as_MFB(paint.getMaskFilter()); - if (rrect.getType() == SkRRect::kRect_Type) { - SkRect devRect = rr->rect(); - if (maskFilter->filterRects(SkSpan(&devRect, 1), *fCTM, *fRC, blitter.get(), cache) == - SkMaskFilterBase::FilterReturn::kTrue) { - return true; - } - } else { - if (maskFilter->filterRRect(*rr, *fCTM, *fRC, blitter.get(), cache)) { - return true; // filterRRect() called the blitter, so we're done - } - } - } - return false; -} - -void Draw::drawDevPath(const SkPathRaw& raw, - const SkPaint& paint, - SkDrawCoverage drawCoverage, - SkBlitter* customBlitter, - bool doFill) const { - if (SkPathPriv::TooBigForMath(raw.bounds())) { - return; - } - - SkBlitter* blitter = nullptr; - SkAutoBlitterChoose blitterStorage; - if (nullptr == customBlitter) { - blitter = blitterStorage.choose(*this, nullptr, paint, raw.bounds(), drawCoverage); - } else { - blitter = customBlitter; - } - - if (paint.getMaskFilter()) { - SkStrokeRec::InitStyle style = doFill ? SkStrokeRec::kFill_InitStyle - : SkStrokeRec::kHairline_InitStyle; - SkResourceCache* cache = nullptr; // TODO(kjlubick) get this from fCtx - if (as_MFB(paint.getMaskFilter())->filterPath(raw, *fCTM, *fRC, blitter, style, cache)) { - return; // filterPath() called the blitter, so we're done - } - } - - void (*proc)(const SkPathRaw&, const SkRasterClip&, SkBlitter*); - if (doFill) { - if (paint.isAntiAlias()) { - proc = SkScan::AntiFillPath; - } else { - proc = SkScan::FillPath; - } - } else { // hairline - if (paint.isAntiAlias()) { - switch (paint.getStrokeCap()) { - case SkPaint::kButt_Cap: - proc = SkScan::AntiHairPath; - break; - case SkPaint::kSquare_Cap: - proc = SkScan::AntiHairSquarePath; - break; - case SkPaint::kRound_Cap: - proc = SkScan::AntiHairRoundPath; - break; - } - } else { - switch (paint.getStrokeCap()) { - case SkPaint::kButt_Cap: - proc = SkScan::HairPath; - break; - case SkPaint::kSquare_Cap: - proc = SkScan::HairSquarePath; - break; - case SkPaint::kRound_Cap: - proc = SkScan::HairRoundPath; - break; - } - } - } - proc(raw, *fRC, blitter); -} - -/* - * Tricky idea: can we treat thin strokes as hairlines? If so, depending on how - * thin, we may decide to modulate the paint's alpha to 'simulate' very think - * strokes, even though hairline is always 1-pixel wide. - * - * The motivation at the time was performance: hairlines draw faster than constructing - * the inner/outer contours and filling that (as we do for normal stroking). - * - * Questionable decision, since our hairline algorithm draws each segment of the path - * separately, meaning a path that crosses itself can have blending artifacts. - * Note: this doesn't happen with normal stroking, as the built inner/outer path - * never double-hits a pixel. - */ -static std::optional<SkPaint> modifyPaintForHairlines(const SkPaint& origPaint, - const SkMatrix& matrix) { - float coverage; - if (DrawTreatAsHairline(origPaint, matrix, &coverage)) { - const auto bm = origPaint.asBlendMode(); - if (coverage == 1) { - SkPaint paint(origPaint); - paint.setStrokeWidth(0); - return paint; - } else if (bm && SkBlendMode_SupportsCoverageAsAlpha(bm.value())) { - U8CPU newAlpha; -#if 0 - newAlpha = SkToU8(SkScalarRoundToInt(coverage * origPaint.getAlpha())); -#else - // this is the old technique, which we preserve for now so - // we don't change previous results (testing) - // the new way seems fine, its just (a tiny bit) different - int scale = (int)(coverage * 256); - newAlpha = origPaint.getAlpha() * scale >> 8; -#endif - SkPaint paint(origPaint); - paint.setStrokeWidth(0); - paint.setAlpha(newAlpha); - return paint; - } - } - return {}; -} - -void Draw::drawPath(const SkPath& origSrcPath, - const SkPaint& origPaint, - const SkMatrix* prePathMatrix, - bool pathIsMutable, - SkDrawCoverage drawCoverage, - SkBlitter* customBlitter) const { - SkDEBUGCODE(this->validate();) - - // nothing to draw - if (fRC->isEmpty()) { - return; - } - - SkPath* pathPtr = const_cast<SkPath*>(&origSrcPath); - bool doFill = true; - SkPath tmpPathStorage; - SkPath* tmpPath = &tmpPathStorage; - SkTCopyOnFirstWrite<SkMatrix> matrix(fCTM); - tmpPath->setIsVolatile(true); - - if (prePathMatrix) { - if (origPaint.getPathEffect() || origPaint.getStyle() != SkPaint::kFill_Style) { - SkPath* result = pathPtr; - - if (!pathIsMutable) { - result = tmpPath; - pathIsMutable = true; - } - pathPtr->transform(*prePathMatrix, result); - pathPtr = result; - } else { - matrix.writable()->preConcat(*prePathMatrix); - } - } - - std::optional<SkPaint> newPaint = modifyPaintForHairlines(origPaint, *matrix); - const SkPaint* paint = newPaint.has_value() ? &newPaint.value() - : &origPaint; - - if (paint->getPathEffect() || paint->getStyle() != SkPaint::kFill_Style) { - SkRect cullRect; - const SkRect* cullRectPtr = nullptr; - if (this->computeConservativeLocalClipBounds(&cullRect)) { - cullRectPtr = &cullRect; - } - SkPathBuilder builder; - doFill = skpathutils::FillPathWithPaint(*pathPtr, *paint, &builder, cullRectPtr, *fCTM); - *tmpPath = builder.detach(); - pathPtr = tmpPath; - } - - // avoid possibly allocating a new path in transform if we can - SkPath* devPathPtr = pathIsMutable ? pathPtr : tmpPath; - - // transform the path into device space - pathPtr->transform(*matrix, devPathPtr); - if (!devPathPtr->isFinite()) { - return; - } - -#if defined(SK_BUILD_FOR_FUZZER) - if (devPathPtr->countPoints() > 1000) { - return; - } -#endif - - SkPathRaw raw = SkPathPriv::Raw(*devPathPtr); - this->drawDevPath(raw, *paint, drawCoverage, customBlitter, doFill); -} - -//////////////////////////////////////////////////////////////////////////////////////////////// - -#ifdef SK_DEBUG - -void Draw::validate() const { - SkASSERT(fCTM != nullptr); - SkASSERT(fRC != nullptr); - - const SkIRect& cr = fRC->getBounds(); - SkIRect br; - - br.setWH(fDst.width(), fDst.height()); - SkASSERT(cr.isEmpty() || br.contains(cr)); -} - #endif - -//////////////////////////////////////////////////////////////////////////////////////////////// - -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(); - - 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) - // Ugh. Guard against gigantic margins from wacky filters. Without this - // check we can request arbitrary amounts of slop beyond our visible - // clip, and bring down the renderer (at least on finite RAM machines - // like handsets, etc.). Need to balance this invented value between - // quality of large filters like blurs, and the corresponding memory - // requests. - static constexpr int kMaxMargin = 128; - if (!bounds->intersect(clipBounds.makeOutset(std::min(margin.fX, kMaxMargin), - std::min(margin.fY, kMaxMargin)))) { - return false; - } - - return true; -} - -static void draw_into_mask(const SkMask& mask, - SkPathRaw raw, - SkStrokeRec::InitStyle style) { - SkPixmap dst; - if (!dst.reset(mask)) { - return; - } - - const float dx = -mask.fBounds.fLeft, - dy = -mask.fBounds.fTop; - const SkMatrix translate = SkMatrix::Translate(dx, dy); - - SkPaint paint; - paint.setAntiAlias(true); - SkBlitterSizedArena alloc; - SkBlitter* blitter = SkChooseA8Blitter(dst, translate, paint, &alloc, - SkDrawCoverage::kNo, nullptr); - - - // transform a copy of the points, so we can apply the ctm/translate - skia_private::AutoSTArray<32, SkPoint> devPoints(raw.fPoints.size()); - translate.mapPoints(devPoints, raw.fPoints); - raw.fPoints = devPoints; - raw.fBounds = raw.fBounds.makeOffset(dx, dy); - if (!raw.fBounds.isFinite()) { - return; - } - - const SkRasterClip clip(SkIRect::MakeWH(mask.fBounds.width(), mask.fBounds.height())); - - switch (style) { - case SkStrokeRec::kHairline_InitStyle: - SkScan::AntiHairPath(raw, clip, blitter); - break; - case SkStrokeRec::kFill_InitStyle: - SkScan::AntiFillPath(raw, clip, blitter); - break; - } -} - -bool DrawToMask(const SkPathRaw& devRaw, - const SkIRect& clipBounds, - const SkMaskFilter* filter, - const SkMatrix* filterMatrix, - SkMaskBuilder* dst, - SkMaskBuilder::CreateMode mode, - SkStrokeRec::InitStyle style) { - SkASSERT(filter); - if (devRaw.empty()) { - return false; - } - - if (SkMaskBuilder::kJustRenderImage_CreateMode != mode) { - // 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 = devRaw.isInverseFillType() ? kInverseBounds : devRaw.bounds(); - if (!compute_mask_bounds(pathBounds, clipBounds, filter, filterMatrix, &dst->bounds())) { - return false; - } - } - - if (SkMaskBuilder::kComputeBoundsAndRenderImage_CreateMode == mode) { - dst->format() = SkMask::kA8_Format; - dst->rowBytes() = dst->fBounds.width(); - size_t size = dst->computeImageSize(); - if (0 == size) { - // we're too big to allocate the mask, abort - return false; - } - dst->image() = SkMaskBuilder::AllocImage(size, SkMaskBuilder::kZeroInit_Alloc); - } - - if (SkMaskBuilder::kJustComputeBounds_CreateMode != mode) { - draw_into_mask(*dst, devRaw, style); - } - return true; -} - -void Draw::drawDevicePoints(SkCanvas::PointMode mode, - SkSpan<const SkPoint> points, - const SkPaint& paint, - SkDevice* device) const { - // if we're in lines mode, force count to be even - if (SkCanvas::kLines_PointMode == mode) { - points = points.first(points.size() & ~1); // force it to be even - } - - SkDEBUGCODE(this->validate();) - - // nothing to draw - if (points.empty() || fRC->isEmpty()) { - return; - } - - // needed? - if (!SkIsFinite(&points[0].fX, points.size() * 2)) { - return; - } - - switch (mode) { - case SkCanvas::kPoints_PointMode: { - // temporarily mark the paint as filling. - SkPaint newPaint(paint); - newPaint.setStyle(SkPaint::kFill_Style); - - SkScalar width = newPaint.getStrokeWidth(); - SkScalar radius = SkScalarHalf(width); - - if (newPaint.getStrokeCap() == SkPaint::kRound_Cap) { - if (device) { - for (const auto& pt : points) { - SkRect r = SkRect::MakeLTRB(pt.fX - radius, pt.fY - radius, - pt.fX + radius, pt.fY + radius); - device->drawOval(r, newPaint); - } - } else { - SkPath path = SkPath::Circle(0, 0, radius); - SkMatrix preMatrix; - - for (const auto& pt : points) { - preMatrix.setTranslate(pt.fX, pt.fY); - // pass true for the last point, since we can modify - // then path then - const bool isLast = &pt == &points.back(); - this->drawPath(path, newPaint, &preMatrix, isLast); - } - } - } else { - SkRect r; - - for (const auto& pt : points) { - r.fLeft = pt.fX - radius; - r.fTop = pt.fY - radius; - r.fRight = r.fLeft + width; - r.fBottom = r.fTop + width; - if (device) { - device->drawRect(r, newPaint); - } else { - this->drawRect(r, newPaint); - } - } - } - break; - } - case SkCanvas::kLines_PointMode: - if (2 == points.size() && paint.getPathEffect()) { - // most likely a dashed line - see if it is one of the ones - // we can accelerate - SkStrokeRec stroke(paint); - SkPathEffectBase::PointData pointData; - - SkPath path = SkPath::Line(points[0], points[1]); - - SkRect cullRect = SkRect::Make(fRC->getBounds()); - - if (as_PEB(paint.getPathEffect()) - ->asPoints(&pointData, path, stroke, *fCTM, &cullRect)) { - // 'asPoints' managed to find some fast path - - SkPaint newP(paint); - newP.setPathEffect(nullptr); - newP.setStyle(SkPaint::kFill_Style); - - if (!pointData.fFirst.isEmpty()) { - if (device) { - device->drawPath(pointData.fFirst, newP, true); - } else { - this->drawPath(pointData.fFirst, newP, nullptr, true); - } - } - - if (!pointData.fLast.isEmpty()) { - if (device) { - device->drawPath(pointData.fLast, newP, true); - } else { - this->drawPath(pointData.fLast, newP, nullptr, true); - } - } - - if (pointData.fSize.fX == pointData.fSize.fY) { - // The rest of the dashed line can just be drawn as points - SkASSERT(pointData.fSize.fX == SkScalarHalf(newP.getStrokeWidth())); - - if (SkPathEffectBase::PointData::kCircles_PointFlag & pointData.fFlags) { - newP.setStrokeCap(SkPaint::kRound_Cap); - } else { - newP.setStrokeCap(SkPaint::kButt_Cap); - } - - if (device) { - device->drawPoints( - SkCanvas::kPoints_PointMode, pointData.points(), newP); - } else { - this->drawDevicePoints( - SkCanvas::kPoints_PointMode, pointData.points(), newP, device); - } - break; - } else { - // The rest of the dashed line must be drawn as rects - SkASSERT(!(SkPathEffectBase::PointData::kCircles_PointFlag & - pointData.fFlags)); - - SkRect r; - - for (const auto& pt : pointData.points()) { - r.setLTRB(pt.fX - pointData.fSize.fX, - pt.fY - pointData.fSize.fY, - pt.fX + pointData.fSize.fX, - pt.fY + pointData.fSize.fY); - if (device) { - device->drawRect(r, newP); - } else { - this->drawRect(r, newP); - } - } - } - - break; - } - } - [[fallthrough]]; // couldn't take fast path - case SkCanvas::kPolygon_PointMode: { - auto count = points.size() - 1; - SkPaint p(paint); - p.setStyle(SkPaint::kStroke_Style); - size_t inc = (SkCanvas::kLines_PointMode == mode) ? 2 : 1; - - for (size_t i = 0; i < count; i += inc) { - auto path = SkPath::Line(points[i], points[i + 1]); - if (device) { - device->drawPath(path, p, true); - } else { - this->drawPath(path, p, nullptr, true); - } - } - break; - } - } -} - -} // namespace skcpu diff --git a/gfx/skia/skia/src/core/SkDraw.h b/gfx/skia/skia/src/core/SkDraw.h @@ -1,3 +1,4 @@ + /* * Copyright 2006 The Android Open Source Project * @@ -5,236 +6,66 @@ * found in the LICENSE file. */ -#ifndef skcpu_Draw_DEFINED -#define skcpu_Draw_DEFINED + +#ifndef SkDraw_DEFINED +#define SkDraw_DEFINED #include "include/core/SkCanvas.h" -#include "include/core/SkPaint.h" -#include "include/core/SkPixmap.h" +#include "include/core/SkColor.h" #include "include/core/SkRefCnt.h" #include "include/core/SkSamplingOptions.h" -#include "include/core/SkSpan.h" -#include "include/core/SkStrokeRec.h" -#include "include/core/SkSurfaceProps.h" -#include "include/private/base/SkDebug.h" #include "src/base/SkZip.h" -#include "src/core/SkDrawTypes.h" -#include "src/core/SkMask.h" +#include "src/core/SkDrawBase.h" +#include <cstddef> class SkArenaAlloc; class SkBitmap; class SkBlender; -class SkBlitter; class SkDevice; class SkGlyph; -class SkMaskFilter; +class SkGlyphRunListPainterCPU; class SkMatrix; -class SkPath; -struct SkPathRaw; -class SkRRect; -class SkRasterClip; -class SkShader; +class SkPaint; class SkVertices; -struct SkIRect; -struct SkPoint; +namespace sktext { class GlyphRunList; } struct SkPoint3; +struct SkPoint; struct SkRSXform; struct SkRect; -namespace sktext { -class GlyphRunList; -} - -namespace skcpu { -class GlyphRunListPainter; -class ContextImpl; - - -/** 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 - solelely to assist in computing the mask's bounds (if the mode requests that). -*/ -bool DrawToMask(const SkPathRaw& devRaw, - const SkIRect& clipBounds, - const SkMaskFilter*, - const SkMatrix* filterMatrix, - SkMaskBuilder* dst, - SkMaskBuilder::CreateMode mode, - SkStrokeRec::InitStyle style); - -/** - * BitmapDevicePainter provides a minimal interface for painting pre-rasterized objects like glyphs - * and bitmaps. - * - * This interface creates a seam that allows components to intercept and handle low-level drawing - * primitives. For example, SkOverdrawCanvas uses this to visualize overdraw by drawing bounding - * boxes for glyphs - */ -class BitmapDevicePainter { -public: - BitmapDevicePainter() = default; - BitmapDevicePainter(const BitmapDevicePainter&) = default; - virtual ~BitmapDevicePainter() = default; - - virtual void paintMasks(SkZip<const SkGlyph*, SkPoint> accepted, - const SkPaint& paint) const = 0; - virtual void drawBitmap(const SkBitmap&, - const SkMatrix&, - const SkRect* dstOrNull, - const SkSamplingOptions&, - const SkPaint&) const = 0; -}; - -/** - * The Draw class is the workhorse for the software rendering backend. It is an implementation - * detail of SkCanvas that orchestrates the drawing of primitives into a CPU-backed SkPixmap. - * - * A Draw object is a lightweight context configured with everything needed for a single drawing - * operation: - * - The destination pixels (SkPixmap) - * - The current transformation matrix - * - The clipping region - * - * Its primary responsibility is to analyze the primitive (path, rect, etc.), the SkPaint, and the - * device state to select and configure the most efficient SkBlitter. The SkBlitter then handles - * the actual work of writing pixels into the destination pixmap. - * The default Blitter is chosen via SkBlitter::Choose() - */ -class Draw : public BitmapDevicePainter { +// defaults to use SkBlitter::Choose() +class SkDraw : public SkDrawBase { public: - Draw(); - - 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 { - this->drawRect(rect, paint, nullptr, nullptr); - } - void drawOval(const SkRect&, const SkPaint&) const; - void drawRRect(const SkRRect&, const SkPaint&) const; - // Specialized draw for RRect that only draws if it is nine-patchable. - bool drawRRectNinePatch(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. - * - * If prePathMatrix is not null, it should logically be applied before any - * stroking or other effects. If there are no effects on the paint that - * 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, - bool pathIsMutable) const { - this->drawPath(path, paint, prePathMatrix, pathIsMutable, SkDrawCoverage::kNo); - } - - /** - * Overwrite the target with the path's coverage (i.e. its mask). - * Will overwrite the entire device, so it need not be zero'd first. - * - * Only device A8 is supported right now. - */ - void drawPathCoverage(const SkPath& src, - const SkPaint& paint, - SkBlitter* customBlitter = nullptr) const { - bool isHairline = paint.getStyle() == SkPaint::kStroke_Style && paint.getStrokeWidth() == 0; - this->drawPath(src, - paint, - nullptr, - false, - isHairline ? SkDrawCoverage::kNo : SkDrawCoverage::kYes, - customBlitter); - } - - void drawDevicePoints(SkCanvas::PointMode, - SkSpan<const SkPoint>, - const SkPaint&, - SkDevice*) const; - - enum class RectType { - kHair, - kFill, - kStroke, - kPath, - }; - - /** - * Based on the paint's style, strokeWidth, and the matrix, classify how - * to draw the rect. If no special-case is available, returns - * RectType::kPath. - * - * Iff RectType == RectType::kStroke, then strokeSize is set to the device - * width and height of the stroke. - */ - static RectType ComputeRectType(const SkRect&, - const SkPaint&, - const SkMatrix&, - SkPoint* strokeSize); - - using BlitterChooser = SkBlitter*(const SkPixmap& dst, - const SkMatrix& ctm, - const SkPaint&, - SkArenaAlloc*, - SkDrawCoverage drawCoverage, - sk_sp<SkShader> clipShader, - const SkSurfaceProps&, - const SkRect& devBounds); + SkDraw(); /* If dstOrNull is null, computes a dst by mapping the bitmap's bounds through the matrix. */ - void drawBitmap(const SkBitmap&, const SkMatrix&, const SkRect* dstOrNull, - const SkSamplingOptions&, const SkPaint&) const override; - void drawSprite(const SkBitmap&, int x, int y, const SkPaint&) const; - void drawGlyphRunList(SkCanvas* canvas, - GlyphRunListPainter* glyphPainter, - const sktext::GlyphRunList& glyphRunList, - const SkPaint& paint) const; + void drawBitmap(const SkBitmap&, const SkMatrix&, const SkRect* dstOrNull, + const SkSamplingOptions&, const SkPaint&) const override; + void drawSprite(const SkBitmap&, int x, int y, const SkPaint&) const; + void drawGlyphRunList(SkCanvas* canvas, + SkGlyphRunListPainterCPU* glyphPainter, + const sktext::GlyphRunList& glyphRunList, + const SkPaint& paint) const; void paintMasks(SkZip<const SkGlyph*, SkPoint> accepted, const SkPaint& paint) const override; - void drawPoints(SkCanvas::PointMode, SkSpan<const SkPoint>, const SkPaint&, SkDevice*) const; + void drawPoints(SkCanvas::PointMode, size_t count, const SkPoint[], + const SkPaint&, SkDevice*) const; /* If skipColorXform, skips color conversion when assigning per-vertex colors */ void drawVertices(const SkVertices*, sk_sp<SkBlender>, const SkPaint&, bool skipColorXform) const; - void drawAtlas(SkSpan<const SkRSXform>, SkSpan<const SkRect>, SkSpan<const SkColor>, + void drawAtlas(const SkRSXform[], const SkRect[], const SkColor[], int count, sk_sp<SkBlender>, const SkPaint&); - void drawDevMask(const SkMask& mask, const SkPaint&, const SkMatrix*) const; - void drawBitmapAsMask(const SkBitmap&, const SkSamplingOptions&, const SkPaint&, - const SkMatrix* paintMatrix) const; +#if defined(SK_SUPPORT_LEGACY_ALPHA_BITMAP_AS_COVERAGE) + void drawDevMask(const SkMask& mask, const SkPaint&) const; + void drawBitmapAsMask(const SkBitmap&, const SkSamplingOptions&, const SkPaint&) const; +#endif private: - void drawPath(const SkPath&, - const SkPaint&, - const SkMatrix* preMatrix, - bool pathIsMutable, - SkDrawCoverage drawCoverage, - SkBlitter* customBlitter = nullptr) const; - - void drawLine(const SkPoint[2], const SkPaint&) const; - - void drawDevPath(const SkPathRaw&, - const SkPaint& paint, - SkDrawCoverage drawCoverage, - SkBlitter* customBlitter, - bool doFill) const; - /** - * Return the current clip bounds, in local coordinates, with slop to account - * for antialiasing or hairlines (i.e. device-bounds outset by 1, and then - * run through the inverse of the matrix). - * - * If the matrix cannot be inverted, or the current clip is empty, return - * false and ignore bounds parameter. - */ - [[nodiscard]] bool computeConservativeLocalClipBounds(SkRect* bounds) const; - void drawFixedVertices(const SkVertices* vertices, sk_sp<SkBlender> blender, const SkPaint& paint, @@ -243,23 +74,6 @@ private: const SkPoint3* dev3, SkArenaAlloc* outerAlloc, bool skipColorXform) const; - -public: - SkPixmap fDst; - BlitterChooser* fBlitterChooser{nullptr}; // required - const SkMatrix* fCTM{nullptr}; // required - const SkRasterClip* fRC{nullptr}; // required - const SkSurfaceProps* fProps{nullptr}; // optional - - const ContextImpl* fCtx{nullptr}; // optional for now - -#ifdef SK_DEBUG - void validate() const; -#else - void validate() const {} -#endif }; -} // namespace skcpu - #endif diff --git a/gfx/skia/skia/src/core/SkDrawBase.cpp b/gfx/skia/skia/src/core/SkDrawBase.cpp @@ -0,0 +1,772 @@ +/* + * Copyright 2023 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "include/core/SkMatrix.h" +#include "include/core/SkPaint.h" +#include "include/core/SkPath.h" +#include "include/core/SkPathEffect.h" +#include "include/core/SkPathTypes.h" +#include "include/core/SkPathUtils.h" +#include "include/core/SkPixmap.h" +#include "include/core/SkPoint.h" +#include "include/core/SkRRect.h" +#include "include/core/SkRect.h" +#include "include/core/SkScalar.h" +#include "include/core/SkStrokeRec.h" +#include "include/private/base/SkAssert.h" +#include "include/private/base/SkCPUTypes.h" +#include "include/private/base/SkDebug.h" +#include "include/private/base/SkFloatingPoint.h" +#include "include/private/base/SkTemplates.h" +#include "src/base/SkTLazy.h" +#include "src/base/SkZip.h" +#include "src/core/SkAutoBlitterChoose.h" +#include "src/core/SkBlendModePriv.h" +#include "src/core/SkBlitter_A8.h" +#include "src/core/SkDevice.h" +#include "src/core/SkDrawBase.h" +#include "src/core/SkDrawProcs.h" +#include "src/core/SkMask.h" +#include "src/core/SkMaskFilterBase.h" +#include "src/core/SkPathEffectBase.h" +#include "src/core/SkPathPriv.h" +#include "src/core/SkRasterClip.h" +#include "src/core/SkRectPriv.h" +#include "src/core/SkScan.h" + +#include <algorithm> +#include <cstddef> +#include <optional> + +class SkBitmap; +class SkBlitter; +class SkGlyph; +class SkMaskFilter; + +using namespace skia_private; + +/////////////////////////////////////////////////////////////////////////////// + +SkDrawBase::SkDrawBase() {} + +bool SkDrawBase::computeConservativeLocalClipBounds(SkRect* localBounds) const { + if (fRC->isEmpty()) { + return false; + } + + SkMatrix inverse; + if (!fCTM->invert(&inverse)) { + return false; + } + + SkIRect devBounds = fRC->getBounds(); + // outset to have slop for antialasing and hairlines + devBounds.outset(1, 1); + inverse.mapRect(localBounds, SkRect::Make(devBounds)); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkDrawBase::drawPaint(const SkPaint& paint) const { + SkDEBUGCODE(this->validate();) + + if (fRC->isEmpty()) { + return; + } + + SkIRect devRect; + devRect.setWH(fDst.width(), fDst.height()); + + SkAutoBlitterChoose blitter(*this, nullptr, paint); + SkScan::FillIRect(devRect, *fRC, blitter.get()); +} + +/////////////////////////////////////////////////////////////////////////////// + +static inline SkPoint compute_stroke_size(const SkPaint& paint, const SkMatrix& matrix) { + SkASSERT(matrix.rectStaysRect()); + SkASSERT(SkPaint::kFill_Style != paint.getStyle()); + + SkVector size; + SkPoint pt = { paint.getStrokeWidth(), paint.getStrokeWidth() }; + matrix.mapVectors(&size, &pt, 1); + return SkPoint::Make(SkScalarAbs(size.fX), SkScalarAbs(size.fY)); +} + +static bool easy_rect_join(const SkRect& rect, const SkPaint& paint, const SkMatrix& matrix, + SkPoint* strokeSize) { + if (rect.isEmpty() || SkPaint::kMiter_Join != paint.getStrokeJoin() || + paint.getStrokeMiter() < SK_ScalarSqrt2) { + return false; + } + + *strokeSize = compute_stroke_size(paint, matrix); + return true; +} + +SkDrawBase::RectType SkDrawBase::ComputeRectType(const SkRect& rect, + const SkPaint& paint, + const SkMatrix& matrix, + SkPoint* strokeSize) { + RectType rtype; + const SkScalar width = paint.getStrokeWidth(); + const bool zeroWidth = (0 == width); + SkPaint::Style style = paint.getStyle(); + + if ((SkPaint::kStrokeAndFill_Style == style) && zeroWidth) { + style = SkPaint::kFill_Style; + } + + if (paint.getPathEffect() || paint.getMaskFilter() || + !matrix.rectStaysRect() || SkPaint::kStrokeAndFill_Style == style) { + rtype = kPath_RectType; + } else if (SkPaint::kFill_Style == style) { + rtype = kFill_RectType; + } else if (zeroWidth) { + rtype = kHair_RectType; + } else if (easy_rect_join(rect, paint, matrix, strokeSize)) { + rtype = kStroke_RectType; + } else { + rtype = kPath_RectType; + } + return rtype; +} + +static const SkPoint* rect_points(const SkRect& r) { + return reinterpret_cast<const SkPoint*>(&r); +} + +static SkPoint* rect_points(SkRect& r) { + return reinterpret_cast<SkPoint*>(&r); +} + +static void draw_rect_as_path(const SkDrawBase& orig, + const SkRect& prePaintRect, + const SkPaint& paint, + const SkMatrix& ctm) { + SkDrawBase draw(orig); + draw.fCTM = &ctm; + SkPath tmp; + tmp.addRect(prePaintRect); + tmp.setFillType(SkPathFillType::kWinding); + draw.drawPath(tmp, paint, nullptr, true); +} + +void SkDrawBase::drawRect(const SkRect& prePaintRect, const SkPaint& paint, + const SkMatrix* paintMatrix, const SkRect* postPaintRect) const { + SkDEBUGCODE(this->validate();) + + // nothing to draw + if (fRC->isEmpty()) { + return; + } + + SkTCopyOnFirstWrite<SkMatrix> matrix(fCTM); + if (paintMatrix) { + SkASSERT(postPaintRect); + matrix.writable()->preConcat(*paintMatrix); + } else { + SkASSERT(!postPaintRect); + } + + SkPoint strokeSize; + RectType rtype = ComputeRectType(prePaintRect, paint, *fCTM, &strokeSize); + + if (kPath_RectType == rtype) { + draw_rect_as_path(*this, prePaintRect, paint, *matrix); + return; + } + + SkRect devRect; + const SkRect& paintRect = paintMatrix ? *postPaintRect : prePaintRect; + // skip the paintMatrix when transforming the rect by the CTM + fCTM->mapPoints(rect_points(devRect), rect_points(paintRect), 2); + devRect.sort(); + + // look for the quick exit, before we build a blitter + SkRect bbox = devRect; + if (paint.getStyle() != SkPaint::kFill_Style) { + // extra space for hairlines + if (paint.getStrokeWidth() == 0) { + bbox.outset(1, 1); + } else { + // For kStroke_RectType, strokeSize is already computed. + const SkPoint& ssize = (kStroke_RectType == rtype) + ? strokeSize + : compute_stroke_size(paint, *fCTM); + bbox.outset(SkScalarHalf(ssize.x()), SkScalarHalf(ssize.y())); + } + } + if (SkPathPriv::TooBigForMath(bbox)) { + return; + } + + if (!SkRectPriv::FitsInFixed(bbox) && rtype != kHair_RectType) { + draw_rect_as_path(*this, prePaintRect, paint, *matrix); + return; + } + + SkIRect ir = bbox.roundOut(); + if (fRC->quickReject(ir)) { + return; + } + + SkAutoBlitterChoose blitterStorage(*this, matrix, paint); + const SkRasterClip& clip = *fRC; + SkBlitter* blitter = blitterStorage.get(); + + // we want to "fill" if we are kFill or kStrokeAndFill, since in the latter + // case we are also hairline (if we've gotten to here), which devolves to + // effectively just kFill + switch (rtype) { + case kFill_RectType: + if (paint.isAntiAlias()) { + SkScan::AntiFillRect(devRect, clip, blitter); + } else { + SkScan::FillRect(devRect, clip, blitter); + } + break; + case kStroke_RectType: + if (paint.isAntiAlias()) { + SkScan::AntiFrameRect(devRect, strokeSize, clip, blitter); + } else { + SkScan::FrameRect(devRect, strokeSize, clip, blitter); + } + break; + case kHair_RectType: + if (paint.isAntiAlias()) { + SkScan::AntiHairRect(devRect, clip, blitter); + } else { + SkScan::HairRect(devRect, clip, blitter); + } + break; + default: + SkDEBUGFAIL("bad rtype"); + } +} + +static SkScalar fast_len(const SkVector& vec) { + SkScalar x = SkScalarAbs(vec.fX); + SkScalar y = SkScalarAbs(vec.fY); + if (x < y) { + using std::swap; + swap(x, y); + } + return x + SkScalarHalf(y); +} + +bool SkDrawTreatAAStrokeAsHairline(SkScalar strokeWidth, const SkMatrix& matrix, + SkScalar* coverage) { + SkASSERT(strokeWidth > 0); + // We need to try to fake a thick-stroke with a modulated hairline. + + if (matrix.hasPerspective()) { + return false; + } + + SkVector src[2], dst[2]; + src[0].set(strokeWidth, 0); + src[1].set(0, strokeWidth); + matrix.mapVectors(dst, src, 2); + SkScalar len0 = fast_len(dst[0]); + SkScalar len1 = fast_len(dst[1]); + if (len0 <= SK_Scalar1 && len1 <= SK_Scalar1) { + if (coverage) { + *coverage = SkScalarAve(len0, len1); + } + return true; + } + return false; +} + +void SkDrawBase::drawRRect(const SkRRect& rrect, const SkPaint& paint) const { + SkDEBUGCODE(this->validate()); + + if (fRC->isEmpty()) { + return; + } + + { + // TODO: Investigate optimizing these options. They are in the same + // order as SkDrawBase::drawPath, which handles each case. It may be + // that there is no way to optimize for these using the SkRRect path. + SkScalar coverage; + if (SkDrawTreatAsHairline(paint, *fCTM, &coverage)) { + goto DRAW_PATH; + } + + if (paint.getPathEffect() || paint.getStyle() != SkPaint::kFill_Style) { + goto DRAW_PATH; + } + } + + if (paint.getMaskFilter()) { + // Transform the rrect into device space. + SkRRect devRRect; + if (rrect.transform(*fCTM, &devRRect)) { + SkAutoBlitterChoose blitter(*this, nullptr, paint); + if (as_MFB(paint.getMaskFilter())->filterRRect(devRRect, *fCTM, *fRC, blitter.get())) { + return; // filterRRect() called the blitter, so we're done + } + } + } + +DRAW_PATH: + // Now fall back to the default case of using a path. + SkPath path; + path.addRRect(rrect); + this->drawPath(path, paint, nullptr, true); +} + +void SkDrawBase::drawDevPath(const SkPath& devPath, + const SkPaint& paint, + SkDrawCoverage drawCoverage, + SkBlitter* customBlitter, + bool doFill) const { + if (SkPathPriv::TooBigForMath(devPath)) { + return; + } + SkBlitter* blitter = nullptr; + SkAutoBlitterChoose blitterStorage; + if (nullptr == customBlitter) { + blitter = blitterStorage.choose(*this, nullptr, paint, drawCoverage); + } else { + blitter = customBlitter; + } + + if (paint.getMaskFilter()) { + SkStrokeRec::InitStyle style = doFill ? SkStrokeRec::kFill_InitStyle + : SkStrokeRec::kHairline_InitStyle; + if (as_MFB(paint.getMaskFilter())->filterPath(devPath, *fCTM, *fRC, blitter, style)) { + return; // filterPath() called the blitter, so we're done + } + } + + void (*proc)(const SkPath&, const SkRasterClip&, SkBlitter*); + if (doFill) { + if (paint.isAntiAlias()) { + proc = SkScan::AntiFillPath; + } else { + proc = SkScan::FillPath; + } + } else { // hairline + if (paint.isAntiAlias()) { + switch (paint.getStrokeCap()) { + case SkPaint::kButt_Cap: + proc = SkScan::AntiHairPath; + break; + case SkPaint::kSquare_Cap: + proc = SkScan::AntiHairSquarePath; + break; + case SkPaint::kRound_Cap: + proc = SkScan::AntiHairRoundPath; + break; + } + } else { + switch (paint.getStrokeCap()) { + case SkPaint::kButt_Cap: + proc = SkScan::HairPath; + break; + case SkPaint::kSquare_Cap: + proc = SkScan::HairSquarePath; + break; + case SkPaint::kRound_Cap: + proc = SkScan::HairRoundPath; + break; + } + } + } + + proc(devPath, *fRC, blitter); +} + +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 + if (fRC->isEmpty()) { + return; + } + + SkPath* pathPtr = const_cast<SkPath*>(&origSrcPath); + bool doFill = true; + SkPath tmpPathStorage; + SkPath* tmpPath = &tmpPathStorage; + SkTCopyOnFirstWrite<SkMatrix> matrix(fCTM); + tmpPath->setIsVolatile(true); + + if (prePathMatrix) { + if (origPaint.getPathEffect() || origPaint.getStyle() != SkPaint::kFill_Style) { + SkPath* result = pathPtr; + + if (!pathIsMutable) { + result = tmpPath; + pathIsMutable = true; + } + pathPtr->transform(*prePathMatrix, result); + pathPtr = result; + } else { + matrix.writable()->preConcat(*prePathMatrix); + } + } + + SkTCopyOnFirstWrite<SkPaint> paint(origPaint); + + { + SkScalar coverage; + if (SkDrawTreatAsHairline(origPaint, *matrix, &coverage)) { + const auto bm = origPaint.asBlendMode(); + if (SK_Scalar1 == coverage) { + paint.writable()->setStrokeWidth(0); + } else if (bm && SkBlendMode_SupportsCoverageAsAlpha(bm.value())) { + U8CPU newAlpha; +#if 0 + newAlpha = SkToU8(SkScalarRoundToInt(coverage * origPaint.getAlpha())); +#else + // this is the old technique, which we preserve for now so + // we don't change previous results (testing) + // the new way seems fine, its just (a tiny bit) different + int scale = (int)(coverage * 256); + newAlpha = origPaint.getAlpha() * scale >> 8; +#endif + SkPaint* writablePaint = paint.writable(); + writablePaint->setStrokeWidth(0); + writablePaint->setAlpha(newAlpha); + } + } + } + + if (paint->getPathEffect() || paint->getStyle() != SkPaint::kFill_Style) { + SkRect cullRect; + const SkRect* cullRectPtr = nullptr; + if (this->computeConservativeLocalClipBounds(&cullRect)) { + cullRectPtr = &cullRect; + } + doFill = skpathutils::FillPathWithPaint(*pathPtr, *paint, tmpPath, cullRectPtr, *fCTM); + pathPtr = tmpPath; + } + + // avoid possibly allocating a new path in transform if we can + SkPath* devPathPtr = pathIsMutable ? pathPtr : tmpPath; + + // transform the path into device space + pathPtr->transform(*matrix, devPathPtr); + +#if defined(SK_BUILD_FOR_FUZZER) + if (devPathPtr->countPoints() > 1000) { + return; + } +#endif + + this->drawDevPath(*devPathPtr, *paint, drawCoverage, customBlitter, doFill); +} + +void SkDrawBase::paintMasks(SkZip<const SkGlyph*, SkPoint>, const SkPaint&) const { + SkASSERT(false); +} +void SkDrawBase::drawBitmap(const SkBitmap&, const SkMatrix&, const SkRect*, + const SkSamplingOptions&, const SkPaint&) const { + SkASSERT(false); +} + +//////////////////////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_DEBUG + +void SkDrawBase::validate() const { + SkASSERT(fCTM != nullptr); + SkASSERT(fRC != nullptr); + + const SkIRect& cr = fRC->getBounds(); + SkIRect br; + + br.setWH(fDst.width(), fDst.height()); + SkASSERT(cr.isEmpty() || br.contains(cr)); +} + +#endif + +//////////////////////////////////////////////////////////////////////////////////////////////// + +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(); + + 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) + // Ugh. Guard against gigantic margins from wacky filters. Without this + // check we can request arbitrary amounts of slop beyond our visible + // clip, and bring down the renderer (at least on finite RAM machines + // like handsets, etc.). Need to balance this invented value between + // quality of large filters like blurs, and the corresponding memory + // requests. + static constexpr int kMaxMargin = 128; + if (!bounds->intersect(clipBounds.makeOutset(std::min(margin.fX, kMaxMargin), + std::min(margin.fY, kMaxMargin)))) { + return false; + } + + return true; +} + +static void draw_into_mask(const SkMask& mask, const SkPath& devPath, + SkStrokeRec::InitStyle style) { + SkDrawBase draw; + draw.fBlitterChooser = SkA8Blitter_Choose; + if (!draw.fDst.reset(mask)) { + return; + } + + SkRasterClip clip; + SkMatrix matrix; + SkPaint paint; + + clip.setRect(SkIRect::MakeWH(mask.fBounds.width(), mask.fBounds.height())); + matrix.setTranslate(-SkIntToScalar(mask.fBounds.fLeft), + -SkIntToScalar(mask.fBounds.fTop)); + + draw.fRC = &clip; + draw.fCTM = &matrix; + paint.setAntiAlias(true); + switch (style) { + case SkStrokeRec::kHairline_InitStyle: + SkASSERT(!paint.getStrokeWidth()); + paint.setStyle(SkPaint::kStroke_Style); + break; + case SkStrokeRec::kFill_InitStyle: + SkASSERT(paint.getStyle() == SkPaint::kFill_Style); + break; + + } + 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, 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 (!compute_mask_bounds(pathBounds, clipBounds, filter, + filterMatrix, &dst->bounds())) { + return false; + } + } + + if (SkMaskBuilder::kComputeBoundsAndRenderImage_CreateMode == mode) { + dst->format() = SkMask::kA8_Format; + dst->rowBytes() = dst->fBounds.width(); + size_t size = dst->computeImageSize(); + if (0 == size) { + // we're too big to allocate the mask, abort + return false; + } + dst->image() = SkMaskBuilder::AllocImage(size, SkMaskBuilder::kZeroInit_Alloc); + } + + if (SkMaskBuilder::kJustComputeBounds_CreateMode != mode) { + draw_into_mask(*dst, devPath, style); + } + return true; +} + +void SkDrawBase::drawDevicePoints(SkCanvas::PointMode mode, size_t count, + const SkPoint pts[], const SkPaint& paint, + SkDevice* device) const { + // if we're in lines mode, force count to be even + if (SkCanvas::kLines_PointMode == mode) { + count &= ~(size_t)1; + } + + SkASSERT(pts != nullptr); + SkDEBUGCODE(this->validate();) + + // nothing to draw + if (!count || fRC->isEmpty()) { + return; + } + + // needed? + if (!SkIsFinite(&pts[0].fX, count * 2)) { + return; + } + + switch (mode) { + case SkCanvas::kPoints_PointMode: { + // temporarily mark the paint as filling. + SkPaint newPaint(paint); + newPaint.setStyle(SkPaint::kFill_Style); + + SkScalar width = newPaint.getStrokeWidth(); + SkScalar radius = SkScalarHalf(width); + + if (newPaint.getStrokeCap() == SkPaint::kRound_Cap) { + if (device) { + for (size_t i = 0; i < count; ++i) { + SkRect r = SkRect::MakeLTRB(pts[i].fX - radius, pts[i].fY - radius, + pts[i].fX + radius, pts[i].fY + radius); + device->drawOval(r, newPaint); + } + } else { + SkPath path; + SkMatrix preMatrix; + + path.addCircle(0, 0, radius); + for (size_t i = 0; i < count; i++) { + preMatrix.setTranslate(pts[i].fX, pts[i].fY); + // pass true for the last point, since we can modify + // then path then + path.setIsVolatile((count-1) == i); + this->drawPath(path, newPaint, &preMatrix, (count-1) == i); + } + } + } else { + SkRect r; + + for (size_t i = 0; i < count; i++) { + r.fLeft = pts[i].fX - radius; + r.fTop = pts[i].fY - radius; + r.fRight = r.fLeft + width; + r.fBottom = r.fTop + width; + if (device) { + device->drawRect(r, newPaint); + } else { + this->drawRect(r, newPaint); + } + } + } + break; + } + case SkCanvas::kLines_PointMode: + if (2 == count && paint.getPathEffect()) { + // most likely a dashed line - see if it is one of the ones + // we can accelerate + SkStrokeRec stroke(paint); + SkPathEffectBase::PointData pointData; + + SkPath path = SkPath::Line(pts[0], pts[1]); + + SkRect cullRect = SkRect::Make(fRC->getBounds()); + + if (as_PEB(paint.getPathEffect())->asPoints(&pointData, path, stroke, *fCTM, + &cullRect)) { + // 'asPoints' managed to find some fast path + + SkPaint newP(paint); + newP.setPathEffect(nullptr); + newP.setStyle(SkPaint::kFill_Style); + + if (!pointData.fFirst.isEmpty()) { + if (device) { + device->drawPath(pointData.fFirst, newP, true); + } else { + this->drawPath(pointData.fFirst, newP, nullptr, true); + } + } + + if (!pointData.fLast.isEmpty()) { + if (device) { + device->drawPath(pointData.fLast, newP, true); + } else { + this->drawPath(pointData.fLast, newP, nullptr, true); + } + } + + if (pointData.fSize.fX == pointData.fSize.fY) { + // The rest of the dashed line can just be drawn as points + SkASSERT(pointData.fSize.fX == SkScalarHalf(newP.getStrokeWidth())); + + if (SkPathEffectBase::PointData::kCircles_PointFlag & pointData.fFlags) { + newP.setStrokeCap(SkPaint::kRound_Cap); + } else { + newP.setStrokeCap(SkPaint::kButt_Cap); + } + + if (device) { + device->drawPoints(SkCanvas::kPoints_PointMode, + pointData.fNumPoints, + pointData.fPoints, + newP); + } else { + this->drawDevicePoints(SkCanvas::kPoints_PointMode, + pointData.fNumPoints, + pointData.fPoints, + newP, + device); + } + break; + } else { + // The rest of the dashed line must be drawn as rects + SkASSERT(!(SkPathEffectBase::PointData::kCircles_PointFlag & + pointData.fFlags)); + + SkRect r; + + for (int i = 0; i < pointData.fNumPoints; ++i) { + r.setLTRB(pointData.fPoints[i].fX - pointData.fSize.fX, + pointData.fPoints[i].fY - pointData.fSize.fY, + pointData.fPoints[i].fX + pointData.fSize.fX, + pointData.fPoints[i].fY + pointData.fSize.fY); + if (device) { + device->drawRect(r, newP); + } else { + this->drawRect(r, newP); + } + } + } + + break; + } + } + [[fallthrough]]; // couldn't take fast path + case SkCanvas::kPolygon_PointMode: { + count -= 1; + SkPath path; + SkPaint p(paint); + p.setStyle(SkPaint::kStroke_Style); + size_t inc = (SkCanvas::kLines_PointMode == mode) ? 2 : 1; + path.setIsVolatile(true); + for (size_t i = 0; i < count; i += inc) { + path.moveTo(pts[i]); + path.lineTo(pts[i+1]); + if (device) { + device->drawPath(path, p, true); + } else { + this->drawPath(path, p, nullptr, true); + } + path.rewind(); + } + break; + } + } +} diff --git a/gfx/skia/skia/src/core/SkDrawBase.h b/gfx/skia/skia/src/core/SkDrawBase.h @@ -0,0 +1,167 @@ +/* + * 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 SkDrawBase_DEFINED +#define SkDrawBase_DEFINED + +#include "include/core/SkCanvas.h" +#include "include/core/SkPaint.h" +#include "include/core/SkPixmap.h" +#include "include/core/SkRefCnt.h" +#include "include/core/SkSamplingOptions.h" +#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" + +#include <cstddef> + +class SkArenaAlloc; +class SkBitmap; +class SkBlitter; +class SkDevice; +class SkGlyph; +class SkMaskFilter; +class SkMatrix; +class SkPath; +class SkRRect; +class SkRasterClip; +class SkShader; +class SkSurfaceProps; +struct SkIRect; +struct SkPoint; +struct SkRect; + +class SkDrawBase : public SkGlyphRunListPainterCPU::BitmapDevicePainter { +public: + SkDrawBase(); + + 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 { + this->drawRect(rect, paint, nullptr, nullptr); + } + 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. + * + * If prePathMatrix is not null, it should logically be applied before any + * stroking or other effects. If there are no effects on the paint that + * 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, bool pathIsMutable) const { + this->drawPath(path, paint, prePathMatrix, pathIsMutable, SkDrawCoverage::kNo); + } + + /** + * Overwrite the target with the path's coverage (i.e. its mask). + * Will overwrite the entire device, so it need not be zero'd first. + * + * Only device A8 is supported right now. + */ + void drawPathCoverage(const SkPath& src, const SkPaint& paint, + SkBlitter* customBlitter = nullptr) const { + bool isHairline = paint.getStyle() == SkPaint::kStroke_Style && + paint.getStrokeWidth() == 0; + 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; + + /** 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). + */ + static bool DrawToMask(const SkPath& devPath, const SkIRect& clipBounds, + const SkMaskFilter*, const SkMatrix* filterMatrix, + SkMaskBuilder* dst, SkMaskBuilder::CreateMode mode, + SkStrokeRec::InitStyle style); + + enum RectType { + kHair_RectType, + kFill_RectType, + kStroke_RectType, + kPath_RectType + }; + + /** + * Based on the paint's style, strokeWidth, and the matrix, classify how + * to draw the rect. If no special-case is available, returns + * kPath_RectType. + * + * Iff RectType == kStroke_RectType, then strokeSize is set to the device + * width and height of the stroke. + */ + static RectType ComputeRectType(const SkRect&, const SkPaint&, const SkMatrix&, + SkPoint* strokeSize); + + using BlitterChooser = SkBlitter*(const SkPixmap& dst, + const SkMatrix& ctm, + const SkPaint&, + SkArenaAlloc*, + SkDrawCoverage drawCoverage, + sk_sp<SkShader> clipShader, + const SkSurfaceProps&); + +private: + // not supported + void paintMasks(SkZip<const SkGlyph*, SkPoint> accepted, const SkPaint& paint) const override; + void drawBitmap(const SkBitmap&, const SkMatrix&, const SkRect* dstOrNull, + const SkSamplingOptions&, const SkPaint&) const override; + + void drawPath(const SkPath&, + const SkPaint&, + const SkMatrix* preMatrix, + bool pathIsMutable, + SkDrawCoverage drawCoverage, + SkBlitter* customBlitter = nullptr) const; + + void drawLine(const SkPoint[2], const SkPaint&) const; + + void drawDevPath(const SkPath& devPath, + const SkPaint& paint, + SkDrawCoverage drawCoverage, + SkBlitter* customBlitter, + bool doFill) const; + /** + * Return the current clip bounds, in local coordinates, with slop to account + * for antialiasing or hairlines (i.e. device-bounds outset by 1, and then + * run through the inverse of the matrix). + * + * If the matrix cannot be inverted, or the current clip is empty, return + * false and ignore bounds parameter. + */ + [[nodiscard]] bool computeConservativeLocalClipBounds(SkRect* bounds) const; + +public: + SkPixmap fDst; + BlitterChooser* fBlitterChooser{nullptr}; // required + const SkMatrix* fCTM{nullptr}; // required + const SkRasterClip* fRC{nullptr}; // required + const SkSurfaceProps* fProps{nullptr}; // optional + +#ifdef SK_DEBUG + void validate() const; +#else + void validate() const {} +#endif +}; + +#endif // SkDrawBase_DEFINED diff --git a/gfx/skia/skia/src/core/SkDrawProcs.h b/gfx/skia/skia/src/core/SkDrawProcs.h @@ -10,11 +10,10 @@ #include "include/core/SkPaint.h" #include "include/core/SkScalar.h" -#include "include/core/SkTypes.h" class SkMatrix; -namespace skcpu { -bool DrawTreatAAStrokeAsHairline(SkScalar strokeWidth, const SkMatrix&, SkScalar* coverage); +bool SkDrawTreatAAStrokeAsHairline(SkScalar strokeWidth, const SkMatrix&, + SkScalar* coverage); /** * If the current paint is set to stroke and the stroke-width when applied to @@ -22,9 +21,8 @@ bool DrawTreatAAStrokeAsHairline(SkScalar strokeWidth, const SkMatrix&, SkScalar * a stroke by drawing a hairline with partial coverage). If any of these * conditions are false, then this returns false and coverage is ignored. */ -inline bool DrawTreatAsHairline(const SkPaint& paint, - const SkMatrix& matrix, - SkScalar* coverage) { +inline bool SkDrawTreatAsHairline(const SkPaint& paint, const SkMatrix& matrix, + SkScalar* coverage) { if (SkPaint::kStroke_Style != paint.getStyle()) { return false; } @@ -39,8 +37,7 @@ inline bool DrawTreatAsHairline(const SkPaint& paint, return false; } - return DrawTreatAAStrokeAsHairline(strokeWidth, matrix, coverage); + return SkDrawTreatAAStrokeAsHairline(strokeWidth, matrix, coverage); } -} // namespace skcpu #endif diff --git a/gfx/skia/skia/src/core/SkDrawShadowInfo.cpp b/gfx/skia/skia/src/core/SkDrawShadowInfo.cpp @@ -153,7 +153,8 @@ void GetLocalBounds(const SkPath& path, const SkDrawShadowRec& rec, const SkMatr rec.fLightPos.fZ, rec.fLightRadius, &spotBlur, &spotScale, &spotOffset); } else { - SkPoint devLightPos = ctm.mapPoint({rec.fLightPos.fX, rec.fLightPos.fY}); + SkPoint devLightPos = SkPoint::Make(rec.fLightPos.fX, rec.fLightPos.fY); + ctm.mapPoints(&devLightPos, 1); SkDrawShadowMetrics::GetSpotParams(occluderZ, devLightPos.fX, devLightPos.fY, rec.fLightPos.fZ, rec.fLightRadius, &spotBlur, &spotScale, &spotOffset); @@ -171,8 +172,9 @@ void GetLocalBounds(const SkPath& path, const SkDrawShadowRec& rec, const SkMatr rec.fLightPos.fZ, rec.fLightRadius, &spotBlur, &spotScale, &spotOffset); // light dir is in device space, so need to map spot offset back into local space - if (auto inverse = ctm.invert()) { - spotOffset = inverse->mapVector(spotOffset); + SkMatrix inverse; + if (ctm.invert(&inverse)) { + inverse.mapVectors(&spotOffset, 1); } } else { SkDrawShadowMetrics::GetSpotParams(occluderZ, rec.fLightPos.fX, rec.fLightPos.fY, @@ -203,8 +205,9 @@ void GetLocalBounds(const SkPath& path, const SkDrawShadowRec& rec, const SkMatr // if perspective, transform back to src space if (ctm.hasPerspective()) { // TODO: create tighter mapping from dev rect back to src rect - if (auto inverse = ctm.invert()) { - inverse->mapRect(bounds); + SkMatrix inverse; + if (ctm.invert(&inverse)) { + inverse.mapRect(bounds); } } } diff --git a/gfx/skia/skia/src/core/SkDraw_atlas.cpp b/gfx/skia/skia/src/core/SkDraw_atlas.cpp @@ -13,13 +13,12 @@ #include "include/core/SkPath.h" #include "include/core/SkPixmap.h" #include "include/core/SkPoint.h" +#include "include/core/SkRSXform.h" #include "include/core/SkRect.h" #include "include/core/SkRefCnt.h" #include "include/core/SkScalar.h" #include "include/core/SkShader.h" -#include "include/core/SkSpan.h" #include "include/core/SkSurfaceProps.h" -#include "include/private/base/SkAssert.h" #include "src/base/SkArenaAlloc.h" #include "src/core/SkBlendModePriv.h" #include "src/core/SkBlenderBase.h" @@ -28,7 +27,6 @@ #include "src/core/SkCoreBlitters.h" #include "src/core/SkDraw.h" #include "src/core/SkEffectPriv.h" -#include "src/core/SkPathRawShapes.h" #include "src/core/SkRasterClip.h" #include "src/core/SkRasterPipeline.h" #include "src/core/SkRasterPipelineOpContexts.h" @@ -38,26 +36,27 @@ #include "src/shaders/SkShaderBase.h" #include "src/shaders/SkTransformShader.h" -#include <cstddef> #include <cstdint> #include <optional> class SkBlender; class SkBlitter; enum class SkBlendMode; -struct SkRSXform; static void fill_rect(const SkMatrix& ctm, const SkRasterClip& rc, - const SkRect& r, SkBlitter* blitter) { + const SkRect& r, SkBlitter* blitter, SkPath* scratchPath) { if (ctm.rectStaysRect()) { - SkScan::FillRect(ctm.mapRect(r), rc, blitter); + SkRect dr; + ctm.mapRect(&dr, r); + SkScan::FillRect(dr, rc, blitter); } else { - SkPathRawShapes::Rect raw(r); - ctm.mapPoints(raw.fStorage); - if (auto bounds = SkRect::Bounds(raw.fStorage)) { - raw.fBounds = *bounds; - SkScan::FillPath(raw, rc, blitter); - } + SkPoint pts[4]; + r.toQuad(pts); + ctm.mapPoints(pts, pts, 4); + + scratchPath->rewind(); + scratchPath->addPoly(pts, 4, true); + SkScan::FillPath(*scratchPath, rc, blitter); } } @@ -69,17 +68,14 @@ static void load_color(SkRasterPipelineContexts::UniformColorCtx* ctx, const flo ctx->rgba[3] = SkScalarRoundToInt(rgba[3]*255); ctx->a = rgba[3]; } -namespace skcpu { -void Draw::drawAtlas(SkSpan<const SkRSXform> xform, - SkSpan<const SkRect> textures, - SkSpan<const SkColor> colors, - sk_sp<SkBlender> blender, - const SkPaint& paint) { - SkASSERT(xform.size() == textures.size()); - SkASSERT(colors.empty() || (xform.size() == colors.size())); - +void SkDraw::drawAtlas(const SkRSXform xform[], + const SkRect textures[], + const SkColor colors[], + int count, + sk_sp<SkBlender> blender, + const SkPaint& paint) { sk_sp<SkShader> atlasShader = paint.refShader(); - if (!atlasShader || xform.empty()) { + if (!atlasShader) { return; } @@ -98,13 +94,8 @@ void Draw::drawAtlas(SkSpan<const SkRSXform> xform, SkRasterPipeline pipeline(&alloc); SkSurfaceProps props = SkSurfacePropsCopyOrDefault(fProps); - SkStageRec rec = {&pipeline, - &alloc, - fDst.colorType(), - fDst.colorSpace(), - p.getColor4f(), - props, - SkRect::MakeEmpty()}; + SkStageRec rec = {&pipeline, &alloc, fDst.colorType(), fDst.colorSpace(), + p.getColor4f(), props}; // We pass an identity matrix here rather than the CTM. The CTM gets folded into the // per-triangle matrix. if (!as_SB(transformShader)->appendRootStages(rec, SkMatrix::I())) { @@ -114,7 +105,7 @@ void Draw::drawAtlas(SkSpan<const SkRSXform> xform, SkRasterPipelineContexts::UniformColorCtx* uniformCtx = nullptr; SkColorSpaceXformSteps steps(sk_srgb_singleton(), kUnpremul_SkAlphaType, rec.fDstCS, kUnpremul_SkAlphaType); - if (!colors.empty()) { + if (colors) { // we will late-bind the values in ctx, once for each color in the loop uniformCtx = alloc.make<SkRasterPipelineContexts::UniformColorCtx>(); rec.fPipeline->append(SkRasterPipelineOp::uniform_color_dst, uniformCtx); @@ -125,7 +116,7 @@ void Draw::drawAtlas(SkSpan<const SkRSXform> xform, SkBlendMode_AppendStages(*bm, rec.fPipeline); } - bool isOpaque = colors.empty() && transformShader->isOpaque(); + bool isOpaque = !colors && transformShader->isOpaque(); if (p.getAlphaf() != 1) { rec.fPipeline->append(SkRasterPipelineOp::scale_1_float, alloc.make<float>(p.getAlphaf())); isOpaque = false; @@ -136,9 +127,10 @@ void Draw::drawAtlas(SkSpan<const SkRSXform> xform, if (!blitter) { return; } + SkPath scratchPath; - for (size_t i = 0; i < xform.size(); ++i) { - if (!colors.empty()) { + for (int i = 0; i < count; ++i) { + if (colors) { SkColor4f c4 = SkColor4f::FromColor(colors[i]); steps.apply(c4.vec()); load_color(uniformCtx, c4.premul().vec()); @@ -148,13 +140,12 @@ void Draw::drawAtlas(SkSpan<const SkRSXform> xform, mx.setRSXform(xform[i]); mx.preTranslate(-textures[i].fLeft, -textures[i].fTop); mx.postConcat(*fCTM); - if (auto inv = mx.invert()) { - if (transformShader->update(*inv)) { - fill_rect(mx, *fRC, textures[i], blitter); - } - } else { - return; // non-invertible + SkMatrix inv; + if (!mx.invert(&inv)) { + return; + } + if (transformShader->update(inv)) { + fill_rect(mx, *fRC, textures[i], blitter, &scratchPath); } } } -} // namespace skcpu diff --git a/gfx/skia/skia/src/core/SkDraw_text.cpp b/gfx/skia/skia/src/core/SkDraw_text.cpp @@ -50,8 +50,7 @@ static bool check_glyph_position(SkPoint position) { lt(position.fY, INT_MIN - (INT16_MIN + 0 /*UINT16_MIN*/))); } -namespace skcpu { -void Draw::paintMasks(SkZip<const SkGlyph*, SkPoint> accepted, const SkPaint& paint) const { +void SkDraw::paintMasks(SkZip<const SkGlyph*, SkPoint> accepted, const SkPaint& paint) const { SkSTArenaAlloc<kSkBlitterContextSize> alloc; SkBlitter* blitter = SkBlitter::Choose(fDst, *fCTM, @@ -59,8 +58,7 @@ void Draw::paintMasks(SkZip<const SkGlyph*, SkPoint> accepted, const SkPaint& pa &alloc, SkDrawCoverage::kNo, fRC->clipShader(), - SkSurfacePropsCopyOrDefault(fProps), - SkRect::MakeEmpty()); + SkSurfacePropsCopyOrDefault(fProps)); SkAAClipBlitterWrapper wrapper{*fRC, blitter}; blitter = wrapper.getBlitter(); @@ -125,10 +123,11 @@ void Draw::paintMasks(SkZip<const SkGlyph*, SkPoint> accepted, const SkPaint& pa } } -void Draw::drawGlyphRunList(SkCanvas* canvas, - GlyphRunListPainter* glyphPainter, - const sktext::GlyphRunList& glyphRunList, - const SkPaint& paint) const { +void SkDraw::drawGlyphRunList(SkCanvas* canvas, + SkGlyphRunListPainterCPU* glyphPainter, + const sktext::GlyphRunList& glyphRunList, + const SkPaint& paint) const { + SkDEBUGCODE(this->validate();) if (fRC->isEmpty()) { @@ -137,7 +136,6 @@ void Draw::drawGlyphRunList(SkCanvas* canvas, glyphPainter->drawForBitmapDevice(canvas, this, glyphRunList, paint, *fCTM); } -} // namespace skcpu #if defined _WIN32 #pragma warning ( pop ) diff --git a/gfx/skia/skia/src/core/SkDraw_vertices.cpp b/gfx/skia/skia/src/core/SkDraw_vertices.cpp @@ -48,8 +48,8 @@ class SkBlitter; -std::optional<SkMatrix> texture_to_matrix(const VertState& state, const SkPoint verts[], - const SkPoint texs[]) { +[[nodiscard]] static bool texture_to_matrix(const VertState& state, const SkPoint verts[], + const SkPoint texs[], SkMatrix* matrix) { SkPoint src[3], dst[3]; src[0] = verts[state.f0]; @@ -58,7 +58,7 @@ std::optional<SkMatrix> texture_to_matrix(const VertState& state, const SkPoint dst[0] = texs[state.f0]; dst[1] = texs[state.f1]; dst[2] = texs[state.f2]; - return SkMatrix::PolyToPoly(src, dst); + return matrix->setPolyToPoly(src, dst, 3); } // Convert the SkColors into float colors. The conversion depends on some conditions: @@ -184,15 +184,14 @@ static void fill_triangle(const VertState& state, SkBlitter* blitter, const SkRa } } -namespace skcpu { -void Draw::drawFixedVertices(const SkVertices* vertices, - sk_sp<SkBlender> blender, - const SkPaint& paint, - const SkMatrix& ctmInverse, - const SkPoint* dev2, - const SkPoint3* dev3, - SkArenaAlloc* outerAlloc, - bool skipColorXform) const { +void SkDraw::drawFixedVertices(const SkVertices* vertices, + sk_sp<SkBlender> blender, + const SkPaint& paint, + const SkMatrix& ctmInverse, + const SkPoint* dev2, + const SkPoint3* dev3, + SkArenaAlloc* outerAlloc, + bool skipColorXform) const { SkVerticesPriv info(vertices->priv()); const int vertexCount = info.vertexCount(); @@ -289,8 +288,7 @@ void Draw::drawFixedVertices(const SkVertices* vertices, *ctm, outerAlloc, fRC->clipShader(), - props, - SkRect::MakeEmpty()); + props); if (!blitter) { return; } @@ -300,18 +298,18 @@ void Draw::drawFixedVertices(const SkVertices* vertices, continue; } - std::optional<SkMatrix> localM; - if (!transformShader || ((localM = texture_to_matrix(state, positions, texCoords)) && - transformShader->update(SkMatrix::Concat(*localM, ctmInverse)))) { + SkMatrix localM; + if (!transformShader || (texture_to_matrix(state, positions, texCoords, &localM) && + transformShader->update(SkMatrix::Concat(localM, ctmInverse)))) { fill_triangle(state, blitter, *fRC, dev2, dev3); } } } -void Draw::drawVertices(const SkVertices* vertices, - sk_sp<SkBlender> blender, - const SkPaint& paint, - bool skipColorXform) const { +void SkDraw::drawVertices(const SkVertices* vertices, + sk_sp<SkBlender> blender, + const SkPaint& paint, + bool skipColorXform) const { SkVerticesPriv info(vertices->priv()); const int vertexCount = info.vertexCount(); const int indexCount = info.indexCount(); @@ -320,8 +318,8 @@ void Draw::drawVertices(const SkVertices* vertices, if (vertexCount < 3 || (indexCount > 0 && indexCount < 3) || fRC->isEmpty()) { return; } - auto ctmInv = fCTM->invert(); - if (!ctmInv) { + SkMatrix ctmInv; + if (!fCTM->invert(&ctmInv)) { return; } @@ -335,21 +333,23 @@ void Draw::drawVertices(const SkVertices* vertices, if (fCTM->hasPerspective()) { dev3 = outerAlloc.makeArray<SkPoint3>(vertexCount); - fCTM->mapPointsToHomogeneous({dev3, vertexCount}, {info.positions(), vertexCount}); + fCTM->mapHomogeneousPoints(dev3, info.positions(), vertexCount); // similar to the bounds check for 2d points (below) if (!SkIsFinite((const SkScalar*)dev3, vertexCount * 3)) { return; } } else { dev2 = outerAlloc.makeArray<SkPoint>(vertexCount); - fCTM->mapPoints({dev2, vertexCount}, {info.positions(), vertexCount}); + fCTM->mapPoints(dev2, info.positions(), vertexCount); - if (SkRect::BoundsOrEmpty({dev2, vertexCount}).isEmpty()) { + SkRect bounds; + // this also sets bounds to empty if we see a non-finite value + bounds.setBounds(dev2, vertexCount); + if (bounds.isEmpty()) { return; } } this->drawFixedVertices( - vertices, std::move(blender), paint, *ctmInv, dev2, dev3, &outerAlloc, skipColorXform); + vertices, std::move(blender), paint, ctmInv, dev2, dev3, &outerAlloc, skipColorXform); } -} // namespace skcpu diff --git a/gfx/skia/skia/src/core/SkEdge.cpp b/gfx/skia/skia/src/core/SkEdge.cpp @@ -7,7 +7,6 @@ #include "src/core/SkEdge.h" -#include "include/core/SkRect.h" #include "include/private/base/SkDebug.h" #include "include/private/base/SkSafe32.h" #include "include/private/base/SkTo.h" @@ -37,84 +36,48 @@ static inline SkFixed SkFDot6ToFixedDiv2(SkFDot6 value) { ///////////////////////////////////////////////////////////////////////// -#if defined(SK_DEBUG) +#ifdef SK_DEBUG void SkEdge::dump() const { - SkASSERT(fSegmentCount == 0); - SkDebugf("line edge: firstY:%d lastY:%d x:%g dx/dy:%g\n" - "\twinding:%d curveShift:%u\n", - fFirstY, - fLastY, - SkFixedToFloat(fX), - SkFixedToFloat(fDxDy), - static_cast<int8_t>(fWinding), - fCurveShift); -} - -void SkQuadraticEdge::dump() const { - SkDebugf("quad edge; %u segment(s) left: firstY:%d lastY:%d x:%g dx/dy:%g\n" - "\tqx:%g qy:%g dqx:%g dqy:%g ddqx:%g ddqy:%g qLastX:%g qLastY:%g\n" - "\twinding:%d curveShift:%u\n", - fSegmentCount, - fFirstY, - fLastY, - SkFixedToFloat(fX), - SkFixedToFloat(fDxDy), - SkFixedToFloat(fQx), - SkFixedToFloat(fQy), - SkFixedToFloat(fQDxDt), - SkFixedToFloat(fQDyDt), - SkFixedToFloat(fQD2xDt2), - SkFixedToFloat(fQD2yDt2), - SkFixedToFloat(fQLastX), - SkFixedToFloat(fQLastY), - static_cast<int8_t>(fWinding), - fCurveShift); -} - -void SkCubicEdge::dump() const { - SkDebugf("cube edge; %u segment(s) left: firstY:%d lastY:%d x:%g dx/dy:%g\n" - "qx:%g qy:%g dcx:%g dcy:%g ddcx:%g ddcy:%g dddcx:%g dddcy:%g cLastX:%g cLastY:%g\n" - "\twinding:%d curveShift:%u dShift:%u\n", - fSegmentCount, + int realLastY = SkScalarToFixed(fLastY); + if (fCurveCount > 0) { + realLastY = static_cast<const SkQuadraticEdge*>(this)->fQLastY; + } else if (fCurveCount < 0) { + realLastY = static_cast<const SkCubicEdge*>(this)->fCLastY; + } + SkDebugf("edge (%c): firstY:%d lastY:%d (%g) x:%g dx:%g w:%d\n", + fCurveCount > 0 ? 'Q' : (fCurveCount < 0 ? 'C' : 'L'), fFirstY, fLastY, + SkFixedToFloat(realLastY), SkFixedToFloat(fX), - SkFixedToFloat(fDxDy), - SkFixedToFloat(fCx), - SkFixedToFloat(fCy), - SkFixedToFloat(fCDxDt), - SkFixedToFloat(fCDyDt), - SkFixedToFloat(fCD2xDt2), - SkFixedToFloat(fCD2yDt2), - SkFixedToFloat(fCD3xDt3), - SkFixedToFloat(fCD3yDt3), - SkFixedToFloat(fCLastX), - SkFixedToFloat(fCLastY), - static_cast<int8_t>(fWinding), - fCurveShift, - fCubicDShift); + SkFixedToFloat(fDX), + static_cast<int8_t>(fWinding)); } #endif -bool SkEdge::setLine(const SkPoint& p0, const SkPoint& p1, const SkIRect* clip) { +int SkEdge::setLine(const SkPoint& p0, const SkPoint& p1, const SkIRect* clip, int shift) { SkFDot6 x0, y0, x1, y1; + { #ifdef SK_RASTERIZE_EVEN_ROUNDING - x0 = SkScalarRoundToFDot6(p0.fX, 0); - y0 = SkScalarRoundToFDot6(p0.fY, 0); - x1 = SkScalarRoundToFDot6(p1.fX, 0); - y1 = SkScalarRoundToFDot6(p1.fY, 0); + x0 = SkScalarRoundToFDot6(p0.fX, shift); + y0 = SkScalarRoundToFDot6(p0.fY, shift); + x1 = SkScalarRoundToFDot6(p1.fX, shift); + y1 = SkScalarRoundToFDot6(p1.fY, shift); #else - x0 = SkFloatToFDot6(p0.fX); - y0 = SkFloatToFDot6(p0.fY); - x1 = SkFloatToFDot6(p1.fX); - y1 = SkFloatToFDot6(p1.fY); + float scale = float(1 << (shift + 6)); + x0 = int(p0.fX * scale); + y0 = int(p0.fY * scale); + x1 = int(p1.fX * scale); + y1 = int(p1.fY * scale); #endif + } Winding winding = Winding::kCW; if (y0 > y1) { - std::swap(x0, x1); - std::swap(y0, y1); + using std::swap; + swap(x0, x1); + swap(y0, y1); winding = Winding::kCCW; } @@ -123,73 +86,64 @@ bool SkEdge::setLine(const SkPoint& p0, const SkPoint& p1, const SkIRect* clip) // are we a zero-height line? if (top == bot) { - return false; + return 0; } // are we completely above or below the clip? if (clip && (top >= clip->fBottom || bot <= clip->fTop)) { - return false; + return 0; } SkFixed slope = SkFDot6Div(x1 - x0, y1 - y0); const SkFDot6 dy = SkEdge_Compute_DY(top, y0); - // Note that SkFixedMul(SkFixed, SkFDot6) produces results in SkFDot6 - fX = SkFDot6ToFixed(x0 + SkFixedMul(slope, dy)); - fDxDy = slope; + fX = SkFDot6ToFixed(x0 + SkFixedMul(slope, dy)); // + SK_Fixed1/2 + fDX = slope; fFirstY = top; fLastY = bot - 1; fEdgeType = Type::kLine; - fSegmentCount = 0; + fCurveCount = 0; fWinding = winding; fCurveShift = 0; if (clip) { this->chopLineWithClip(*clip); } - return true; + return 1; } -bool SkEdge::nextSegment() { - SkDEBUGFAILF("Shouldn't be asking a linear edge to go to the next curve."); - return false; -} - -// Draws a line between the provided points and then calculates the slope and starting -// x value to line up with the closest pixel center. Updates the fields in the SkEdge -// base class appropriately. Returns false if this edge would start and stop in the -// same row. -bool SkEdge::updateLine(SkFixed xStart, SkFixed yStart, SkFixed xEnd, SkFixed yEnd) { +// called from a curve subclass +int SkEdge::updateLine(SkFixed x0, SkFixed y0, SkFixed x1, SkFixed y1) +{ SkASSERT(fWinding == Winding::kCW || fWinding == Winding::kCCW); - SkASSERT(fSegmentCount != 0); + SkASSERT(fCurveCount != 0); +// SkASSERT(fCurveShift != 0); - const SkFDot6 y0 = SkFixedToFDot6(yStart); - const SkFDot6 y1 = SkFixedToFDot6(yEnd); + y0 >>= 10; + y1 >>= 10; SkASSERT(y0 <= y1); - const int top = SkFDot6Round(y0); - const int bot = SkFDot6Round(y1); + int top = SkFDot6Round(y0); + int bot = SkFDot6Round(y1); + +// SkASSERT(top >= fFirstY); // are we a zero-height line? - if (top == bot) { - return false; - } + if (top == bot) + return 0; - const SkFDot6 x0 = SkFixedToFDot6(xStart); - const SkFDot6 x1 = SkFixedToFDot6(xEnd); + x0 >>= 10; + x1 >>= 10; SkFixed slope = SkFDot6Div(x1 - x0, y1 - y0); - const SkFDot6 dy = SkEdge_Compute_DY(top, y0); + const SkFDot6 dy = SkEdge_Compute_DY(top, y0); - // We could do this math in fixed point, but it would potentially require some - // rebaselining https://codereview.chromium.org/960353005/#msg6 - // Note that SkFixedMul(SkFixed, SkFDot6) produces results in SkFDot6 - fX = SkFDot6ToFixed(x0 + SkFixedMul(slope, dy)); - fDxDy = slope; + fX = SkFDot6ToFixed(x0 + SkFixedMul(slope, dy)); // + SK_Fixed1/2 + fDX = slope; fFirstY = top; fLastY = bot - 1; - return true; + return 1; } void SkEdge::chopLineWithClip(const SkIRect& clip) @@ -202,36 +156,34 @@ void SkEdge::chopLineWithClip(const SkIRect& clip) if (top < clip.fTop) { SkASSERT(fLastY >= clip.fTop); - fX += fDxDy * (clip.fTop - top); + fX += fDX * (clip.fTop - top); fFirstY = clip.fTop; } } /////////////////////////////////////////////////////////////////////////////// -/* This limits the number of lines we use to approximate a curve. - If we need to increase this, we need to store fSegmentCount in a larger data type. - TODO(kjlubick): now that this is in an unsigned byte, we could go up to 7 +/* We store 1<<shift in a (signed) byte, so its maximum value is 1<<6 == 64. + Note that this limits the number of lines we use to approximate a curve. + If we need to increase this, we need to store fCurveCount in something + larger than int8_t. */ #define MAX_COEFF_SHIFT 6 -// Approximate the distance from (0,0) to (dx, dy). -// When dx and dy are about the same -// sqrt(dx^2 + dy^2) => sqrt(2dx^2) => dx sqrt(2) = 1.41 * dx -// When dx >> dy -// sqrt(dx^2 + dy^2) => sqrt(dx^2) => dx -// So this is a reasonable approximation -static inline SkFDot6 cheap_distance(SkFDot6 dx, SkFDot6 dy) { +static inline SkFDot6 cheap_distance(SkFDot6 dx, SkFDot6 dy) +{ dx = SkAbs32(dx); dy = SkAbs32(dy); // return max + min/2 - if (dx > dy) { - return dx + (dy / 2); - } - return dy + (dx / 2); + if (dx > dy) + dx += dy >> 1; + else + dx = dy + (dx >> 1); + return dx; } -static inline int diff_to_shift(SkFDot6 dx, SkFDot6 dy, int accuracy) { +static inline int diff_to_shift(SkFDot6 dx, SkFDot6 dy, int shiftAA = 2) +{ // cheap calc of distance from center of p0-p2 to the center of the curve SkFDot6 dist = cheap_distance(dx, dy); @@ -241,69 +193,62 @@ static inline int diff_to_shift(SkFDot6 dx, SkFDot6 dy, int accuracy) { // ... but small enough so that our curves still look smooth // When shift > 0, we're using AA and everything is scaled up so we can // lower the accuracy. - // For cubics still, we have shift > 0. TODO(kjlubick) can we align cubics and quads? - dist = (dist + (1 << (2 + accuracy))) >> (3 + accuracy); + dist = (dist + (1 << (2 + shiftAA))) >> (3 + shiftAA); // each subdivision (shift value) cuts this dist (error) by 1/4 return (32 - SkCLZ(dist)) >> 1; } -bool SkQuadraticEdge::setQuadratic(const SkPoint pts[3]) { +bool SkQuadraticEdge::setQuadraticWithoutUpdate(const SkPoint pts[3], int shift) { SkFDot6 x0, y0, x1, y1, x2, y2; -#if defined(SK_RASTERIZE_EVEN_ROUNDING) - x0 = SkScalarRoundToFDot6(pts[0].fX, 0); - y0 = SkScalarRoundToFDot6(pts[0].fY, 0); - x1 = SkScalarRoundToFDot6(pts[1].fX, 0); - y1 = SkScalarRoundToFDot6(pts[1].fY, 0); - x2 = SkScalarRoundToFDot6(pts[2].fX, 0); - y2 = SkScalarRoundToFDot6(pts[2].fY, 0); + { +#ifdef SK_RASTERIZE_EVEN_ROUNDING + x0 = SkScalarRoundToFDot6(pts[0].fX, shift); + y0 = SkScalarRoundToFDot6(pts[0].fY, shift); + x1 = SkScalarRoundToFDot6(pts[1].fX, shift); + y1 = SkScalarRoundToFDot6(pts[1].fY, shift); + x2 = SkScalarRoundToFDot6(pts[2].fX, shift); + y2 = SkScalarRoundToFDot6(pts[2].fY, shift); #else - x0 = SkFloatToFDot6(pts[0].fX); - y0 = SkFloatToFDot6(pts[0].fY); - x1 = SkFloatToFDot6(pts[1].fX); - y1 = SkFloatToFDot6(pts[1].fY); - x2 = SkFloatToFDot6(pts[2].fX); - y2 = SkFloatToFDot6(pts[2].fY); + float scale = float(1 << (shift + 6)); + x0 = int(pts[0].fX * scale); + y0 = int(pts[0].fY * scale); + x1 = int(pts[1].fX * scale); + y1 = int(pts[1].fY * scale); + x2 = int(pts[2].fX * scale); + y2 = int(pts[2].fY * scale); #endif + } Winding winding = Winding::kCW; - if (y0 > y2) { - std::swap(x0, x2); - std::swap(y0, y2); + if (y0 > y2) + { + using std::swap; + swap(x0, x2); + swap(y0, y2); winding = Winding::kCCW; } - SkASSERTF(y0 <= y1 && y1 <= y2, "curve must be monotonic"); + SkASSERT(y0 <= y1 && y1 <= y2); - const int top = SkFDot6Round(y0); - const int bot = SkFDot6Round(y2); + int top = SkFDot6Round(y0); + int bot = SkFDot6Round(y2); // are we a zero-height quad (line)? - if (top == bot) { - return false; - } + if (top == bot) + return 0; - // compute number of steps needed (2^shift) based on the distance between - // this curve at the half-way point (t=0.5) and the midpoint of a straight - // line between p0 and p2. - // B(1/2) = p0 (1-t)^2 + 2 p1 t(1-t) + p2 t^2; t = 1/2 - // = p0 (1/2)^2 + 2 p1 (1/2)(1/2) + p2 (1/2)^2 - // = 1/4 (p0 + 2 p1 + p2) - // Midpoint of p0 and p2 is M(p0, p2) = (p2 + p0) / 2 - // Subtracting the two terms to get the vector representing the difference - // distance = B(1/2) - M(p0, p2) - // = 1/4 (p0 + 2 p1 + p2) - (p2 + p0) / 2 - // = 1/4 (p0 + 2 p1 + p2) - (2 p2 + 2 p0) / 4 - // = 1/4 (-p0 + 2 p1 - p2) - SkFDot6 deltaX = (2*x1 - x0 - x2) >> 2; - SkFDot6 deltaY = (2*y1 - y0 - y2) >> 2; - // We pass those points into this function which will find the total distance - // and use a heuristic to reduce the error to some threshold. - int shift = diff_to_shift(deltaX, deltaY, 0); - SkASSERT(shift >= 0); - - // We need at least 2 line segments for us to be able to save the derivatives as - // half their values to avoid overflow. + // compute number of steps needed (1 << shift) + { + SkFDot6 dx = (SkLeftShift(x1, 1) - x0 - x2) >> 2; + SkFDot6 dy = (SkLeftShift(y1, 1) - y0 - y2) >> 2; + // This is a little confusing: + // before this line, shift is the scale up factor for AA; + // after this line, shift is the fCurveShift. + shift = diff_to_shift(dx, dy, shift); + SkASSERT(shift >= 0); + } + // need at least 1 subdivision for our bias trick if (shift == 0) { shift = 1; } else if (shift > MAX_COEFF_SHIFT) { @@ -311,14 +256,15 @@ bool SkQuadraticEdge::setQuadratic(const SkPoint pts[3]) { } fWinding = winding; + //fCubicDShift only set for cubics fEdgeType = Type::kQuad; - fSegmentCount = SkToU8(1 << shift); + fCurveCount = SkToS8(1 << shift); /* - * By re-arranging the Bezier curve in polynomial form, it is easier to - * find the derivatives and forward-differentiate from one segment to the next. + * We want to reformulate into polynomial form, to make it clear how we + * should forward-difference. * - * p0 (1-t)^2 + 2 p1 t(1-t) + p2 t^2 ==> At^2 + Bt + C + * p0 (1 - t)^2 + p1 t(1 - t) + p2 t^2 ==> At^2 + Bt + C * * A = p0 - 2p1 + p2 * B = 2(p1 - p0) @@ -328,77 +274,75 @@ bool SkQuadraticEdge::setQuadratic(const SkPoint pts[3]) { * 16.16. However, as seen above, we sometimes compute values that can be * larger (e.g. B = 2*(p1 - p0)). To guard against overflow, we will store * A and B at 1/2 of their actual value, and just apply a 2x scale during - * application in nextSegment(). Hence we store (shift - 1) in + * application in updateQuadratic(). Hence we store (shift - 1) in * fCurveShift. */ fCurveShift = SkToU8(shift - 1); - // TODO(kjlubick): Can we use SkVx and calculate both X and Y at once? - - // The extra 1/2 factor avoids overflow - SkFixed A_half = SkFDot6ToFixedDiv2(x0 - x1 - x1 + x2); - SkFixed B_half = SkFDot6ToFixed(x1 - x0); - - // We want to calculate the slope at the midpoint of our first segment. This means evaluating - // dx/dt = 2A*t + B - // dx^2/dt^2 = 2A - // at t = 1/N * 1/2 - // There's an extra 1/2 on the whole expression to avoid overflows (as above). - // 1/2 ( 2A*t + B) => 1/2 (2A*1/2N + B) => A/2*1/N + B/2 => A/2 * 1/2^shift + B/2 - fQDxDt = B_half + (A_half >> shift); - // The second derivatives are constant, so we can pre-multiply them by 1/N to save having - // to do it in nextSegment(). Since A_half was already calculated we can use a smaller shift. - // 1/2 (2A * 1/N) => A * 1/N => A * 1/2^shift => A/2 * 1/2^(shift-1) - fQD2xDt2 = A_half >> (shift - 1); - - A_half = SkFDot6ToFixedDiv2(y0 - y1 - y1 + y2); - B_half = SkFDot6ToFixed(y1 - y0); - - fQDyDt = B_half + (A_half >> shift); - fQD2yDt2 = A_half >> (shift - 1); + + SkFixed A = SkFDot6ToFixedDiv2(x0 - x1 - x1 + x2); // 1/2 the real value + SkFixed B = SkFDot6ToFixed(x1 - x0); // 1/2 the real value fQx = SkFDot6ToFixed(x0); + fQDx = B + (A >> shift); // biased by shift + fQDDx = A >> (shift - 1); // biased by shift + + A = SkFDot6ToFixedDiv2(y0 - y1 - y1 + y2); // 1/2 the real value + B = SkFDot6ToFixed(y1 - y0); // 1/2 the real value + fQy = SkFDot6ToFixed(y0); + fQDy = B + (A >> shift); // biased by shift + fQDDy = A >> (shift - 1); // biased by shift + fQLastX = SkFDot6ToFixed(x2); fQLastY = SkFDot6ToFixed(y2); - return this->nextSegment(); + return true; +} + +int SkQuadraticEdge::setQuadratic(const SkPoint pts[3], int shift) { + if (!setQuadraticWithoutUpdate(pts, shift)) { + return 0; + } + return this->updateQuadratic(); } -bool SkQuadraticEdge::nextSegment() { - bool success; - int count = fSegmentCount; +int SkQuadraticEdge::updateQuadratic() +{ + int success; + int count = fCurveCount; SkFixed oldx = fQx; SkFixed oldy = fQy; - SkFixed dx = fQDxDt; - SkFixed dy = fQDyDt; + SkFixed dx = fQDx; + SkFixed dy = fQDy; SkFixed newx, newy; int shift = fCurveShift; SkASSERT(count > 0); do { - if (--count > 0) { - newx = oldx + (dx >> shift); - dx += fQD2xDt2; - newy = oldy + (dy >> shift); - dy += fQD2yDt2; + if (--count > 0) + { + newx = oldx + (dx >> shift); + dx += fQDDx; + newy = oldy + (dy >> shift); + dy += fQDDy; } else // last segment { - newx = fQLastX; - newy = fQLastY; + newx = fQLastX; + newy = fQLastY; } success = this->updateLine(oldx, oldy, newx, newy); oldx = newx; oldy = newy; } while (count > 0 && !success); - fQx = newx; - fQy = newy; - fQDxDt = dx; - fQDyDt = dy; - fSegmentCount = SkToU8(count); + fQx = newx; + fQy = newy; + fQDx = dx; + fQDy = dy; + fCurveCount = SkToS8(count); return success; } @@ -426,35 +370,40 @@ static SkFDot6 cubic_delta_from_line(SkFDot6 a, SkFDot6 b, SkFDot6 c, SkFDot6 d) return std::max(SkAbs32(oneThird), SkAbs32(twoThird)); } -bool SkCubicEdge::setCubic(const SkPoint pts[4]) { +bool SkCubicEdge::setCubicWithoutUpdate(const SkPoint pts[4], int shift, bool sortY) { SkFDot6 x0, y0, x1, y1, x2, y2, x3, y3; -#if defined(SK_RASTERIZE_EVEN_ROUNDING) - x0 = SkScalarRoundToFDot6(pts[0].fX, 0); - y0 = SkScalarRoundToFDot6(pts[0].fY, 0); - x1 = SkScalarRoundToFDot6(pts[1].fX, 0); - y1 = SkScalarRoundToFDot6(pts[1].fY, 0); - x2 = SkScalarRoundToFDot6(pts[2].fX, 0); - y2 = SkScalarRoundToFDot6(pts[2].fY, 0); - x3 = SkScalarRoundToFDot6(pts[3].fX, 0); - y3 = SkScalarRoundToFDot6(pts[3].fY, 0); + { +#ifdef SK_RASTERIZE_EVEN_ROUNDING + x0 = SkScalarRoundToFDot6(pts[0].fX, shift); + y0 = SkScalarRoundToFDot6(pts[0].fY, shift); + x1 = SkScalarRoundToFDot6(pts[1].fX, shift); + y1 = SkScalarRoundToFDot6(pts[1].fY, shift); + x2 = SkScalarRoundToFDot6(pts[2].fX, shift); + y2 = SkScalarRoundToFDot6(pts[2].fY, shift); + x3 = SkScalarRoundToFDot6(pts[3].fX, shift); + y3 = SkScalarRoundToFDot6(pts[3].fY, shift); #else - x0 = SkFloatToFDot6(pts[0].fX); - y0 = SkFloatToFDot6(pts[0].fY); - x1 = SkFloatToFDot6(pts[1].fX); - y1 = SkFloatToFDot6(pts[1].fY); - x2 = SkFloatToFDot6(pts[2].fX); - y2 = SkFloatToFDot6(pts[2].fY); - x3 = SkFloatToFDot6(pts[3].fX); - y3 = SkFloatToFDot6(pts[3].fY); + float scale = float(1 << (shift + 6)); + x0 = int(pts[0].fX * scale); + y0 = int(pts[0].fY * scale); + x1 = int(pts[1].fX * scale); + y1 = int(pts[1].fY * scale); + x2 = int(pts[2].fX * scale); + y2 = int(pts[2].fY * scale); + x3 = int(pts[3].fX * scale); + y3 = int(pts[3].fY * scale); #endif + } Winding winding = Winding::kCW; - if (y0 > y3) { - std::swap(x0, x3); - std::swap(x1, x2); - std::swap(y0, y3); - std::swap(y1, y2); + if (sortY && y0 > y3) + { + using std::swap; + swap(x0, x3); + swap(x1, x2); + swap(y0, y3); + swap(y1, y2); winding = Winding::kCCW; } @@ -462,18 +411,19 @@ bool SkCubicEdge::setCubic(const SkPoint pts[4]) { int bot = SkFDot6Round(y3); // are we a zero-height cubic (line)? - if (top == bot) { - return false; - } + if (sortY && top == bot) + return 0; // compute number of steps needed (1 << shift) - // Can't use (center of curve - center of baseline), since center-of-curve - // need not be the max delta from the baseline (it could even be coincident) - // so we try just looking at the two off-curve points - SkFDot6 dx = cubic_delta_from_line(x0, x1, x2, x3); - SkFDot6 dy = cubic_delta_from_line(y0, y1, y2, y3); - // add 1 (by observation) - int shift = diff_to_shift(dx, dy, 2) + 1; + { + // Can't use (center of curve - center of baseline), since center-of-curve + // need not be the max delta from the baseline (it could even be coincident) + // so we try just looking at the two off-curve points + SkFDot6 dx = cubic_delta_from_line(x0, x1, x2, x3); + SkFDot6 dy = cubic_delta_from_line(y0, y1, y2, y3); + // add 1 (by observation) + shift = diff_to_shift(dx, dy) + 1; + } // need at least 1 subdivision for our bias trick SkASSERT(shift > 0); if (shift > MAX_COEFF_SHIFT) { @@ -493,83 +443,69 @@ bool SkCubicEdge::setCubic(const SkPoint pts[4]) { fWinding = winding; fEdgeType = Type::kCubic; - fSegmentCount = SkToU8(SkLeftShift(1, shift)); + fCurveCount = SkToS8(SkLeftShift(-1, shift)); fCurveShift = SkToU8(shift); fCubicDShift = SkToU8(downShift); - /* - * By re-arranging the Bezier curve in polynomial form, it is easier to - * find the derivatives and forward-differentiate from one segment to the next. - * - * p0 (1-t)^3 + 3 p1 t(1-t)^2 + 3 p2 t^2 (1-t) + p3 t^3 ==> At^3 + Bt^2 + Ct + D - * Where A = -p0 + 3p1 + -3p2 + p3 - * B = 3p0 - 6p1 + 3p2 - * C = -3p0 + 3p1 - * D = p0 - */ - // TODO(kjlubick): Can we use SkVx and calculate both X and Y at once? - - SkFixed A = SkFDot6UpShift(x3 + 3 * (x1 - x2) - x0, upShift); - SkFixed B = SkFDot6UpShift(3 * (x0 - 2*x1 + x2), upShift); - SkFixed C = SkFDot6UpShift(3 * (x1 - x0), upShift); - - // We want to calculate the slope at the midpoint of our first segment. This means evaluating - // dx/dt = 3A*t^2 + 2B*t + C - // dx^2/dt^2 = 6A * t + 2B - // dx^3/dt^3 = 6A - // at t = 1/N * 1/2 - // TODO(kjlubick): I'm not sure these cubic approximations are being done correctly. Some - // coefficients seem to be missing. Maybe it's being done not at the midpoint - // of the first line but just...somewhere in there? - fCDxDt = (A >> 2*shift) + (B >> shift) + C; - fCD2xDt2 = (3*A >> (shift - 1)) + 2*B; - - // This may be attempting to precompute the third derivative times 1/N - // 6A / N => 6A / 2^shift => 3A / 2^(shift-1) - fCD3xDt3 = 3*A >> (shift - 1); - - A = SkFDot6UpShift(y3 + 3 * (y1 - y2) - y0, upShift); - B = SkFDot6UpShift(3 * (y0 - 2*y1 + y2), upShift); - C = SkFDot6UpShift(3 * (y1 - y0), upShift); - - fCDyDt = (A >> 2*shift) + (B >> shift) + C; - fCD2yDt2 = (3*A >> (shift - 1)) + 2*B; - fCD3yDt3 = 3*A >> (shift - 1); - - fCx = SkFDot6ToFixed(x0); - fCy = SkFDot6ToFixed(y0); + SkFixed B = SkFDot6UpShift(3 * (x1 - x0), upShift); + SkFixed C = SkFDot6UpShift(3 * (x0 - x1 - x1 + x2), upShift); + SkFixed D = SkFDot6UpShift(x3 + 3 * (x1 - x2) - x0, upShift); + + fCx = SkFDot6ToFixed(x0); + fCDx = B + (C >> shift) + (D >> 2*shift); // biased by shift + fCDDx = 2*C + (3*D >> (shift - 1)); // biased by 2*shift + fCDDDx = 3*D >> (shift - 1); // biased by 2*shift + + B = SkFDot6UpShift(3 * (y1 - y0), upShift); + C = SkFDot6UpShift(3 * (y0 - y1 - y1 + y2), upShift); + D = SkFDot6UpShift(y3 + 3 * (y1 - y2) - y0, upShift); + + fCy = SkFDot6ToFixed(y0); + fCDy = B + (C >> shift) + (D >> 2*shift); // biased by shift + fCDDy = 2*C + (3*D >> (shift - 1)); // biased by 2*shift + fCDDDy = 3*D >> (shift - 1); // biased by 2*shift + fCLastX = SkFDot6ToFixed(x3); fCLastY = SkFDot6ToFixed(y3); - return this->nextSegment(); + return true; +} + +int SkCubicEdge::setCubic(const SkPoint pts[4], int shift) { + if (!this->setCubicWithoutUpdate(pts, shift)) { + return 0; + } + return this->updateCubic(); } -bool SkCubicEdge::nextSegment() { - bool success; - int count = fSegmentCount; +int SkCubicEdge::updateCubic() +{ + int success; + int count = fCurveCount; SkFixed oldx = fCx; SkFixed oldy = fCy; SkFixed newx, newy; const int ddshift = fCurveShift; const int dshift = fCubicDShift; - SkASSERT(count > 0); + SkASSERT(count < 0); do { - if (--count > 0) + if (++count < 0) { - newx = oldx + (fCDxDt >> dshift); - fCDxDt += fCD2xDt2 >> ddshift; - fCD2xDt2 += fCD3xDt3; + newx = oldx + (fCDx >> dshift); + fCDx += fCDDx >> ddshift; + fCDDx += fCDDDx; - newy = oldy + (fCDyDt >> dshift); - fCDyDt += fCD2yDt2 >> ddshift; - fCD2yDt2 += fCD3yDt3; + newy = oldy + (fCDy >> dshift); + fCDy += fCDDy >> ddshift; + fCDDy += fCDDDy; } else // last segment { - newx = fCLastX; - newy = fCLastY; + // SkDebugf("LastX err=%d, LastY err=%d\n", (oldx + (fCDx >> shift) - fLastX), (oldy + (fCDy >> shift) - fLastY)); + newx = fCLastX; + newy = fCLastY; } // we want to say SkASSERT(oldy <= newy), but our finite fixedpoint @@ -581,10 +517,10 @@ bool SkCubicEdge::nextSegment() { success = this->updateLine(oldx, oldy, newx, newy); oldx = newx; oldy = newy; - } while (count > 0 && !success); + } while (count < 0 && !success); - fCx = newx; - fCy = newy; - fSegmentCount = SkToU8(count); + fCx = newx; + fCy = newy; + fCurveCount = SkToS8(count); return success; } diff --git a/gfx/skia/skia/src/core/SkEdge.h b/gfx/skia/skia/src/core/SkEdge.h @@ -9,6 +9,7 @@ #define SkEdge_DEFINED #include "include/core/SkPoint.h" +#include "include/core/SkRect.h" #include "include/private/base/SkAssert.h" #include "include/private/base/SkDebug.h" #include "include/private/base/SkFixed.h" @@ -18,27 +19,10 @@ #include <cstdint> #include <utility> -struct SkIRect; - // This correctly favors the lower-pixel when y0 is on a 1/2 pixel boundary #define SkEdge_Compute_DY(top, y0) (SkLeftShift(top, 6) + 32 - (y0)) -/** - SkEdge approximates monotonic curves with one or more line segments in a way that makes - computing scan lines (rows of horizontal pixels between multiple edges) efficient and easier - to do. In particular, the line segments will be in terms of Y instead of a general Bezier curve - which is in terms of t. - - The number of segments will be a power of 2 (for easy division) based on internal heuristics - about how bendy the curve is. - - The general flow is to create an SkEdge (or one of its subclasses), use the fields to represent - a line segment, and then use updateQuadratic or updateCubic to update the fields with the next - line segment's values. -*/ -class SkEdge { -public: - virtual ~SkEdge() = default; +struct SkEdge { enum class Type : int8_t { kLine, kQuad, @@ -49,57 +33,32 @@ public: kCCW = -1, // counter clockwise }; - // Can be used to join edges together SkEdge* fNext; SkEdge* fPrev; - // The current line segment starts at (fX, fFirstY + 0.5). It has slope - // fDxDy (yes, run over rise, because this is geared toward horizontal scanlines) - // and stops once Y gets to fLastY + 0.5. SkFixed fX; - SkFixed fDxDy; - - // These are integers because they represent a discrete pixel. Mathematically, these are - // treated as half way inside the pixel, so 6 -> 6.5. + SkFixed fDX; int32_t fFirstY; int32_t fLastY; - 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 Winding fWinding; - // Represent a straight line with an optional clip. This will always be a single segment. - // Returns false if the line has height 0. - bool setLine(const SkPoint& p0, const SkPoint& p1, const SkIRect* clip); + 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 - inline bool setLine(const SkPoint& p0, const SkPoint& p1); + inline int setLine(const SkPoint& p0, const SkPoint& p1, int shiftUp); + inline int updateLine(SkFixed ax, SkFixed ay, SkFixed bx, SkFixed by); + void chopLineWithClip(const SkIRect& clip); - bool hasNextSegment() const { - return fSegmentCount != 0; + inline bool intersectsClip(const SkIRect& clip) const { + SkASSERT(fFirstY < clip.fBottom); + return fLastY >= clip.fTop; } - // Update fX, fDxDY, fFirstY, and fLastY to represent the segment. It will skip over - // any lines that have a height of 0 pixels and return false if there were only 0-height - // segments remaining. For quadratic and cubic curves this will involve forward-differencing - // (see the subclasses for those values). - virtual bool nextSegment(); - - uint8_t segmentsLeft() const { - return fSegmentCount; - } - -protected: - inline bool updateLine(SkFixed ax, SkFixed ay, SkFixed bx, SkFixed by); - - uint8_t fSegmentCount; // only non-zero for Quad and Cubics - // How much to shift the derivatives to multiply by deltaT when doing forward-differencing. - // For cubics, this is log_2(N) and for quadratics this is log_2(N) - 1. - uint8_t fCurveShift; - -private: - void chopLineWithClip(const SkIRect& clip); -#if defined(SK_DEBUG) -public: - virtual void dump() const; +#ifdef SK_DEBUG + void dump() const; void validate() const { SkASSERT(fPrev && fNext); SkASSERT(fPrev->fNext == this); @@ -111,74 +70,46 @@ public: #endif }; -class SkQuadraticEdge final : public SkEdge { -public: - // Sets up the line segments. Returns false if the line would be of height 0. - bool setQuadratic(const SkPoint pts[3]); - bool nextSegment() override; - -private: - // These are the non-rounded points that the current line segment ends at. +struct SkQuadraticEdge : public SkEdge { SkFixed fQx, fQy; - // These represent the first derivatives of the quadratic curve evaluated - // at the midpoint of the next line segment. To avoid overflows, we store them as half - // their normal value. During the forward-difference step, instead of multiplying by - // a deltaT of 1/N, we'll multiply by 2/N instead. - SkFixed fQDxDt, fQDyDt; - // These are the second derivatives of the quadratic curve pre-multiplied by 1/N. - SkFixed fQD2xDt2, fQD2yDt2; - - // The non-rounded end points for the entire curve. On the last segment, these - // will be used instead of the results from our forward-differnce technique - // to make sure cumulative error doesn't result in a dramatically different line. + SkFixed fQDx, fQDy; + SkFixed fQDDx, fQDDy; SkFixed fQLastX, fQLastY; -#if defined(SK_DEBUG) -public: - void dump() const override; -#endif + bool setQuadraticWithoutUpdate(const SkPoint pts[3], int shiftUp); + int setQuadratic(const SkPoint pts[3], int shiftUp); + int updateQuadratic(); }; -class SkCubicEdge final : public SkEdge { -public: - // Sets up the line segments. Returns false if the line would be of height 0. - bool setCubic(const SkPoint pts[4]); - bool nextSegment() override; - -private: - // These are the non-rounded points that the current line segment ends at. +struct SkCubicEdge : public SkEdge { SkFixed fCx, fCy; - SkFixed fCDxDt, fCDyDt; - SkFixed fCD2xDt2, fCD2yDt2; - SkFixed fCD3xDt3, fCD3yDt3; - - // The non-rounded end points for the entire curve. On the last segment, these - // will be used instead of the results from our forward-difference technique - // to make sure cumulative error doesn't result in a dramatically different line. + SkFixed fCDx, fCDy; + SkFixed fCDDx, fCDDy; + SkFixed fCDDDx, fCDDDy; SkFixed fCLastX, fCLastY; - uint8_t fCubicDShift; // applied to fCDxDt and fCDyDt only in cubic - -#if defined(SK_DEBUG) -public: - void dump() const override; -#endif + bool setCubicWithoutUpdate(const SkPoint pts[4], int shiftUp, bool sortY = true); + int setCubic(const SkPoint pts[4], int shiftUp); + int updateCubic(); }; -bool SkEdge::setLine(const SkPoint& p0, const SkPoint& p1) { +int SkEdge::setLine(const SkPoint& p0, const SkPoint& p1, int shift) { SkFDot6 x0, y0, x1, y1; -#if defined(SK_RASTERIZE_EVEN_ROUNDING) - x0 = SkScalarRoundToFDot6(p0.fX, 0); - y0 = SkScalarRoundToFDot6(p0.fY, 0); - x1 = SkScalarRoundToFDot6(p1.fX, 0); - y1 = SkScalarRoundToFDot6(p1.fY, 0); + { +#ifdef SK_RASTERIZE_EVEN_ROUNDING + x0 = SkScalarRoundToFDot6(p0.fX, shift); + y0 = SkScalarRoundToFDot6(p0.fY, shift); + x1 = SkScalarRoundToFDot6(p1.fX, shift); + y1 = SkScalarRoundToFDot6(p1.fY, shift); #else - x0 = SkFloatToFDot6(p0.fX); - y0 = SkFloatToFDot6(p0.fY); - x1 = SkFloatToFDot6(p1.fX); - y1 = SkFloatToFDot6(p1.fY); + float scale = float(1 << (shift + 6)); + x0 = int(p0.fX * scale); + y0 = int(p0.fY * scale); + x1 = int(p1.fX * scale); + y1 = int(p1.fY * scale); #endif + } Winding winding = Winding::kCW; @@ -194,22 +125,21 @@ bool SkEdge::setLine(const SkPoint& p0, const SkPoint& p1) { // are we a zero-height line? if (top == bot) { - return false; + return 0; } SkFixed slope = SkFDot6Div(x1 - x0, y1 - y0); const SkFDot6 dy = SkEdge_Compute_DY(top, y0); - // Note that SkFixedMul(SkFixed, SkFDot6) produces results in SkFDot6 - fX = SkFDot6ToFixed(x0 + SkFixedMul(slope, dy)); - fDxDy = slope; + fX = SkFDot6ToFixed(x0 + SkFixedMul(slope, dy)); // + SK_Fixed1/2 + fDX = slope; fFirstY = top; fLastY = bot - 1; fEdgeType = Type::kLine; - fSegmentCount = 0; + fCurveCount = 0; fWinding = winding; fCurveShift = 0; - return true; + return 1; } #endif diff --git a/gfx/skia/skia/src/core/SkEdgeBuilder.cpp b/gfx/skia/skia/src/core/SkEdgeBuilder.cpp @@ -9,11 +9,13 @@ #include "include/core/SkPath.h" #include "include/core/SkPoint.h" +#include "include/core/SkScalar.h" #include "include/core/SkTypes.h" #include "include/private/base/SkDebug.h" #include "include/private/base/SkFixed.h" #include "include/private/base/SkFloatingPoint.h" #include "include/private/base/SkSafe32.h" +#include "include/private/base/SkTo.h" #include "src/base/SkSafeMath.h" #include "src/core/SkAnalyticEdge.h" #include "src/core/SkEdge.h" @@ -25,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::Type::kLine || last->fDxDy || edge->fX != last->fX) { + if (last->fEdgeType != SkEdge::Type::kLine || last->fDX || edge->fX != last->fX) { return kNo_Combine; } if (edge->fWinding == last->fWinding) { @@ -117,18 +119,12 @@ SkEdgeBuilder::Combine SkAnalyticEdgeBuilder::combineVertical(const SkAnalyticEd return kNo_Combine; } -static bool is_vertical(const SkEdge* edge) { - // We only consider edges that were originally lines to be vertical to avoid numerical issues - // (crbug.com/1154864). - return edge->fDxDy == 0 - && edge->fEdgeType == SkEdge::Type::kLine; -} - -static bool is_vertical(const SkAnalyticEdge* edge) { +template <typename Edge> +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 == SkAnalyticEdge::Type::kLine; + && edge->fEdgeType == Edge::Type::kLine; } // TODO: we can deallocate the edge if edge->setFoo() fails @@ -136,7 +132,7 @@ static bool is_vertical(const SkAnalyticEdge* edge) { void SkBasicEdgeBuilder::addLine(const SkPoint pts[]) { SkEdge* edge = fAlloc.make<SkEdge>(); - if (edge->setLine(pts[0], pts[1])) { + if (edge->setLine(pts[0], pts[1], fClipShift)) { Combine combine = is_vertical(edge) && !fList.empty() ? this->combineVertical(edge, (SkEdge*)fList.back()) : kNo_Combine; @@ -165,7 +161,7 @@ void SkAnalyticEdgeBuilder::addLine(const SkPoint pts[]) { } void SkBasicEdgeBuilder::addQuad(const SkPoint pts[]) { SkQuadraticEdge* edge = fAlloc.make<SkQuadraticEdge>(); - if (edge->setQuadratic(pts)) { + if (edge->setQuadratic(pts, fClipShift)) { fList.push_back(edge); } } @@ -178,7 +174,7 @@ void SkAnalyticEdgeBuilder::addQuad(const SkPoint pts[]) { void SkBasicEdgeBuilder::addCubic(const SkPoint pts[]) { SkCubicEdge* edge = fAlloc.make<SkCubicEdge>(); - if (edge->setCubic(pts)) { + if (edge->setCubic(pts, fClipShift)) { fList.push_back(edge); } } @@ -191,6 +187,18 @@ void SkAnalyticEdgeBuilder::addCubic(const SkPoint pts[]) { // TODO: merge addLine() and addPolyLine()? +SkEdgeBuilder::Combine SkBasicEdgeBuilder::addPolyLine(const SkPoint pts[], + char* arg_edge, char** arg_edgePtr) { + auto edge = (SkEdge*) arg_edge; + auto edgePtr = (SkEdge**)arg_edgePtr; + + if (edge->setLine(pts[0], pts[1], fClipShift)) { + return is_vertical(edge) && edgePtr > (SkEdge**)fEdgeList + ? this->combineVertical(edge, edgePtr[-1]) + : kNo_Combine; + } + return SkEdgeBuilder::kPartial_Combine; // A convenient lie. Same do-nothing behavior. +} SkEdgeBuilder::Combine SkAnalyticEdgeBuilder::addPolyLine(const SkPoint pts[], char* arg_edge, char** arg_edgePtr) { auto edge = (SkAnalyticEdge*) arg_edge; @@ -205,20 +213,27 @@ SkEdgeBuilder::Combine SkAnalyticEdgeBuilder::addPolyLine(const SkPoint pts[], } SkRect SkBasicEdgeBuilder::recoverClip(const SkIRect& src) const { - return SkRect::Make(src); + return { SkIntToScalar(src.fLeft >> fClipShift), + SkIntToScalar(src.fTop >> fClipShift), + SkIntToScalar(src.fRight >> fClipShift), + SkIntToScalar(src.fBottom >> fClipShift), }; } SkRect SkAnalyticEdgeBuilder::recoverClip(const SkIRect& src) const { return SkRect::Make(src); } +char* SkBasicEdgeBuilder::allocEdges(size_t n, size_t* size) { + *size = sizeof(SkEdge); + return (char*)fAlloc.makeArrayDefault<SkEdge>(n); +} char* SkAnalyticEdgeBuilder::allocEdges(size_t n, size_t* size) { *size = sizeof(SkAnalyticEdge); return (char*)fAlloc.makeArrayDefault<SkAnalyticEdge>(n); } // TODO: maybe get rid of buildPoly() entirely? -int SkEdgeBuilder::buildPoly(const SkPathRaw& raw, const SkIRect* iclip, bool canCullToTheRight) { - size_t maxEdgeCount = raw.fPoints.size(); +int SkEdgeBuilder::buildPoly(const SkPath& path, const SkIRect* iclip, bool canCullToTheRight) { + size_t maxEdgeCount = path.countPoints(); if (iclip) { // clipping can turn 1 line into (up to) kMaxClippedLineSegments, since // we turn portions that are clipped out on the left/right into vertical @@ -230,7 +245,14 @@ int SkEdgeBuilder::buildPoly(const SkPathRaw& raw, const SkIRect* iclip, bool ca } } - SkPathEdgeIter iter(raw); + size_t edgeSize; + char* edge = this->allocEdges(maxEdgeCount, &edgeSize); + + SkDEBUGCODE(char* edgeStart = edge); + char** edgePtr = fAlloc.makeArrayDefault<char*>(maxEdgeCount); + fEdgeList = (void**)edgePtr; + + SkPathEdgeIter iter(path); if (iclip) { SkRect clip = this->recoverClip(*iclip); @@ -241,7 +263,12 @@ int SkEdgeBuilder::buildPoly(const SkPathRaw& raw, const SkIRect* iclip, bool ca int lineCount = SkLineClipper::ClipLine(e.fPts, clip, lines, canCullToTheRight); SkASSERT(lineCount <= SkLineClipper::kMaxClippedLineSegments); for (int i = 0; i < lineCount; i++) { - this->addLine(lines + i); + switch( this->addPolyLine(lines + i, edge, edgePtr) ) { + case kTotal_Combine: edgePtr--; break; + case kPartial_Combine: break; + case kNo_Combine: *edgePtr++ = edge; + edge += edgeSize; + } } break; } @@ -254,7 +281,12 @@ int SkEdgeBuilder::buildPoly(const SkPathRaw& raw, const SkIRect* iclip, bool ca while (auto e = iter.next()) { switch (e.fEdge) { case SkPathEdgeIter::Edge::kLine: { - this->addLine(e.fPts); + switch( this->addPolyLine(e.fPts, edge, edgePtr) ) { + case kTotal_Combine: edgePtr--; break; + case kPartial_Combine: break; + case kNo_Combine: *edgePtr++ = edge; + edge += edgeSize; + } break; } default: @@ -263,11 +295,13 @@ int SkEdgeBuilder::buildPoly(const SkPathRaw& raw, const SkIRect* iclip, bool ca } } } - fEdgeList = fList.begin(); - return fList.size(); + SkASSERT((size_t)(edge - edgeStart) <= maxEdgeCount * edgeSize); + SkASSERT((size_t)(edgePtr - (char**)fEdgeList) <= maxEdgeCount); + return SkToInt(edgePtr - (char**)fEdgeList); } -int SkEdgeBuilder::build(const SkPathRaw& raw, const SkIRect* iclip, bool canCullToTheRight) { +int SkEdgeBuilder::build(const SkPath& path, const SkIRect* iclip, bool canCullToTheRight) { + SkPathEdgeIter iter(path); if (iclip) { SkRect clip = this->recoverClip(*iclip); struct Rec { @@ -275,21 +309,22 @@ int SkEdgeBuilder::build(const SkPathRaw& raw, const SkIRect* iclip, bool canCul bool fIsFinite; } rec = { this, true }; - SkEdgeClipper::ClipPath(raw, clip, canCullToTheRight, + SkEdgeClipper::ClipPath(path, clip, canCullToTheRight, [](SkEdgeClipper* clipper, bool, void* ctx) { Rec* rec = (Rec*)ctx; SkPoint pts[4]; + SkPath::Verb verb; - while (auto verb = clipper->next(pts)) { - const int count = SkPathPriv::PtsInIter(*verb); + while ((verb = clipper->next(pts)) != SkPath::kDone_Verb) { + const int count = SkPathPriv::PtsInIter(verb); if (!SkIsFinite(&pts[0].fX, count*2)) { rec->fIsFinite = false; return; } - switch (*verb) { - case SkPathVerb::kLine: rec->fBuilder->addLine (pts); break; - case SkPathVerb::kQuad: rec->fBuilder->addQuad (pts); break; - case SkPathVerb::kCubic: rec->fBuilder->addCubic(pts); break; + switch (verb) { + case SkPath::kLine_Verb: rec->fBuilder->addLine (pts); break; + case SkPath::kQuad_Verb: rec->fBuilder->addQuad (pts); break; + case SkPath::kCubic_Verb: rec->fBuilder->addCubic(pts); break; default: break; } } @@ -298,7 +333,6 @@ int SkEdgeBuilder::build(const SkPathRaw& raw, const SkIRect* iclip, bool canCul return rec.fIsFinite ? fList.size() : 0; } - SkPathEdgeIter iter(raw); SkAutoConicToQuads quadder; constexpr float kConicTol = 0.25f; SkPoint monoY[10]; @@ -334,25 +368,22 @@ int SkEdgeBuilder::build(const SkPathRaw& raw, const SkIRect* iclip, bool canCul } break; } - default: - SkDEBUGFAIL("Unknown edge type"); - break; } } fEdgeList = fList.begin(); return fList.size(); } -int SkEdgeBuilder::buildEdges(const SkPathRaw& raw, +int SkEdgeBuilder::buildEdges(const SkPath& path, const SkIRect* shiftedClip) { // If we're convex, then we need both edges, even if the right edge is past the clip. - const bool canCullToTheRight = !raw.isConvex(); + const bool canCullToTheRight = !path.isConvex(); // We can use our buildPoly() optimization if all the segments are lines. // (Edges are homogeneous and stored contiguously in memory, no need for indirection.) - const int count = SkPath::kLine_SegmentMask == raw.segmentMasks() - ? this->buildPoly(raw, shiftedClip, canCullToTheRight) - : this->build (raw, shiftedClip, canCullToTheRight); + const int count = SkPath::kLine_SegmentMask == path.getSegmentMasks() + ? this->buildPoly(path, shiftedClip, canCullToTheRight) + : this->build (path, shiftedClip, canCullToTheRight); SkASSERT(count >= 0); @@ -362,7 +393,3 @@ int SkEdgeBuilder::buildEdges(const SkPathRaw& raw, } return count; } - -int SkEdgeBuilder::buildEdges(const SkPath& path, const SkIRect* shiftedClip) { - return buildEdges(SkPathPriv::Raw(path), shiftedClip); -} diff --git a/gfx/skia/skia/src/core/SkEdgeBuilder.h b/gfx/skia/skia/src/core/SkEdgeBuilder.h @@ -7,23 +7,21 @@ #ifndef SkEdgeBuilder_DEFINED #define SkEdgeBuilder_DEFINED -#include "include/core/SkPoint.h" #include "include/core/SkRect.h" -#include "include/private/base/SkAssert.h" #include "include/private/base/SkTDArray.h" #include "src/base/SkArenaAlloc.h" #include <cstddef> -class SkEdge; class SkPath; -struct SkPathRaw; struct SkAnalyticEdge; +struct SkEdge; +struct SkPoint; class SkEdgeBuilder { public: - int buildEdges(const SkPathRaw&, const SkIRect* shiftedClip); - int buildEdges(const SkPath&, const SkIRect* shiftedClip); + int buildEdges(const SkPath& path, + const SkIRect* shiftedClip); protected: SkEdgeBuilder() = default; @@ -42,8 +40,8 @@ protected: }; private: - int build (const SkPathRaw&, const SkIRect* clip, bool clipToTheRight); - int buildPoly(const SkPathRaw&, const SkIRect* clip, bool clipToTheRight); + int build (const SkPath& path, const SkIRect* clip, bool clipToTheRight); + int buildPoly(const SkPath& path, const SkIRect* clip, bool clipToTheRight); virtual char* allocEdges(size_t n, size_t* sizeof_edge) = 0; virtual SkRect recoverClip(const SkIRect&) const = 0; @@ -56,27 +54,22 @@ private: class SkBasicEdgeBuilder final : public SkEdgeBuilder { public: - explicit SkBasicEdgeBuilder() {} + explicit SkBasicEdgeBuilder(int clipShift) : fClipShift(clipShift) {} SkEdge** edgeList() { return (SkEdge**)fEdgeList; } private: Combine combineVertical(const SkEdge* edge, SkEdge* last); - char* allocEdges(size_t, size_t*) override { - SkDEBUGFAIL("Not implemented"); - return nullptr; - } - + char* allocEdges(size_t, size_t*) override; SkRect recoverClip(const SkIRect&) const override; void addLine (const SkPoint pts[]) override; void addQuad (const SkPoint pts[]) override; void addCubic(const SkPoint pts[]) override; - Combine addPolyLine(const SkPoint pts[], char* edge, char** edgePtr) override { - SkDEBUGFAIL("Not implemented"); - return kNo_Combine; - } + Combine addPolyLine(const SkPoint pts[], char* edge, char** edgePtr) override; + + const int fClipShift; }; class SkAnalyticEdgeBuilder final : public SkEdgeBuilder { diff --git a/gfx/skia/skia/src/core/SkEdgeClipper.cpp b/gfx/skia/skia/src/core/SkEdgeClipper.cpp @@ -61,10 +61,10 @@ bool SkEdgeClipper::clipLine(SkPoint p0, SkPoint p1, const SkRect& clip) { this->appendLine(lines[i], lines[i + 1]); } - fCurrVerbStop = fCurrVerb; + *fCurrVerb = SkPath::kDone_Verb; fCurrPoint = fPoints; fCurrVerb = fVerbs; - return fCurrVerbStop != fCurrVerb; + return SkPath::kDone_Verb != fVerbs[0]; } /////////////////////////////////////////////////////////////////////////////// @@ -227,7 +227,8 @@ bool SkEdgeClipper::clipQuad(const SkPoint srcPts[3], const SkRect& clip) { fCurrPoint = fPoints; fCurrVerb = fVerbs; - const SkRect bounds = SkRect::BoundsOrEmpty({srcPts, 3}); + SkRect bounds; + bounds.setBounds(srcPts, 3); if (!quick_reject(bounds, clip)) { SkPoint monoY[5]; @@ -243,10 +244,10 @@ bool SkEdgeClipper::clipQuad(const SkPoint srcPts[3], const SkRect& clip) { } } - fCurrVerbStop = fCurrVerb; + *fCurrVerb = SkPath::kDone_Verb; fCurrPoint = fPoints; fCurrVerb = fVerbs; - return fCurrVerbStop != fCurrVerb; + return SkPath::kDone_Verb != fVerbs[0]; } /////////////////////////////////////////////////////////////////////////////// @@ -402,7 +403,9 @@ void SkEdgeClipper::clipMonoCubic(const SkPoint src[4], const SkRect& clip) { } static SkRect compute_cubic_bounds(const SkPoint pts[4]) { - return SkRect::BoundsOrEmpty({pts, 4}); + SkRect r; + r.setBounds(pts, 4); + return r; } static bool too_big_for_reliable_float_math(const SkRect& r) { @@ -446,23 +449,23 @@ bool SkEdgeClipper::clipCubic(const SkPoint srcPts[4], const SkRect& clip) { } } - fCurrVerbStop = fCurrVerb; + *fCurrVerb = SkPath::kDone_Verb; fCurrPoint = fPoints; fCurrVerb = fVerbs; - return fCurrVerbStop != fCurrVerb; + return SkPath::kDone_Verb != fVerbs[0]; } /////////////////////////////////////////////////////////////////////////////// void SkEdgeClipper::appendLine(SkPoint p0, SkPoint p1) { - *fCurrVerb++ = SkPathVerb::kLine; + *fCurrVerb++ = SkPath::kLine_Verb; fCurrPoint[0] = p0; fCurrPoint[1] = p1; fCurrPoint += 2; } void SkEdgeClipper::appendVLine(SkScalar x, SkScalar y0, SkScalar y1, bool reverse) { - *fCurrVerb++ = SkPathVerb::kLine; + *fCurrVerb++ = SkPath::kLine_Verb; if (reverse) { using std::swap; @@ -474,7 +477,7 @@ void SkEdgeClipper::appendVLine(SkScalar x, SkScalar y0, SkScalar y1, bool rever } void SkEdgeClipper::appendQuad(const SkPoint pts[3], bool reverse) { - *fCurrVerb++ = SkPathVerb::kQuad; + *fCurrVerb++ = SkPath::kQuad_Verb; if (reverse) { fCurrPoint[0] = pts[2]; @@ -488,7 +491,7 @@ void SkEdgeClipper::appendQuad(const SkPoint pts[3], bool reverse) { } void SkEdgeClipper::appendCubic(const SkPoint pts[4], bool reverse) { - *fCurrVerb++ = SkPathVerb::kCubic; + *fCurrVerb++ = SkPath::kCubic_Verb; if (reverse) { for (int i = 0; i < 4; i++) { @@ -500,25 +503,26 @@ void SkEdgeClipper::appendCubic(const SkPoint pts[4], bool reverse) { fCurrPoint += 4; } -std::optional<SkPathVerb> SkEdgeClipper::next(SkPoint pts[]) { - SkASSERT(fCurrVerb <= fCurrVerbStop); - if (fCurrVerb >= fCurrVerbStop) { - return {}; - } +SkPath::Verb SkEdgeClipper::next(SkPoint pts[]) { + SkPath::Verb verb = *fCurrVerb; - auto verb = *fCurrVerb++; switch (verb) { - case SkPathVerb::kLine: + case SkPath::kLine_Verb: memcpy(pts, fCurrPoint, 2 * sizeof(SkPoint)); fCurrPoint += 2; + fCurrVerb += 1; break; - case SkPathVerb::kQuad: + case SkPath::kQuad_Verb: memcpy(pts, fCurrPoint, 3 * sizeof(SkPoint)); fCurrPoint += 3; + fCurrVerb += 1; break; - case SkPathVerb::kCubic: + case SkPath::kCubic_Verb: memcpy(pts, fCurrPoint, 4 * sizeof(SkPoint)); fCurrPoint += 4; + fCurrVerb += 1; + break; + case SkPath::kDone_Verb: break; default: SkDEBUGFAIL("unexpected verb in quadclippper2 iter"); @@ -559,12 +563,14 @@ void sk_assert_monotonic_x(const SkPoint pts[], int count) { } #endif -void SkEdgeClipper::ClipPath(const SkPathRaw& raw, const SkRect& clip, bool canCullToTheRight, +void SkEdgeClipper::ClipPath(const SkPath& path, const SkRect& clip, bool canCullToTheRight, void (*consume)(SkEdgeClipper*, bool newCtr, void* ctx), void* ctx) { + SkASSERT(path.isFinite()); + SkAutoConicToQuads quadder; - constexpr float kConicTol = 0.25f; + const SkScalar conicTol = SK_Scalar1 / 4; - SkPathEdgeIter iter(raw); + SkPathEdgeIter iter(path); SkEdgeClipper clipper(canCullToTheRight); while (auto e = iter.next()) { @@ -580,8 +586,7 @@ void SkEdgeClipper::ClipPath(const SkPathRaw& raw, const SkRect& clip, bool canC } break; case SkPathEdgeIter::Edge::kConic: { - const SkPoint* quadPts = - quadder.computeQuads(e.fPts, iter.conicWeight(), kConicTol); + const SkPoint* quadPts = quadder.computeQuads(e.fPts, iter.conicWeight(), conicTol); for (int i = 0; i < quadder.countQuads(); ++i) { if (clipper.clipQuad(quadPts, clip)) { consume(&clipper, e.fIsNewContour, ctx); @@ -594,9 +599,6 @@ void SkEdgeClipper::ClipPath(const SkPathRaw& raw, const SkRect& clip, bool canC consume(&clipper, e.fIsNewContour, ctx); } break; - default: - SkDEBUGFAIL("Unknown edge type"); - break; } } } diff --git a/gfx/skia/skia/src/core/SkEdgeClipper.h b/gfx/skia/skia/src/core/SkEdgeClipper.h @@ -9,14 +9,11 @@ #ifndef SkEdgeClipper_DEFINED #define SkEdgeClipper_DEFINED -#include "include/core/SkPathTypes.h" +#include "include/core/SkPath.h" #include "include/core/SkPoint.h" #include "include/core/SkScalar.h" #include "include/private/base/SkDebug.h" -#include <optional> - -struct SkPathRaw; struct SkRect; /** This is basically an iterator. It is initialized with an edge and a clip, @@ -30,7 +27,7 @@ public: bool clipQuad(const SkPoint pts[3], const SkRect& clip); bool clipCubic(const SkPoint pts[4], const SkRect& clip); - std::optional<SkPathVerb> next(SkPoint pts[]); + SkPath::Verb next(SkPoint pts[]); bool canCullToTheRight() const { return fCanCullToTheRight; } @@ -38,20 +35,20 @@ public: * Clips each segment from the path, and passes the result (in a clipper) to the * consume proc. */ - static void ClipPath(const SkPathRaw&, const SkRect& clip, bool canCullToTheRight, + static void ClipPath(const SkPath& path, const SkRect& clip, bool canCullToTheRight, void (*consume)(SkEdgeClipper*, bool newCtr, void* ctx), void* ctx); private: - SkPoint* fCurrPoint; - SkPathVerb* fCurrVerb, *fCurrVerbStop; - const bool fCanCullToTheRight; + SkPoint* fCurrPoint; + SkPath::Verb* fCurrVerb; + const bool fCanCullToTheRight; enum { kMaxVerbs = 18, // max curvature in X and Y split cubic into 9 pieces, * (line + cubic) kMaxPoints = 54 // 2 lines + 1 cubic require 6 points; times 9 pieces }; - SkPoint fPoints[kMaxPoints]; - SkPathVerb fVerbs[kMaxVerbs]; + SkPoint fPoints[kMaxPoints]; + SkPath::Verb fVerbs[kMaxVerbs]; void clipMonoQuad(const SkPoint srcPts[3], const SkRect& clip); void clipMonoCubic(const SkPoint srcPts[4], const SkRect& clip); diff --git a/gfx/skia/skia/src/core/SkEffectPriv.h b/gfx/skia/skia/src/core/SkEffectPriv.h @@ -10,7 +10,6 @@ #include "include/core/SkColor.h" #include "include/core/SkColorType.h" -#include "include/core/SkRect.h" class SkArenaAlloc; class SkColorSpace; @@ -25,10 +24,6 @@ struct SkStageRec { SkColorSpace* fDstCS; // may be nullptr SkColor4f fPaintColor; const SkSurfaceProps& fSurfaceProps; - // The device-space bounding box of the geometry being drawn. - // An empty value can be used when it is expensive to compute, - // in which case a heuristic will be used if necessary. - SkRect fDstBounds; }; #endif // SkEffectPriv_DEFINED diff --git a/gfx/skia/skia/src/core/SkExecutor.cpp b/gfx/skia/skia/src/core/SkExecutor.cpp @@ -9,7 +9,6 @@ #include "include/private/base/SkMutex.h" #include "include/private/base/SkSemaphore.h" #include "include/private/base/SkTArray.h" -#include "include/private/base/SkTPin.h" #include "src/base/SkNoDestructor.h" #include <deque> @@ -36,14 +35,9 @@ SkExecutor::~SkExecutor() {} // The default default SkExecutor is an SkTrivialExecutor, which just runs the work right away. class SkTrivialExecutor final : public SkExecutor { -public: - void add(std::function<void(void)> work, int /* workList */) override { - work(); - } void add(std::function<void(void)> work) override { - this->add(std::move(work), /* workList= */ 0); + work(); } - int discardAllPendingWork() override { return 0;} }; static SkExecutor& trivial_executor() { @@ -80,12 +74,7 @@ static inline std::function<void(void)> pop(TArray<std::function<void(void)>>* l template <typename WorkList> class SkThreadPool final : public SkExecutor { public: - explicit SkThreadPool(int numWorkLists, int threads, bool allowBorrowing) - : fNumWorkLists(numWorkLists < 1 ? 1 : numWorkLists) - , fAllowBorrowing(allowBorrowing) { - - fWorkLists = std::make_unique<WorkList[]>(fNumWorkLists); - + explicit SkThreadPool(int threads, bool allowBorrowing) : fAllowBorrowing(allowBorrowing) { for (int i = 0; i < threads; i++) { fThreads.emplace_back(&Loop, this); } @@ -94,8 +83,7 @@ public: ~SkThreadPool() override { // Signal each thread that it's time to shut down. for (int i = 0; i < fThreads.size(); i++) { - // Add the notification to the highest priority list - this->add(nullptr, /* workList= */ 0); + this->add(nullptr); } // Wait for each thread to shut down. for (int i = 0; i < fThreads.size(); i++) { @@ -103,35 +91,16 @@ public: } } - void add(std::function<void(void)> work, int workList) override { - workList = SkTPin(workList, 0, fNumWorkLists-1); - + void add(std::function<void(void)> work) override { // Add some work to our pile of work to do. { SkAutoMutexExclusive lock(fWorkLock); - - fWorkLists[workList].emplace_back(std::move(work)); + fWork.emplace_back(std::move(work)); } // Tell the Loop() threads to pick it up. fWorkAvailable.signal(1); } - void add(std::function<void(void)> work) override { - this->add(std::move(work), /* workList= */ 0); - } - - int discardAllPendingWork() override { - SkAutoMutexExclusive lock(fWorkLock); - - int numDiscarded = 0; - for (int i = 0; i < fNumWorkLists; ++i) { - numDiscarded += fWorkLists[i].size(); - fWorkLists[i].clear(); - } - - return numDiscarded; - } - void borrow() override { // If there is work waiting and we're allowed to borrow work, do it. if (fAllowBorrowing && fWorkAvailable.try_wait()) { @@ -140,26 +109,13 @@ public: } private: - // This method should usually be called only when fWorkAvailable indicates there's work to do. + // This method should be called only when fWorkAvailable indicates there's work to do. bool do_work() { std::function<void(void)> work; - bool workAvailable = false; { SkAutoMutexExclusive lock(fWorkLock); - - for (int i = 0; i < fNumWorkLists; ++i) { - if (!fWorkLists[i].empty()) { - workAvailable = true; - work = pop(&fWorkLists[i]); - break; - } - } - } - - if (!workAvailable) { - // Because we can discard work asynchronous to Loop() we can sometimes get in this - // method with no work to do - return true; + SkASSERT(!fWork.empty()); // TODO: if (fWork.empty()) { return true; } ? + work = pop(&fWork); } if (!work) { @@ -180,40 +136,20 @@ private: // Both SkMutex and SkSpinlock can work here. using Lock = SkMutex; - TArray<std::thread> fThreads; - const int fNumWorkLists; // guaranteed >= 1 - std::unique_ptr<WorkList[]> fWorkLists SK_GUARDED_BY(fWorkLock); - Lock fWorkLock; - SkSemaphore fWorkAvailable; - const bool fAllowBorrowing; + TArray<std::thread> fThreads; + WorkList fWork; + Lock fWorkLock; + SkSemaphore fWorkAvailable; + bool fAllowBorrowing; }; std::unique_ptr<SkExecutor> SkExecutor::MakeFIFOThreadPool(int threads, bool allowBorrowing) { using WorkList = std::deque<std::function<void(void)>>; - return std::make_unique<SkThreadPool<WorkList>>(/* numWorkLists= */ 1, - threads > 0 ? threads : num_cores(), + return std::make_unique<SkThreadPool<WorkList>>(threads > 0 ? threads : num_cores(), allowBorrowing); } std::unique_ptr<SkExecutor> SkExecutor::MakeLIFOThreadPool(int threads, bool allowBorrowing) { using WorkList = TArray<std::function<void(void)>>; - return std::make_unique<SkThreadPool<WorkList>>(/* numWorkLists= */ 1, - threads > 0 ? threads : num_cores(), - allowBorrowing); -} - -std::unique_ptr<SkExecutor> SkExecutor::MakeMultiListFIFOThreadPool(int numWorkLists, - int threads, - bool allowBorrowing) { - using WorkList = std::deque<std::function<void(void)>>; - return std::make_unique<SkThreadPool<WorkList>>(numWorkLists, - threads > 0 ? threads : num_cores(), - allowBorrowing); -} -std::unique_ptr<SkExecutor> SkExecutor::MakeMultiListLIFOThreadPool(int numWorkLists, - int threads, - bool allowBorrowing) { - using WorkList = TArray<std::function<void(void)>>; - return std::make_unique<SkThreadPool<WorkList>>(numWorkLists, - threads > 0 ? threads : num_cores(), + return std::make_unique<SkThreadPool<WorkList>>(threads > 0 ? threads : num_cores(), allowBorrowing); } diff --git a/gfx/skia/skia/src/core/SkFDot6.h b/gfx/skia/skia/src/core/SkFDot6.h @@ -20,7 +20,7 @@ typedef int32_t SkFDot6; * _cairo_fixed_from_double. It does banker's rounding * (i.e. round to nearest even) */ -inline SkFDot6 SkScalarRoundToFDot6(SkScalar x, int shift) +inline SkFDot6 SkScalarRoundToFDot6(SkScalar x, int shift = 0) { union { double fDouble; @@ -41,7 +41,7 @@ inline SkFDot6 SkScalarRoundToFDot6(SkScalar x, int shift) #define SK_FDot6Half (32) #ifdef SK_DEBUG - constexpr inline SkFDot6 SkIntToFDot6(int x) { + inline SkFDot6 SkIntToFDot6(int x) { SkASSERT(SkToS16(x) == x); return x << 6; } @@ -51,7 +51,7 @@ inline SkFDot6 SkScalarRoundToFDot6(SkScalar x, int shift) #define SkFDot6Floor(x) ((x) >> 6) #define SkFDot6Ceil(x) (((x) + 63) >> 6) -#define SkFDot6Round(x) (((x) + SK_FDot6Half) >> 6) +#define SkFDot6Round(x) (((x) + 32) >> 6) #define SkFixedToFDot6(x) ((x) >> 10) @@ -61,10 +61,9 @@ inline SkFixed SkFDot6ToFixed(SkFDot6 x) { return SkLeftShift(x, 10); } -#define SkFloatToFDot6(x) (SkFDot6)((x) * SK_FDot6One) -#define SkScalarToFDot6 SkFloatToFDot6 -#define SkFDot6ToFloat(x) ((float)(x) * 0.015625f) -#define SkFDot6ToScalar SkFDot6ToFloat +#define SkScalarToFDot6(x) (SkFDot6)((x) * 64) +#define SkFDot6ToScalar(x) ((SkScalar)(x) * 0.015625f) +#define SkFDot6ToFloat SkFDot6ToScalar inline SkFixed SkFDot6Div(SkFDot6 a, SkFDot6 b) { SkASSERT(b != 0); diff --git a/gfx/skia/skia/src/core/SkFont.cpp b/gfx/skia/skia/src/core/SkFont.cpp @@ -13,7 +13,6 @@ #include "include/core/SkPaint.h" #include "include/core/SkPath.h" #include "include/core/SkPathEffect.h" -#include "include/core/SkPoint.h" #include "include/core/SkRect.h" #include "include/core/SkRefCnt.h" #include "include/core/SkScalar.h" @@ -21,10 +20,11 @@ #include "include/core/SkTypes.h" #include "include/private/base/SkDebug.h" #include "include/private/base/SkFloatingPoint.h" +#include "include/private/base/SkPoint_impl.h" +#include "include/private/base/SkSpan_impl.h" #include "include/private/base/SkTemplates.h" #include "include/private/base/SkTo.h" #include "src/base/SkUTF.h" -#include "src/base/SkZip.h" #include "src/core/SkFontPriv.h" #include "src/core/SkGlyph.h" #include "src/core/SkMatrixPriv.h" @@ -174,37 +174,38 @@ SkGlyphID SkFont::unicharToGlyph(SkUnichar uni) const { return this->getTypeface()->unicharToGlyph(uni); } -void SkFont::unicharsToGlyphs(SkSpan<const SkUnichar> unis, SkSpan<SkGlyphID> glyphs) const { - this->getTypeface()->unicharsToGlyphs(unis, glyphs); +void SkFont::unicharsToGlyphs(const SkUnichar uni[], int count, SkGlyphID glyphs[]) const { + this->getTypeface()->unicharsToGlyphs(uni, count, glyphs); } -size_t SkFont::textToGlyphs(const void* text, size_t byteLength, SkTextEncoding encoding, - SkSpan<SkGlyphID> glyphs) const { - return this->getTypeface()->textToGlyphs(text, byteLength, encoding, glyphs); +int SkFont::textToGlyphs(const void* text, size_t byteLength, SkTextEncoding encoding, + SkGlyphID glyphs[], int maxGlyphCount) const { + return this->getTypeface()->textToGlyphs(text, byteLength, encoding, + glyphs, maxGlyphCount); } SkScalar SkFont::measureText(const void* text, size_t length, SkTextEncoding encoding, SkRect* bounds, const SkPaint* paint) const { SkAutoToGlyphs atg(*this, text, length, encoding); - const SkSpan<const SkGlyphID> glyphIDs = atg.glyphs(); - - if (glyphIDs.size() == 0) { + const int glyphCount = atg.count(); + if (glyphCount == 0) { if (bounds) { bounds->setEmpty(); } return 0; } + const SkGlyphID* glyphIDs = atg.glyphs(); auto [strikeSpec, strikeToSourceScale] = SkStrikeSpec::MakeCanonicalized(*this, paint); SkBulkGlyphMetrics metrics{strikeSpec}; - SkSpan<const SkGlyph*> glyphs = metrics.glyphs(glyphIDs); + SkSpan<const SkGlyph*> glyphs = metrics.glyphs(SkSpan(glyphIDs, glyphCount)); SkScalar width = 0; if (bounds) { *bounds = glyphs[0]->rect(); width = glyphs[0]->advanceX(); - for (size_t i = 1; i < glyphIDs.size(); ++i) { + for (int i = 1; i < glyphCount; ++i) { SkRect r = glyphs[i]->rect(); r.offset(width, 0); bounds->join(r); @@ -229,62 +230,59 @@ SkScalar SkFont::measureText(const void* text, size_t length, SkTextEncoding enc return width; } -static inline SkRect scale_pos(SkRect r, SkScalar s) { - SkASSERT(s >= 0); // so we don't have to worry about swapping the rect to stay valid - return { - r.fLeft * s, r.fTop * s, r.fRight * s, r.fBottom * s, - }; -} -void SkFont::getWidthsBounds(SkSpan<const SkGlyphID> glyphIDs, - SkSpan<SkScalar> widths, - SkSpan<SkRect> bounds, +void SkFont::getWidthsBounds(const SkGlyphID glyphIDs[], + int count, + SkScalar widths[], + SkRect bounds[], const SkPaint* paint) const { auto [strikeSpec, strikeToSourceScale] = SkStrikeSpec::MakeCanonicalized(*this, paint); SkBulkGlyphMetrics metrics{strikeSpec}; - SkSpan<const SkGlyph*> glyphs = metrics.glyphs(glyphIDs); + SkSpan<const SkGlyph*> glyphs = metrics.glyphs(SkSpan(glyphIDs, count)); - if (bounds.size()) { - const auto n = std::min(bounds.size(), glyphs.size()); - for (auto [bound, glyph] : SkMakeZip(bounds.first(n), glyphs.first(n))) { - bound = scale_pos(glyph->rect(), strikeToSourceScale); + if (bounds) { + SkMatrix scaleMat = SkMatrix::Scale(strikeToSourceScale, strikeToSourceScale); + SkRect* cursor = bounds; + for (auto glyph : glyphs) { + scaleMat.mapRectScaleTranslate(cursor++, glyph->rect()); } } - if (widths.size()) { - const auto n = std::min(widths.size(), glyphs.size()); - for (auto [width, glyph] : SkMakeZip(widths.first(n), glyphs.first(n))) { - width = glyph->advanceX() * strikeToSourceScale; + if (widths) { + SkScalar* cursor = widths; + for (auto glyph : glyphs) { + *cursor++ = glyph->advanceX() * strikeToSourceScale; } } } -void SkFont::getPos(SkSpan<const SkGlyphID> glyphIDs, SkSpan<SkPoint> pos, SkPoint origin) const { +void SkFont::getPos(const SkGlyphID glyphIDs[], int count, SkPoint pos[], SkPoint origin) const { auto [strikeSpec, strikeToSourceScale] = SkStrikeSpec::MakeCanonicalized(*this); SkBulkGlyphMetrics metrics{strikeSpec}; - SkSpan<const SkGlyph*> glyphs = metrics.glyphs(glyphIDs); + SkSpan<const SkGlyph*> glyphs = metrics.glyphs(SkSpan(glyphIDs, count)); SkPoint sum = origin; - const auto n = std::min(pos.size(), glyphs.size()); - for (auto [position, glyph] : SkMakeZip(pos.first(n), glyphs.first(n))) { - position = sum; + for (auto glyph : glyphs) { + *pos++ = sum; sum += glyph->advanceVector() * strikeToSourceScale; } } -void SkFont::getXPos(SkSpan<const SkGlyphID> gIDs, SkSpan<SkScalar> xpos, SkScalar origin) const { +void SkFont::getXPos( + const SkGlyphID glyphIDs[], int count, SkScalar xpos[], SkScalar origin) const { + auto [strikeSpec, strikeToSourceScale] = SkStrikeSpec::MakeCanonicalized(*this); SkBulkGlyphMetrics metrics{strikeSpec}; - SkSpan<const SkGlyph*> glyphs = metrics.glyphs(gIDs); + SkSpan<const SkGlyph*> glyphs = metrics.glyphs(SkSpan(glyphIDs, count)); SkScalar loc = origin; - const auto n = std::min(xpos.size(), glyphs.size()); - for (auto [xposition, glyph] : SkMakeZip(xpos.first(n), glyphs.first(n))) { - xposition = loc; + SkScalar* cursor = xpos; + for (auto glyph : glyphs) { + *cursor++ = loc; loc += glyph->advanceX() * strikeToSourceScale; } } -void SkFont::getPaths(SkSpan<const SkGlyphID> glyphIDs, +void SkFont::getPaths(const SkGlyphID glyphIDs[], int count, void (*proc)(const SkPath*, const SkMatrix&, void*), void* ctx) const { SkFont font(*this); SkScalar scale = font.setupForAsPaths(nullptr); @@ -292,35 +290,28 @@ void SkFont::getPaths(SkSpan<const SkGlyphID> glyphIDs, SkStrikeSpec strikeSpec = SkStrikeSpec::MakeWithNoDevice(font); SkBulkGlyphMetricsAndPaths paths{strikeSpec}; - SkSpan<const SkGlyph*> glyphs = paths.glyphs(glyphIDs); + SkSpan<const SkGlyph*> glyphs = paths.glyphs(SkSpan(glyphIDs, count)); for (auto glyph : glyphs) { proc(glyph->path(), mx, ctx); } } -std::optional<SkPath> SkFont::getPath(SkGlyphID glyphID) const { - std::optional<SkPath> result; - - this->getPaths({&glyphID, 1}, [](const SkPath* path, const SkMatrix& mx, void* ctx) { - if (path) { - auto* result = static_cast<std::optional<SkPath>*>(ctx); - *result = path->makeTransform(mx); - } - }, &result); - - return result; -} - -#ifndef SK_HIDE_PATH_EDIT_METHODS bool SkFont::getPath(SkGlyphID glyphID, SkPath* path) const { - if (auto maybepath = this->getPath(glyphID)) { - *path = *maybepath; - return true; - } - return false; + struct Pair { + SkPath* fPath; + bool fWasSet; + } pair = { path, false }; + + this->getPaths(&glyphID, 1, [](const SkPath* orig, const SkMatrix& mx, void* ctx) { + Pair* pair = static_cast<Pair*>(ctx); + if (orig) { + orig->transform(mx, pair->fPath); + pair->fWasSet = true; + } + }, &pair); + return pair.fWasSet; } -#endif SkScalar SkFont::getMetrics(SkFontMetrics* metrics) const { @@ -387,7 +378,7 @@ SkScalar SkFontPriv::ApproximateTransformedTextSize(const SkFont& font, const Sk } } -size_t SkFontPriv::CountTextElements(const void* text, size_t byteLength, SkTextEncoding encoding) { +int SkFontPriv::CountTextElements(const void* text, size_t byteLength, SkTextEncoding encoding) { switch (encoding) { case SkTextEncoding::kUTF8: return SkUTF::CountUTF8(reinterpret_cast<const char*>(text), byteLength); @@ -411,7 +402,7 @@ void SkFontPriv::GlyphsToUnichars(const SkFont& font, const SkGlyphID glyphs[], auto typeface = font.getTypeface(); const unsigned numGlyphsInTypeface = typeface->countGlyphs(); AutoTArray<SkUnichar> unichars(static_cast<size_t>(numGlyphsInTypeface)); - typeface->getGlyphToUnicodeMap(unichars); + typeface->getGlyphToUnicodeMap(unichars.get()); for (int i = 0; i < count; ++i) { unsigned id = glyphs[i]; diff --git a/gfx/skia/skia/src/core/SkFontDescriptor.cpp b/gfx/skia/skia/src/core/SkFontDescriptor.cpp @@ -14,7 +14,6 @@ #include "include/private/base/SkTFitsIn.h" #include "include/private/base/SkTo.h" #include "src/core/SkStreamPriv.h" -#include "src/utils/SkFloatUtils.h" #include <cstddef> #include <cstdint> @@ -272,10 +271,6 @@ void SkFontDescriptor::serialize(SkWStream* stream) const { } SkFontStyle::Width SkFontDescriptor::SkFontStyleWidthForWidthAxisValue(SkScalar width) { - int usWidth = SkScalarRoundToInt(SkFloatInterpFunc(width, &width_for_usWidth[1], usWidths, 9)); + int usWidth = SkScalarRoundToInt(SkScalarInterpFunc(width, &width_for_usWidth[1], usWidths, 9)); return static_cast<SkFontStyle::Width>(usWidth); } - -SkScalar SkFontDescriptor::SkFontWidthAxisValueForStyleWidth(int width) { - return width_for_usWidth[width & 0xF]; -} diff --git a/gfx/skia/skia/src/core/SkFontDescriptor.h b/gfx/skia/skia/src/core/SkFontDescriptor.h @@ -142,7 +142,6 @@ public: this->getPaletteEntryOverrideCount()}); } static SkFontStyle::Width SkFontStyleWidthForWidthAxisValue(SkScalar width); - static SkScalar SkFontWidthAxisValueForStyleWidth(int width); private: SkString fFamilyName; diff --git a/gfx/skia/skia/src/core/SkFontMgr.cpp b/gfx/skia/skia/src/core/SkFontMgr.cpp @@ -152,8 +152,8 @@ sk_sp<SkTypeface> SkFontMgr::legacyMakeTypeface(const char familyName[], SkFontS } sk_sp<SkFontMgr> SkFontMgr::RefEmpty() { - static SkFontMgr* singleton = new SkEmptyFontMgr(); - return sk_ref_sp(singleton); + static sk_sp<SkFontMgr> singleton(new SkEmptyFontMgr); + return singleton; } /** diff --git a/gfx/skia/skia/src/core/SkFontPriv.h b/gfx/skia/skia/src/core/SkFontPriv.h @@ -80,7 +80,7 @@ public: } // Returns the number of elements (characters or glyphs) in the array. - static size_t CountTextElements(const void* text, size_t byteLength, SkTextEncoding); + static int CountTextElements(const void* text, size_t byteLength, SkTextEncoding); static void GlyphsToUnichars(const SkFont&, const SkGlyphID glyphs[], int count, SkUnichar[]); @@ -94,25 +94,26 @@ class SkAutoToGlyphs { public: SkAutoToGlyphs(const SkFont& font, const void* text, size_t length, SkTextEncoding encoding) { if (encoding == SkTextEncoding::kGlyphID || length == 0) { - fGlyphs = {reinterpret_cast<const uint16_t*>(text), length >> 1}; + fGlyphs = reinterpret_cast<const uint16_t*>(text); + fCount = SkToInt(length >> 1); } else { - const size_t count = font.countText(text, length, encoding); - fStorage.reset(count); - SkSpan<SkGlyphID> glyphs = {fStorage.get(), count}; - (void)font.textToGlyphs(text, length, encoding, glyphs); - fGlyphs = glyphs; + fCount = font.countText(text, length, encoding); + if (fCount < 0) { + fCount = 0; + } + fStorage.reset(fCount); + font.textToGlyphs(text, length, encoding, fStorage.get(), fCount); + fGlyphs = fStorage.get(); } } - size_t size() const { return fGlyphs.size(); } - const SkGlyphID* data() const { return fGlyphs.data(); } - - size_t count() const { return fGlyphs.size(); } - SkSpan<const SkGlyphID> glyphs() const { return fGlyphs; } + int count() const { return fCount; } + const SkGlyphID* glyphs() const { return fGlyphs; } private: skia_private::AutoSTArray<32, SkGlyphID> fStorage; - SkSpan<const SkGlyphID> fGlyphs; + const SkGlyphID* fGlyphs; + int fCount; }; #endif diff --git a/gfx/skia/skia/src/core/SkFontStream.cpp b/gfx/skia/skia/src/core/SkFontStream.cpp @@ -13,7 +13,6 @@ #include "src/base/SkAutoMalloc.h" #include "src/base/SkEndian.h" -#include <algorithm> #include <cstdint> struct SkSFNTHeader { @@ -159,15 +158,17 @@ int SkFontStream::CountTTCEntries(SkStream* stream) { } } -int SkFontStream::GetTableTags(SkStream* stream, int ttcIndex, SkSpan<SkFontTableTag> tags) { +int SkFontStream::GetTableTags(SkStream* stream, int ttcIndex, + SkFontTableTag tags[]) { SfntHeader header; if (!header.init(stream, ttcIndex)) { return 0; } - const size_t n = std::min((size_t)header.fCount, tags.size()); - for (size_t i = 0; i < n; i++) { - tags[i] = SkEndian_SwapBE32(header.fDir[i].fTag); + if (tags) { + for (int i = 0; i < header.fCount; i++) { + tags[i] = SkEndian_SwapBE32(header.fDir[i].fTag); + } } return header.fCount; } diff --git a/gfx/skia/skia/src/core/SkFontStream.h b/gfx/skia/skia/src/core/SkFontStream.h @@ -8,7 +8,6 @@ #ifndef SkFontStream_DEFINED #define SkFontStream_DEFINED -#include "include/core/SkSpan.h" #include "include/core/SkTypeface.h" #include <cstddef> @@ -33,7 +32,7 @@ public: * Note: the stream is rewound initially, but is returned at an arbitrary * read offset. */ - static int GetTableTags(SkStream*, int ttcIndex, SkSpan<SkFontTableTag> tags); + static int GetTableTags(SkStream*, int ttcIndex, SkFontTableTag tags[]); /** * @param ttcIndex 0 for normal sfnts, or the index within a TTC sfnt. diff --git a/gfx/skia/skia/src/core/SkGeometry.cpp b/gfx/skia/skia/src/core/SkGeometry.cpp @@ -1485,12 +1485,8 @@ bool SkConic::asQuadTol(SkScalar tol) const { // Limit the number of suggested quads to approximate a conic #define kMaxConicToQuadPOW2 5 -static inline bool bad_conic_w(float w) { - return w < 0 || !SkIsFinite(w); -} - int SkConic::computeQuadPOW2(SkScalar tol) const { - if (tol < 0 || !SkIsFinite(tol) || !SkPointPriv::AreFinite(fPts, 3) || bad_conic_w(fW)) { + if (tol < 0 || !SkIsFinite(tol) || !SkPointPriv::AreFinite(fPts, 3)) { return 0; } @@ -1572,12 +1568,7 @@ static SkPoint* subdivide(const SkConic& src, SkPoint pts[], int level) { } int SkConic::chopIntoQuadsPOW2(SkPoint pts[], int pow2) const { - SkASSERT(pow2 >= 0 && pow2 <= kMaxConicToQuadPOW2); - - if (bad_conic_w(fW)) { - pow2 = 0; - } - + SkASSERT(pow2 >= 0); *pts = fPts[0]; SkDEBUGCODE(SkPoint* endPts); if (pow2 == kMaxConicToQuadPOW2) { // If an extreme weight generates many quads ... @@ -1702,11 +1693,11 @@ void SkConic::computeTightBounds(SkRect* bounds) const { if (this->findYExtrema(&t)) { this->evalAt(t, &pts[count++]); } - *bounds = SkRect::BoundsOrEmpty({pts, count}); + bounds->setBounds(pts, count); } void SkConic::computeFastBounds(SkRect* bounds) const { - *bounds = SkRect::BoundsOrEmpty(fPts); + bounds->setBounds(fPts, 3); } #if 0 // unimplemented @@ -1725,7 +1716,7 @@ SkScalar SkConic::TransformW(const SkPoint pts[3], SkScalar w, const SkMatrix& m ratquad_mapTo3D(pts, w, src); - matrix.mapHomogeneousPoints(dst, src); + matrix.mapHomogeneousPoints(dst, src, 3); // w' = sqrt(w1*w1/w0*w2) // use doubles temporarily, to handle small numer/denom @@ -1735,7 +1726,7 @@ SkScalar SkConic::TransformW(const SkPoint pts[3], SkScalar w, const SkMatrix& m return sk_double_to_float(sqrt(sk_ieee_double_divide(w1 * w1, w0 * w2))); } -int SkConic::BuildUnitArc(const SkVector& uStart, const SkVector& uStop, SkPathDirection dir, +int SkConic::BuildUnitArc(const SkVector& uStart, const SkVector& uStop, SkRotationDirection dir, const SkMatrix* userMatrix, SkConic dst[kMaxConicsForArc]) { // rotate by x,y so that uStart is (1.0) SkScalar x = SkPoint::DotProduct(uStart, uStop); @@ -1746,12 +1737,12 @@ int SkConic::BuildUnitArc(const SkVector& uStart, const SkVector& uStop, SkPathD // check for (effectively) coincident vectors // this can happen if our angle is nearly 0 or nearly 180 (y == 0) // ... we use the dot-prod to distinguish between 0 and 180 (x > 0) - if (absY <= SK_ScalarNearlyZero && x > 0 && ((y >= 0 && SkPathDirection::kCW == dir) || - (y <= 0 && SkPathDirection::kCCW == dir))) { + if (absY <= SK_ScalarNearlyZero && x > 0 && ((y >= 0 && kCW_SkRotationDirection == dir) || + (y <= 0 && kCCW_SkRotationDirection == dir))) { return 0; } - if (dir == SkPathDirection::kCCW) { + if (dir == kCCW_SkRotationDirection) { y = -y; } @@ -1814,14 +1805,14 @@ int SkConic::BuildUnitArc(const SkVector& uStart, const SkVector& uStop, SkPathD // now handle counter-clockwise and the initial unitStart rotation SkMatrix matrix; matrix.setSinCos(uStart.fY, uStart.fX); - if (dir == SkPathDirection::kCCW) { + if (dir == kCCW_SkRotationDirection) { matrix.preScale(SK_Scalar1, -SK_Scalar1); } if (userMatrix) { matrix.postConcat(*userMatrix); } for (int i = 0; i < conicCount; ++i) { - matrix.mapPoints(dst[i].fPts); + matrix.mapPoints(dst[i].fPts, 3); } return conicCount; } diff --git a/gfx/skia/skia/src/core/SkGeometry.h b/gfx/skia/skia/src/core/SkGeometry.h @@ -8,10 +8,8 @@ #ifndef SkGeometry_DEFINED #define SkGeometry_DEFINED -#include "include/core/SkPathTypes.h" #include "include/core/SkPoint.h" #include "include/core/SkScalar.h" -#include "include/core/SkSpan.h" #include "include/core/SkTypes.h" #include "include/private/base/SkFloatingPoint.h" #include "src/base/SkVx.h" @@ -320,6 +318,11 @@ SkCubicType SkClassifyCubic(const SkPoint p[4], double t[2] = nullptr, double s[ /////////////////////////////////////////////////////////////////////////////// +enum SkRotationDirection { + kCW_SkRotationDirection, + kCCW_SkRotationDirection +}; + struct SkConic { SkConic() {} SkConic(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2, SkScalar w) { @@ -407,7 +410,7 @@ struct SkConic { enum { kMaxConicsForArc = 5 }; - static int BuildUnitArc(const SkVector& start, const SkVector& stop, SkPathDirection, + static int BuildUnitArc(const SkVector& start, const SkVector& stop, SkRotationDirection, const SkMatrix*, SkConic conics[kMaxConicsForArc]); }; @@ -435,7 +438,7 @@ struct SkQuadCoeff { fA = P2 - times_2(P1) + fC; } - skvx::float2 eval(const skvx::float2& tt) const { + skvx::float2 eval(const skvx::float2& tt) { return (fA * tt + fB) * tt + fC; } @@ -461,7 +464,7 @@ struct SkConicCoeff { fDenom.fA = 0 - fDenom.fB; } - skvx::float2 eval(SkScalar t) const { + skvx::float2 eval(SkScalar t) { skvx::float2 tt(t); skvx::float2 numer = fNumer.eval(tt); skvx::float2 denom = fDenom.eval(tt); @@ -485,7 +488,7 @@ struct SkCubicCoeff { fD = P0; } - skvx::float2 eval(const skvx::float2& t) const { + skvx::float2 eval(const skvx::float2& t) { return ((fA * t + fB) * t + fC) * t + fD; } @@ -526,16 +529,13 @@ public: return pts; } - const SkPoint* computeQuads(SkSpan<const SkPoint> pts, SkScalar weight, SkScalar tol) { + const SkPoint* computeQuads(const SkPoint pts[3], SkScalar weight, + SkScalar tol) { SkConic conic; - conic.set(pts.data(), weight); + conic.set(pts, weight); return computeQuads(conic, tol); } - const SkPoint* computeQuads(const SkPoint pts[3], SkScalar weight, SkScalar tol) { - return this->computeQuads({pts, 3}, weight, tol); - } - int countQuads() const { return fQuadCount; } private: diff --git a/gfx/skia/skia/src/core/SkGlyph.cpp b/gfx/skia/skia/src/core/SkGlyph.cpp @@ -40,7 +40,7 @@ SkPictureBackedGlyphDrawable::MakeFromBuffer(SkReadBuffer& buffer) { sk_sp<SkData> pictureData = buffer.readByteArrayAsData(); // Return nullptr if invalid or there an empty drawable, which is represented by nullptr. - if (!buffer.isValid() || pictureData->empty()) { + if (!buffer.isValid() || pictureData->size() == 0) { return nullptr; } @@ -68,7 +68,7 @@ void SkPictureBackedGlyphDrawable::FlattenDrawable(SkWriteBuffer& buffer, SkDraw sk_sp<SkData> data = picture->serialize(); // If the picture is too big, or there is no picture, then drop by sending an empty byte array. - if (!SkTFitsIn<uint32_t>(data->size()) || data->empty()) { + if (!SkTFitsIn<uint32_t>(data->size()) || data->size() == 0) { buffer.writeByteArray(nullptr, 0); return; } @@ -400,9 +400,11 @@ size_t SkGlyph::addPathFromBuffer(SkReadBuffer& buffer, SkArenaAlloc* alloc) { if (hasPath) { const bool pathIsHairline = buffer.readBool(); const bool pathIsModified = buffer.readBool(); - if (auto path = buffer.readPath()) { - if (this->setPath(alloc, &path.value(), pathIsHairline, pathIsModified)) { - memoryIncrease += path->approximateBytesUsed(); + SkPath path; + buffer.readPath(&path); + if (buffer.isValid()) { + if (this->setPath(alloc, &path, pathIsHairline, pathIsModified)) { + memoryIncrease += path.approximateBytesUsed(); } } } else { @@ -451,26 +453,27 @@ static std::tuple<SkScalar, SkScalar> calculate_path_gap( }; // Handle all the different verbs for the path. - auto addLine = [&](SkSpan<const SkPoint> pts, SkScalar offset) { + SkPoint pts[4]; + auto addLine = [&](SkScalar offset) { SkScalar t = sk_ieee_float_divide(offset - pts[0].fY, pts[1].fY - pts[0].fY); if (0 <= t && t < 1) { // this handles divide by zero above expandGap(pts[0].fX + t * (pts[1].fX - pts[0].fX)); } }; - auto addQuad = [&](SkSpan<const SkPoint> pts, SkScalar offset) { + auto addQuad = [&](SkScalar offset) { SkScalar intersectionStorage[2]; auto intersections = SkBezierQuad::IntersectWithHorizontalLine( - pts, offset, intersectionStorage); + SkSpan(pts, 3), offset, intersectionStorage); for (SkScalar x : intersections) { expandGap(x); } }; - auto addCubic = [&](SkSpan<const SkPoint> pts, SkScalar offset) { + auto addCubic = [&](SkScalar offset) { float intersectionStorage[3]; auto intersections = SkBezierCubic::IntersectWithHorizontalLine( - pts, offset, intersectionStorage); + SkSpan{pts, 4}, offset, intersectionStorage); for(double intersection : intersections) { expandGap(intersection); @@ -478,60 +481,64 @@ static std::tuple<SkScalar, SkScalar> calculate_path_gap( }; // Handle when a verb's points are in the gap between top and bottom. - auto addPts = [&expandGap, topOffset, bottomOffset](SkSpan<const SkPoint> pts) { - for (const SkPoint p : pts) { - if (topOffset < p.fY && p.fY < bottomOffset) { - expandGap(p.fX); + auto addPts = [&expandGap, &pts, topOffset, bottomOffset](int ptCount) { + for (int i = 0; i < ptCount; ++i) { + if (topOffset < pts[i].fY && pts[i].fY < bottomOffset) { + expandGap(pts[i].fX); } } }; SkPath::Iter iter(path, false); - while (auto rec = iter.next()) { - const SkSpan<const SkPoint> pts = rec->fPoints; - switch (rec->fVerb) { - case SkPathVerb::kMove: { + SkPath::Verb verb; + while (SkPath::kDone_Verb != (verb = iter.next(pts))) { + switch (verb) { + case SkPath::kMove_Verb: { break; } - case SkPathVerb::kLine: { + case SkPath::kLine_Verb: { auto [lineTop, lineBottom] = std::minmax({pts[0].fY, pts[1].fY}); // The y-coordinates of the points intersect the top and bottom offsets. if (topOffset <= lineBottom && lineTop <= bottomOffset) { - addLine(pts, topOffset); - addLine(pts, bottomOffset); - addPts(pts); + addLine(topOffset); + addLine(bottomOffset); + addPts(2); } break; } - case SkPathVerb::kQuad: { + case SkPath::kQuad_Verb: { auto [quadTop, quadBottom] = std::minmax({pts[0].fY, pts[1].fY, pts[2].fY}); // The y-coordinates of the points intersect the top and bottom offsets. if (topOffset <= quadBottom && quadTop <= bottomOffset) { - addQuad(pts, topOffset); - addQuad(pts, bottomOffset); - addPts(pts); + addQuad(topOffset); + addQuad(bottomOffset); + addPts(3); } break; } - case SkPathVerb::kConic: { + case SkPath::kConic_Verb: { SkDEBUGFAIL("There should be no conic primitives in glyph outlines."); break; } - case SkPathVerb::kCubic: { + case SkPath::kCubic_Verb: { auto [cubicTop, cubicBottom] = std::minmax({pts[0].fY, pts[1].fY, pts[2].fY, pts[3].fY}); // The y-coordinates of the points intersect the top and bottom offsets. if (topOffset <= cubicBottom && cubicTop <= bottomOffset) { - addCubic(pts, topOffset); - addCubic(pts, bottomOffset); - addPts(pts); + addCubic(topOffset); + addCubic(bottomOffset); + addPts(4); } break; } - case SkPathVerb::kClose: { + case SkPath::kClose_Verb: { + break; + } + default: { + SkDEBUGFAIL("Unknown path verb generating glyph underline."); break; } } diff --git a/gfx/skia/skia/src/core/SkGlyphRunPainter.cpp b/gfx/skia/skia/src/core/SkGlyphRunPainter.cpp @@ -17,7 +17,6 @@ #include "include/core/SkMatrix.h" #include "include/core/SkPaint.h" #include "include/core/SkPath.h" -#include "include/core/SkPathBuilder.h" #include "include/core/SkPoint.h" #include "include/core/SkRect.h" #include "include/core/SkRefCnt.h" @@ -26,7 +25,6 @@ #include "include/private/base/SkFloatingPoint.h" #include "include/private/base/SkSpan_impl.h" #include "include/private/base/SkTArray.h" -#include "src/core/SkDraw.h" #include "src/core/SkGlyph.h" #include "src/core/SkMask.h" #include "src/core/SkScalerContext.h" @@ -206,20 +204,20 @@ prepare_for_direct_bitmap_drawing(SkStrike* strike, } } // namespace -namespace skcpu { -GlyphRunListPainter::GlyphRunListPainter(const SkSurfaceProps& props, - SkColorType colorType, - SkColorSpace* cs) +// -- SkGlyphRunListPainterCPU --------------------------------------------------------------------- +SkGlyphRunListPainterCPU::SkGlyphRunListPainterCPU(const SkSurfaceProps& props, + SkColorType colorType, + SkColorSpace* cs) : fDeviceProps{props} , fBitmapFallbackProps{props.cloneWithPixelGeometry(kUnknown_SkPixelGeometry)} , fColorType{colorType} , fScalerContextFlags{compute_scaler_context_flags(cs)} {} -void GlyphRunListPainter::drawForBitmapDevice(SkCanvas* canvas, - const BitmapDevicePainter* bitmapDevice, - const sktext::GlyphRunList& glyphRunList, - const SkPaint& paint, - const SkMatrix& drawMatrix) { +void SkGlyphRunListPainterCPU::drawForBitmapDevice(SkCanvas* canvas, + const BitmapDevicePainter* bitmapDevice, + const sktext::GlyphRunList& glyphRunList, + const SkPaint& paint, + const SkMatrix& drawMatrix) { STArray<64, const SkGlyph*> acceptedPackedGlyphIDs; STArray<64, SkPoint> acceptedPositions; STArray<64, SkGlyphID> rejectedGlyphIDs; @@ -290,10 +288,10 @@ void GlyphRunListPainter::drawForBitmapDevice(SkCanvas* canvas, m.setScaleTranslate(strikeToSourceScale, strikeToSourceScale, translate.x(), translate.y()); - SkPathBuilder builder; - builder.addPath(*path, m); - builder.setIsVolatile(true); - canvas->drawPath(builder.detach(), pathPaint); + SkPath deviceOutline; + path->transform(m, &deviceOutline); + deviceOutline.setIsVolatile(true); + canvas->drawPath(deviceOutline, pathPaint); } } } @@ -419,4 +417,3 @@ void GlyphRunListPainter::drawForBitmapDevice(SkCanvas* canvas, // rejects in a more sophisticated stage. } } -} // namespace skcpu diff --git a/gfx/skia/skia/src/core/SkGlyphRunPainter.h b/gfx/skia/skia/src/core/SkGlyphRunPainter.h @@ -5,30 +5,44 @@ * found in the LICENSE file. */ -#ifndef skcpu_GlyphRunPainter_DEFINED -#define skcpu_GlyphRunPainter_DEFINED +#ifndef SkGlyphRunPainter_DEFINED +#define SkGlyphRunPainter_DEFINED +#include "include/core/SkSamplingOptions.h" #include "include/core/SkSurfaceProps.h" +#include "src/base/SkZip.h" #include <cstdint> +class SkBitmap; class SkCanvas; class SkColorSpace; +class SkGlyph; class SkMatrix; class SkPaint; enum SkColorType : int; enum class SkScalerContextFlags : uint32_t; -namespace sktext { -class GlyphRunList; -} +namespace sktext { class GlyphRunList; } +struct SkPoint; +struct SkRect; -namespace skcpu { - -class BitmapDevicePainter; - -class GlyphRunListPainter { +class SkGlyphRunListPainterCPU { public: - GlyphRunListPainter(const SkSurfaceProps& props, SkColorType colorType, SkColorSpace* cs); + class BitmapDevicePainter { + public: + BitmapDevicePainter() = default; + BitmapDevicePainter(const BitmapDevicePainter&) = default; + virtual ~BitmapDevicePainter() = default; + + virtual void paintMasks(SkZip<const SkGlyph*, SkPoint> accepted, + const SkPaint& paint) const = 0; + virtual void drawBitmap(const SkBitmap&, const SkMatrix&, const SkRect* dstOrNull, + const SkSamplingOptions&, const SkPaint&) const = 0; + }; + + SkGlyphRunListPainterCPU(const SkSurfaceProps& props, + SkColorType colorType, + SkColorSpace* cs); void drawForBitmapDevice( SkCanvas* canvas, const BitmapDevicePainter* bitmapDevice, @@ -43,7 +57,4 @@ private: const SkColorType fColorType; const SkScalerContextFlags fScalerContextFlags; }; - -} // namespace skcpu - -#endif // skcpu_GlyphRunPainter_DEFINED +#endif // SkGlyphRunPainter_DEFINED diff --git a/gfx/skia/skia/src/core/SkGraphics.cpp b/gfx/skia/skia/src/core/SkGraphics.cpp @@ -78,27 +78,6 @@ void SkGraphics::PurgePinnedFontCache() { SkStrikeCache::GlobalStrikeCache()->purgePinned(); } -size_t SkGraphics::GetResourceCacheTotalBytesUsed() { return SkResourceCache::GetTotalBytesUsed(); } - -size_t SkGraphics::GetResourceCacheTotalByteLimit() { return SkResourceCache::GetTotalByteLimit(); } - -size_t SkGraphics::SetResourceCacheTotalByteLimit(size_t newLimit) { - return SkResourceCache::SetTotalByteLimit(newLimit); -} - -size_t SkGraphics::GetResourceCacheSingleAllocationByteLimit() { - return SkResourceCache::GetSingleAllocationByteLimit(); -} - -size_t SkGraphics::SetResourceCacheSingleAllocationByteLimit(size_t newLimit) { - return SkResourceCache::SetSingleAllocationByteLimit(newLimit); -} - -void SkGraphics::PurgeResourceCache() { - SkImageFilter_Base::PurgeCache(); - return SkResourceCache::PurgeAll(); -} - static int gTypefaceCacheCountLimit = 1024; // historical default value int SkGraphics::GetTypefaceCacheCountLimit() { diff --git a/gfx/skia/skia/src/core/SkImageFilter.cpp b/gfx/skia/skia/src/core/SkImageFilter.cpp @@ -103,7 +103,7 @@ bool SkImageFilter_Base::affectsTransparentBlack() const { if (this->onAffectsTransparentBlack()) { return true; } else if (this->ignoreInputsAffectsTransparentBlack()) { - // TODO(skbug.com/40045513): Automatically infer this from output bounds being finite + // TODO(skbug.com/14611): Automatically infer this from output bounds being finite return false; } for (int i = 0; i < this->countInputs(); i++) { diff --git a/gfx/skia/skia/src/core/SkImageFilterCache.cpp b/gfx/skia/skia/src/core/SkImageFilterCache.cpp @@ -158,11 +158,12 @@ sk_sp<SkImageFilterCache> SkImageFilterCache::Create(size_t maxBytes) { sk_sp<SkImageFilterCache> SkImageFilterCache::Get(CreateIfNecessary createIfNecessary) { static SkOnce once; - static SkImageFilterCache* cache = nullptr; + static sk_sp<SkImageFilterCache> cache; if (createIfNecessary == CreateIfNecessary::kNo) { - return sk_ref_sp(cache); + return cache; } - once([] { cache = SkImageFilterCache::Create(kDefaultCacheSize).release(); }); - return sk_ref_sp(cache); + + once([]{ cache = SkImageFilterCache::Create(kDefaultCacheSize); }); + return cache; } diff --git a/gfx/skia/skia/src/core/SkImageFilterTypes.cpp b/gfx/skia/skia/src/core/SkImageFilterTypes.cpp @@ -13,6 +13,7 @@ #include "include/core/SkCanvas.h" #include "include/core/SkClipOp.h" #include "include/core/SkColor.h" +#include "include/core/SkColorType.h" #include "include/core/SkImage.h" #include "include/core/SkImageInfo.h" #include "include/core/SkM44.h" @@ -204,9 +205,16 @@ public: return SkImages::RasterFromBitmap(data); } +#if defined(SK_USE_LEGACY_BLUR_RASTER) + const SkBlurEngine* getBlurEngine() const override { return nullptr; } +#else + bool useLegacyFilterResultBlur() const override { return false; } + const SkBlurEngine* getBlurEngine() const override { return SkBlurEngine::GetRasterBlurEngine(); } +#endif + }; } // anonymous namespace @@ -223,6 +231,11 @@ Backend::Backend(sk_sp<SkImageFilterCache> cache, Backend::~Backend() = default; sk_sp<Backend> MakeRasterBackend(const SkSurfaceProps& surfaceProps, SkColorType colorType) { + // TODO (skbug:14286): Remove this forcing to 8888. Many legacy image filters only support + // N32 on CPU, but once they are implemented in terms of draws and SkSL they will support + // all color types, like the GPU backends. + colorType = kN32_SkColorType; + return sk_make_sp<RasterBackend>(surfaceProps, colorType); } @@ -349,23 +362,29 @@ SkIRect Mapping::map<SkIRect>(const SkIRect& geom, const SkMatrix& matrix) { template<> SkIPoint Mapping::map<SkIPoint>(const SkIPoint& geom, const SkMatrix& matrix) { - SkPoint p = matrix.mapPoint({SkIntToScalar(geom.fX), SkIntToScalar(geom.fY)}); + SkPoint p = SkPoint::Make(SkIntToScalar(geom.fX), SkIntToScalar(geom.fY)); + matrix.mapPoints(&p, 1); return SkIPoint::Make(SkScalarRoundToInt(p.fX), SkScalarRoundToInt(p.fY)); } template<> SkPoint Mapping::map<SkPoint>(const SkPoint& geom, const SkMatrix& matrix) { - return matrix.mapPoint(geom); + SkPoint p; + matrix.mapPoints(&p, &geom, 1); + return p; } template<> Vector Mapping::map<Vector>(const Vector& geom, const SkMatrix& matrix) { - return Vector(matrix.mapVector({geom.fX, geom.fY})); + SkVector v = SkVector::Make(geom.fX, geom.fY); + matrix.mapVectors(&v, 1); + return Vector{v}; } template<> IVector Mapping::map<IVector>(const IVector& geom, const SkMatrix& matrix) { - const SkVector v = matrix.mapVector({SkIntToScalar(geom.fX), SkIntToScalar(geom.fY)}); + SkVector v = SkVector::Make(SkIntToScalar(geom.fX), SkIntToScalar(geom.fY)); + matrix.mapVectors(&v, 1); return IVector(SkScalarRoundToInt(v.fX), SkScalarRoundToInt(v.fY)); } @@ -398,7 +417,8 @@ SkMatrix Mapping::map<SkMatrix>(const SkMatrix& m, const SkMatrix& matrix) { // operates on, and outputs to, the C1 coord space, we want to return a new matrix that is // equivalent to 'm' that operates on and outputs to C2. This is the same as mapping the input // from C2 to C1 (matrix^-1), then transforming by 'm', and then mapping from C1 to C2 (matrix). - SkMatrix inv = matrix.invert().value_or(SkMatrix()); + SkMatrix inv; + SkAssertResult(matrix.invert(&inv)); inv.postConcat(m); inv.postConcat(matrix); return inv; @@ -1436,7 +1456,7 @@ sk_sp<SkShader> FilterResult::getAnalyzedShaderView( if (analysis & BoundsAnalysis::kRequiresDecalInLayerSpace) { SkASSERT(fTileMode == SkTileMode::kDecal); - // TODO(skbug.com/40043877) - As part of fully supporting subsets in image shaders, it probably + // TODO(skbug:12784) - As part of fully supporting subsets in image shaders, it probably // makes sense to share the subset tiling logic that's in GrTextureEffect as dedicated // SkShaders. Graphite can then add those to its program as-needed vs. always doing // shader-based tiling, and CPU can have raster-pipeline tiling applied more flexibly than @@ -1543,7 +1563,7 @@ void draw_tiled_border(SkCanvas* canvas, // 8 draws should be batchable with the primary fill that had used `paint`. auto drawEdge = [&](const SkRect& src, const SkRect& dst) { canvas->save(); - canvas->concat(SkMatrix::RectToRectOrIdentity(src, dst)); + canvas->concat(SkMatrix::RectToRect(src, dst)); canvas->drawRect(src, paint); canvas->restore(); }; @@ -1616,8 +1636,7 @@ void draw_tiled_border(SkCanvas* canvas, FilterResult FilterResult::rescale(const Context& ctx, const LayerSpace<SkSize>& scale, - bool enforceDecal, - bool allowOverscaling) const { + bool enforceDecal) const { LayerSpace<SkIRect> visibleLayerBounds = fLayerBounds; if (!fImage || !visibleLayerBounds.intersect(ctx.desiredOutput()) || scale.width() <= 0.f || scale.height() <= 0.f) { @@ -1736,37 +1755,37 @@ FilterResult FilterResult::rescale(const Context& ctx, } } + // For now, if we are deferring periodic tiling, we need to ensure that the low-res image bounds + // are pixel aligned. This is because the tiling is applied at the pixel level in SkImageShader, + // and we need the period of the low-res image to align with the original high-resolution period + // If/when SkImageShader supports shader-tiling over fractional bounds, this can relax. + float finalScaleX = xSteps > 0 ? scale.width() : 1.f; + float finalScaleY = ySteps > 0 ? scale.height() : 1.f; if (deferPeriodicTiling) { + PixelSpace<SkRect> dstBoundsF = scale_about_center(stepBoundsF, finalScaleX, finalScaleY); + // Use a pixel bounds that's smaller than what was requested to ensure any post-blur amount + // is lower than the max supported. In the event that roundIn() would collapse to an empty + // rect, use a 1x1 bounds that contains the center point. + PixelSpace<SkIRect> innerDstPixels = dstBoundsF.roundIn(); + int dstCenterX = sk_float_floor2int(0.5f * dstBoundsF.right() + 0.5f * dstBoundsF.left()); + int dstCenterY = sk_float_floor2int(0.5f * dstBoundsF.bottom() + 0.5f * dstBoundsF.top()); + dstBoundsF = PixelSpace<SkRect>({(float) std::min(dstCenterX, innerDstPixels.left()), + (float) std::min(dstCenterY, innerDstPixels.top()), + (float) std::max(dstCenterX+1, innerDstPixels.right()), + (float) std::max(dstCenterY+1, innerDstPixels.bottom())}); + + finalScaleX = dstBoundsF.width() / srcRect.width(); + finalScaleY = dstBoundsF.height() / srcRect.height(); + + // Recompute how many steps are needed, as we may need to do one more step from the round-in + xSteps = downscale_step_count(finalScaleX); + ySteps = downscale_step_count(finalScaleY); + // The periodic tiling effect will be manually rendered into the lower resolution image so // that clamp tiling can be used at each decimation. image.fTileMode = SkTileMode::kClamp; - } else { - // When not deferring periodic tiling, it provides a better user behavior for animating - // sigma values and matrix scale factors to not overscale to the next factor of 1/2 and just - // scale the requisite amount between 1/2 and 1 for the final step. - // - // This can lead to some slight flickering when content animates underneath a fixed blur - // region, but this scenario is most likely to occur with backdrop filters. Backdrop filters - // generally use kMirror for their boundary condition so would hit the periodic tiling case - // anyways. - // - // The long term solution to address all of these issues is to be able to track bounds and - // image placement in floating point, and blend over and underscaled images into an image - // of the exact required size. - allowOverscaling = false; } - // For now, if we are deferring periodic tiling, we need to ensure that the low-res image bounds - // are pixel aligned. This is because the tiling is applied at the pixel level in SkImageShader, - // and we need the period of the low-res image to align with the original high-resolution period - // If/when SkImageShader supports shader-tiling over fractional bounds, this can relax. - float finalScaleX = xSteps > 0 ? (allowOverscaling ? (1.f / (1 << xSteps)) - : scale.width()) - : 1.f; - float finalScaleY = ySteps > 0 ? (allowOverscaling ? (1.f / (1 << ySteps)) - : scale.height()) - : 1.f; - do { float sx = 1.f; if (xSteps > 0) { @@ -1783,16 +1802,6 @@ FilterResult FilterResult::rescale(const Context& ctx, // Downscale relative to the center of the image, which better distributes any sort of // sampling errors across the image (vs. emphasizing the bottom right edges). PixelSpace<SkRect> dstBoundsF = scale_about_center(stepBoundsF, sx, sy); - const bool finalXStep = xSteps == 0 && sx != 1.f; - const bool finalYStep = ySteps == 0 && sy != 1.f; - if (deferPeriodicTiling && (finalXStep || finalYStep)) { - PixelSpace<SkIRect> dstPixels = dstBoundsF.roundOut(); - dstBoundsF = PixelSpace<SkRect>({ - finalXStep ? (float) dstPixels.left() : dstBoundsF.left(), - finalYStep ? (float) dstPixels.top() : dstBoundsF.top(), - finalXStep ? (float) dstPixels.right() : dstBoundsF.right(), - finalYStep ? (float) dstPixels.bottom() : dstBoundsF.bottom()}); - } // NOTE: Rounding out is overly conservative when dstBoundsF has an odd integer width/height // but with coordinates at 1/2. In this case, we could create a pixel grid that has a @@ -1954,7 +1963,7 @@ FilterResult FilterResult::MakeFromImage(const Context& ctx, SkRect imageBounds = SkRect::Make(image->dimensions()); if (!imageBounds.contains(srcRect)) { - SkMatrix srcToDst = SkMatrix::RectToRectOrIdentity(srcRect, SkRect(dstRect)); + SkMatrix srcToDst = SkMatrix::RectToRect(srcRect, SkRect(dstRect)); if (!srcRect.intersect(imageBounds)) { return {}; // No overlap, so return an empty/transparent image } @@ -2017,7 +2026,7 @@ SkSpan<sk_sp<SkShader>> 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). - layerToParam = fContext.mapping().layerMatrix().asM33().invert().value_or(SkMatrix()); + 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<SkMatrix>(layerToParam))) { @@ -2108,6 +2117,8 @@ FilterResult FilterResult::Builder::merge() { FilterResult FilterResult::Builder::blur(const LayerSpace<SkSize>& sigma) { SkASSERT(fInputs.size() == 1); + // TODO: The blur functor is only supported for GPU contexts; SkBlurImageFilter should have + // detected this. const SkBlurEngine* blurEngine = fContext.backend()->getBlurEngine(); SkASSERT(blurEngine); @@ -2134,19 +2145,33 @@ FilterResult FilterResult::Builder::blur(const LayerSpace<SkSize>& sigma) { auto sampleBounds = outputBounds; sampleBounds.outset(radii); + if (fContext.backend()->useLegacyFilterResultBlur()) { + SkASSERT(sigma.width() <= algorithm->maxSigma() && sigma.height() <= algorithm->maxSigma()); + + FilterResult resolved = fInputs[0].fImage.resolve(fContext, sampleBounds); + if (!resolved) { + return {}; + } + auto srcRelativeOutput = outputBounds; + srcRelativeOutput.offset(-resolved.layerBounds().topLeft()); + resolved = {algorithm->blur(SkSize(sigma), + resolved.fImage, + SkIRect::MakeSize(resolved.fImage->dimensions()), + SkTileMode::kDecal, + SkIRect(srcRelativeOutput)), + outputBounds.topLeft()}; + return resolved; + } + float sx = sigma.width() > algorithm->maxSigma() ? algorithm->maxSigma()/sigma.width() : 1.f; float sy = sigma.height() > algorithm->maxSigma() ? algorithm->maxSigma()/sigma.height() : 1.f; // For identity scale factors, this rescale() is a no-op when possible, but otherwise it will // also handle resolving any color filters or transform similar to a resolve() except that it // can defer the tile mode. - // - // Always allow overscaling for higher quality filtering, and because we can adjust the blur - // sigma applied to the low res image to account for any extra scale factor. FilterResult lowResImage = fInputs[0].fImage.rescale( fContext.withNewDesiredOutput(sampleBounds), LayerSpace<SkSize>({sx, sy}), - algorithm->supportsOnlyDecalTiling(), - /*allowOverscaling=*/true); + algorithm->supportsOnlyDecalTiling()); if (!lowResImage) { return {}; } diff --git a/gfx/skia/skia/src/core/SkImageFilterTypes.h b/gfx/skia/skia/src/core/SkImageFilterTypes.h @@ -497,7 +497,7 @@ public: static LayerSpace<SkMatrix> RectToRect(const LayerSpace<SkRect>& from, const LayerSpace<SkRect>& to) { - return LayerSpace<SkMatrix>(SkMatrix::RectToRectOrIdentity(SkRect(from), SkRect(to))); + return LayerSpace<SkMatrix>(SkMatrix::RectToRect(SkRect(from), SkRect(to))); } // Parrot a limited selection of the SkMatrix API while preserving coordinate space. @@ -524,13 +524,7 @@ public: } bool invert(LayerSpace<SkMatrix>* inverse) const { - if (auto inv = fData.invert()) { - if (inverse) { - inverse->fData = *inv; - } - return true; - } - return false; + return fData.invert(inverse ? &inverse->fData : nullptr); } // Transforms 'r' by the inverse of this matrix if it is invertible and stores it in 'out'. @@ -629,10 +623,11 @@ public: // 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. - if (auto devToLayer33 = fLayerToDevMatrix.asM33().invert()) { - return LayerSpace<T>(map(static_cast<const T&>(devGeometry), *devToLayer33)); + SkMatrix devToLayer33; + if (!fLayerToDevMatrix.asM33().invert(&devToLayer33)) { + return LayerSpace<T>::Empty(); } - return LayerSpace<T>::Empty(); + return LayerSpace<T>(map(static_cast<const T&>(devGeometry), devToLayer33)); } template<typename T> @@ -931,16 +926,12 @@ private: // and may also have a deferred tilemode. If 'enforceDecal' is true, the returned // FilterResult will be kDecal sampled and any tiling will already be applied. // - // If `allowOverscaling` is true, the returned image may be scaled beyond what's requested in - // `scale` to remain a multiple of 1/2X steps. - // // All deferred effects, other than potentially tile mode, will be applied. The FilterResult // will also be converted to the color type and color space of 'ctx' so the result is suitable // to pass to the blur engine. FilterResult rescale(const Context& ctx, const LayerSpace<SkSize>& scale, - bool enforceDecal, - bool allowOverscaling) const; + bool enforceDecal) const; // Draw directly to the device, which draws the same image as produced by resolve() but can be // useful if multiple operations need to be performed on the canvas. // @@ -1117,6 +1108,10 @@ public: // TODO: Once all Backends provide a blur engine, maybe just have Backend extend it. virtual const SkBlurEngine* getBlurEngine() const = 0; + // TODO: Can be removed once all blur engines rely on FilterResult::rescale and not their own + // rescale implementations. + virtual bool useLegacyFilterResultBlur() const { return true; } + // Properties controlling the pixel data for offscreen surfaces rendered to during filtering. const SkSurfaceProps& surfaceProps() const { return fSurfaceProps; } SkColorType colorType() const { return fColorType; } diff --git a/gfx/skia/skia/src/core/SkLRUCache.h b/gfx/skia/skia/src/core/SkLRUCache.h @@ -24,12 +24,11 @@ template <typename K, typename V, typename HashK = SkGoodHash, typename PurgeCB class SkLRUCache { private: struct Entry { - template<typename K1, typename V1> - Entry(K1&& key, V1&& value) - : fKey(std::forward<K1>(key)) - , fValue(std::forward<V1>(value)) {} + Entry(const K& key, V&& value) + : fKey(key) + , fValue(std::move(value)) {} - const K fKey; + K fKey; V fValue; SK_DECLARE_INTERNAL_LLIST_INTERFACE(Entry); @@ -67,7 +66,10 @@ public: return &entry->fValue; } - V* insert(Entry* entry) { + V* insert(const K& key, V value) { + SkASSERT(!this->find(key)); + + Entry* entry = new Entry(key, std::move(value)); fMap.set(entry); fLRU.addToHead(entry); while (fMap.count() > fMaxCount) { @@ -76,26 +78,20 @@ public: return &entry->fValue; } - template<typename K1, typename V1> - V* insert(K1&& key, V1&& value) { - SkASSERT(!this->find(key)); - return this->insert(new Entry(std::forward<K1>(key), std::forward<V1>(value))); - } - - template<typename K1, typename V1> - V* insert_or_update(K1&& key, V1&& value) { + V* insert_or_update(const K& key, V value) { if (V* found = this->find(key)) { - *found = std::forward<V1>(value); + *found = std::move(value); return found; + } else { + return this->insert(key, std::move(value)); } - return this->insert(new Entry(std::forward<K1>(key), std::forward<V1>(value))); } int count() const { return fMap.count(); } - template <typename Fn> // f(const K*, V*) + template <typename Fn> // f(K*, V*) void foreach(Fn&& fn) { typename SkTInternalLList<Entry>::Iter iter; for (Entry* e = iter.init(fLRU, SkTInternalLList<Entry>::Iter::kHead_IterStart); e; diff --git a/gfx/skia/skia/src/core/SkLineClipper.cpp b/gfx/skia/skia/src/core/SkLineClipper.cpp @@ -75,7 +75,7 @@ static SkScalar sect_clamp_with_vertical(const SkPoint src[2], SkScalar x) { // Our caller expects y to be between src[0].fY and src[1].fY (unsorted), but due to the // numerics of floats/doubles, we might have computed a value slightly outside of that, // so we have to manually clamp afterwards. - // See skbug.com/40038736 + // See skbug.com/7491 return pin_unsorted(y, src[0].fY, src[1].fY); } diff --git a/gfx/skia/skia/src/core/SkLocalMatrixImageFilter.cpp b/gfx/skia/skia/src/core/SkLocalMatrixImageFilter.cpp @@ -27,10 +27,12 @@ sk_sp<SkImageFilter> SkLocalMatrixImageFilter::Make(const SkMatrix& localMatrix, return nullptr; } - if (auto invLocal = localMatrix.invert()) { - return sk_sp<SkImageFilter>(new SkLocalMatrixImageFilter(localMatrix, *invLocal, &input)); + SkMatrix invLocal; + if (!localMatrix.invert(&invLocal)) { + return nullptr; } - return nullptr; + + return sk_sp<SkImageFilter>(new SkLocalMatrixImageFilter(localMatrix, invLocal, &input)); } sk_sp<SkFlattenable> SkLocalMatrixImageFilter::CreateProc(SkReadBuffer& buffer) { diff --git a/gfx/skia/skia/src/core/SkMaskFilterBase.cpp b/gfx/skia/skia/src/core/SkMaskFilterBase.cpp @@ -29,7 +29,6 @@ #include <cstdint> #include <optional> -class SkPaint; class SkRRect; SkMaskFilterBase::NinePatch::~NinePatch() { @@ -46,9 +45,8 @@ bool SkMaskFilterBase::asABlur(BlurRec*) const { return false; } -std::pair<sk_sp<SkImageFilter>, bool> SkMaskFilterBase::asImageFilter(const SkMatrix& ctm, - const SkPaint& paint) const { - return std::make_pair(nullptr, false); +sk_sp<SkImageFilter> SkMaskFilterBase::asImageFilter(const SkMatrix& ctm) const { + return nullptr; } static SkMask extract_mask_subset(const SkMask& src, SkIRect bounds, int32_t newX, int32_t newY) { @@ -203,27 +201,19 @@ static void draw_nine(const SkMask& mask, const SkIRect& outerR, const SkIPoint& } } -static int countNestedRects(const SkPathRaw& raw, SkRect rects[2]) { - if (SkPathPriv::IsNestedFillRects(raw, rects)) { +static int countNestedRects(const SkPath& path, SkRect rects[2]) { + if (SkPathPriv::IsNestedFillRects(path, rects)) { return 2; } - if (auto r = raw.isRect()) { - rects[0] = *r; - return 1; - } - return 0; + return path.isRect(&rects[0]); } -bool SkMaskFilterBase::filterRRect(const SkRRect& devRRect, - const SkMatrix& matrix, - const SkRasterClip& clip, - SkBlitter* blitter, - SkResourceCache* cache) const { +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<NinePatch> patch = - this->filterRRectToNine(devRRect, matrix, clip.getBounds(), cache); + std::optional<NinePatch> patch = this->filterRRectToNine(devRRect, matrix, clip.getBounds()); if (!patch.has_value()) { return false; @@ -232,51 +222,31 @@ bool SkMaskFilterBase::filterRRect(const SkRRect& devRRect, return true; } -SkMaskFilterBase::FilterReturn SkMaskFilterBase::filterRects(SkSpan<const SkRect> devRects, - const SkMatrix& matrix, - const SkRasterClip& clip, - SkBlitter* blitter, - SkResourceCache* cache) const { - std::optional<NinePatch> patch; - - FilterReturn filterReturn = this->filterRectsToNine( - devRects, matrix, clip.getBounds(), &patch, cache); - switch (filterReturn) { - case FilterReturn::kFalse: - SkASSERT(!patch.has_value()); - break; - - case FilterReturn::kTrue: - draw_nine(patch->fMask, patch->fOuterRect, patch->fCenter, 1 == devRects.size(), clip, - blitter); - break; - - case FilterReturn::kUnimplemented: - SkASSERT(!patch.has_value()); - // fall out - break; - } - return filterReturn; -} - -bool SkMaskFilterBase::filterPath(const SkPathRaw& devRaw, - const SkMatrix& matrix, - const SkRasterClip& clip, - SkBlitter* blitter, - SkStrokeRec::InitStyle style, - SkResourceCache* cache) const { +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(devRaw, rects); + rectCount = countNestedRects(devPath, rects); } if (rectCount > 0) { - switch (this->filterRects(SkSpan(rects, rectCount), matrix, clip, blitter, cache)) { + std::optional<NinePatch> 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; } } @@ -288,13 +258,9 @@ bool SkMaskFilterBase::filterPath(const SkPathRaw& devRaw, return false; } #endif - if (!skcpu::DrawToMask(devRaw, - clip.getBounds(), - this, - &matrix, - &srcM, - SkMaskBuilder::kComputeBoundsAndRenderImage_CreateMode, - style)) { + if (!SkDraw::DrawToMask(devPath, clip.getBounds(), this, &matrix, &srcM, + SkMaskBuilder::kComputeBoundsAndRenderImage_CreateMode, + style)) { return false; } SkAutoMaskFreeImage autoSrc(srcM.image()); @@ -322,15 +288,15 @@ bool SkMaskFilterBase::filterPath(const SkPathRaw& devRaw, } std::optional<SkMaskFilterBase::NinePatch> SkMaskFilterBase::filterRRectToNine( - const SkRRect&, const SkMatrix&, const SkIRect&, SkResourceCache*) const { + const SkRRect&, const SkMatrix&, const SkIRect&) const { return std::nullopt; } -SkMaskFilterBase::FilterReturn SkMaskFilterBase::filterRectsToNine(SkSpan<const SkRect>, - const SkMatrix&, - const SkIRect&, - std::optional<NinePatch>*, - SkResourceCache*) const { +SkMaskFilterBase::FilterReturn SkMaskFilterBase::filterRectsToNine( + SkSpan<const SkRect>, + const SkMatrix&, + const SkIRect&, + std::optional<NinePatch>*) const { return FilterReturn::kUnimplemented; } diff --git a/gfx/skia/skia/src/core/SkMaskFilterBase.h b/gfx/skia/skia/src/core/SkMaskFilterBase.h @@ -20,23 +20,16 @@ #include "src/core/SkMask.h" #include <optional> -#include <utility> -class SkPaint; class SkBlitter; class SkImageFilter; class SkCachedData; class SkMatrix; -class SkResourceCache; -struct SkPathRaw; +class SkPath; class SkRRect; class SkRasterClip; enum SkBlurStyle : int; -namespace skcpu { -class Draw; -} - class SkMaskFilterBase : public SkMaskFilter { public: /** Returns the format of the resulting mask that this subclass will return @@ -95,20 +88,10 @@ public: virtual bool asABlur(BlurRec*) const; /** - * Return an SkImageFilter representation of this mask filter that SkCanvas can apply - * to an alpha-only image to produce an equivalent effect to running the mask filter directly. - * - * Additionally, return a boolean that indicates if the image filter applies shading properties. - * When restoring a layer, this affects whether to draw a rgba image or blend the coverage - * mask (A8 image). - * - * The paint parameter can be used to apply shading. Some mask filters (e.g. EmbossMaskFilter) - * may not produce correct results under these circumstances and different blend modes, - * given that the coverage mask will be blended in the mask filter as image filter impl in - * these cases. + * Return an SkImageFilter representation of this mask filter that SkCanvas can apply to an + * alpha-only image to produce an equivalent effect to running the mask filter directly. */ - virtual std::pair<sk_sp<SkImageFilter>, bool> asImageFilter(const SkMatrix& ctm, - const SkPaint& paint) const; + virtual sk_sp<SkImageFilter> asImageFilter(const SkMatrix& ctm) const; static SkFlattenable::Type GetFlattenableType() { return kSkMaskFilter_Type; @@ -163,30 +146,25 @@ protected: virtual FilterReturn filterRectsToNine(SkSpan<const SkRect>, const SkMatrix&, const SkIRect& clipBounds, - std::optional<NinePatch>*, - SkResourceCache*) const; + std::optional<NinePatch>*) const; /** * Similar to filterRectsToNine, except it performs the work on a round rect. */ virtual std::optional<NinePatch> filterRRectToNine(const SkRRect&, const SkMatrix&, - const SkIRect& clipBounds, - SkResourceCache*) const; + const SkIRect& clipBounds) const; private: - friend class skcpu::Draw; + friend class SkDraw; + friend class SkDrawBase; - /** Helper method that, given a raw path in device space, will rasterize it into a - kA8_Format 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. + /** Helper method that, given a path in device space, will rasterize it into a kA8_Format 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. This method is not exported to java. */ - bool filterPath(const SkPathRaw& devRaw, - const SkMatrix& ctm, - const SkRasterClip&, - SkBlitter*, - SkStrokeRec::InitStyle, - SkResourceCache*) const; + bool filterPath(const SkPath& devPath, const SkMatrix& ctm, const SkRasterClip&, SkBlitter*, + SkStrokeRec::InitStyle) const; /** Helper method that, given a roundRect in device space, will rasterize it into a kA8_Format mask and then call filterMask(). If this returns true, the specified blitter will be called @@ -195,14 +173,7 @@ private: bool filterRRect(const SkRRect& devRRect, const SkMatrix& ctm, const SkRasterClip&, - SkBlitter*, - SkResourceCache*) const; - - FilterReturn filterRects(SkSpan<const SkRect> devRects, - const SkMatrix& ctm, - const SkRasterClip& clip, - SkBlitter* blitter, - SkResourceCache* cache) const; + SkBlitter*) const; }; inline SkMaskFilterBase* as_MFB(SkMaskFilter* mf) { diff --git a/gfx/skia/skia/src/core/SkMatrix.cpp b/gfx/skia/skia/src/core/SkMatrix.cpp @@ -7,7 +7,7 @@ #include "include/core/SkMatrix.h" -#include "include/core/SkPathBuilder.h" +#include "include/core/SkPath.h" #include "include/core/SkPoint3.h" #include "include/core/SkRSXform.h" #include "include/core/SkSamplingOptions.h" @@ -27,10 +27,6 @@ #include <algorithm> #include <cmath> -template <typename S, typename T> int min_count(SkSpan<S> a, SkSpan<T> b) { - return SkToInt(std::min(a.size(), b.size())); -} - void SkMatrix::doNormalizePerspective() { // If the bottom row of the matrix is [0, 0, not_one], we will treat the matrix as if it // is in perspective, even though it stills behaves like its affine. If we divide everything @@ -539,63 +535,55 @@ SkMatrix& SkMatrix::postSkew(SkScalar sx, SkScalar sy) { /////////////////////////////////////////////////////////////////////////////// -SkMatrix SkMatrix::ScaleTranslate(float sx, float sy, float tx, float ty) { - uint8_t mask = 0; - if (sx != 1 || sy != 1) { - mask |= SkMatrix::kScale_Mask; - } - if (tx != 0.0f || ty != 0.0f) { - mask |= SkMatrix::kTranslate_Mask; - } - if (sx != 0 && sy != 0) { - mask |= SkMatrix::kRectStaysRect_Mask; - } - return SkMatrix(sx, 0, tx, - 0, sy, ty, - 0, 0, 1, - mask); -} - -std::optional<SkMatrix> SkMatrix::Rect2Rect(const SkRect& src, const SkRect& dst, ScaleToFit stf) { +bool SkMatrix::setRectToRect(const SkRect& src, const SkRect& dst, ScaleToFit align) { if (src.isEmpty()) { - return {}; + this->reset(); + return false; } - SkScalar tx, sx = sk_ieee_float_divide(dst.width(), src.width()); - SkScalar ty, sy = sk_ieee_float_divide(dst.height(), src.height()); - bool xLarger = false; - - if (stf != kFill_ScaleToFit) { - if (sx > sy) { - xLarger = true; - sx = sy; - } else { - sy = sx; + if (dst.isEmpty()) { + sk_bzero(fMat, 8 * sizeof(SkScalar)); + fMat[kMPersp2] = 1; + this->setTypeMask(kScale_Mask); + } else { + SkScalar tx, sx = sk_ieee_float_divide(dst.width(), src.width()); + SkScalar ty, sy = sk_ieee_float_divide(dst.height(), src.height()); + bool xLarger = false; + + if (align != kFill_ScaleToFit) { + if (sx > sy) { + xLarger = true; + sx = sy; + } else { + sy = sx; + } } - } - tx = dst.fLeft - src.fLeft * sx; - ty = dst.fTop - src.fTop * sy; - if (stf == kCenter_ScaleToFit || stf == kEnd_ScaleToFit) { - SkScalar diff; + tx = dst.fLeft - src.fLeft * sx; + ty = dst.fTop - src.fTop * sy; + if (align == kCenter_ScaleToFit || align == kEnd_ScaleToFit) { + SkScalar diff; - if (xLarger) { - diff = dst.width() - src.width() * sy; - } else { - diff = dst.height() - src.height() * sy; - } + if (xLarger) { + diff = dst.width() - src.width() * sy; + } else { + diff = dst.height() - src.height() * sy; + } - if (stf == kCenter_ScaleToFit) { - diff = SkScalarHalf(diff); - } + if (align == kCenter_ScaleToFit) { + diff = SkScalarHalf(diff); + } - if (xLarger) { - tx += diff; - } else { - ty += diff; + if (xLarger) { + tx += diff; + } else { + ty += diff; + } } + + this->setScaleTranslate(sx, sy, tx, ty); } - return ScaleTranslate(sx, sy, tx, ty); + return true; } /////////////////////////////////////////////////////////////////////////////// @@ -779,9 +767,16 @@ bool SkMatrix::asAffine(SkScalar affine[6]) const { return true; } -void SkMatrix::mapPoints(SkSpan<SkPoint> dst, SkSpan<const SkPoint> src) const { - const auto count = min_count(dst, src); - this->getMapPtsProc()(*this, dst.data(), src.data(), count); +void SkMatrix::mapPoints(SkPoint dst[], const SkPoint src[], int count) const { + SkASSERT((dst && src && count > 0) || 0 == count); + // no partial overlap + SkASSERT(src == dst || &dst[count] <= &src[0] || &src[count] <= &dst[0]); + this->getMapPtsProc()(*this, dst, src, count); +} + +void SkMatrix::mapXY(SkScalar x, SkScalar y, SkPoint* result) const { + SkASSERT(result); + this->getMapXYProc()(*this, x, y, result); } void SkMatrix::ComputeInv(SkScalar dst[9], const SkScalar src[9], double invDet, bool isPersp) { @@ -815,12 +810,10 @@ void SkMatrix::ComputeInv(SkScalar dst[9], const SkScalar src[9], double invDet, } } -std::optional<SkMatrix> SkMatrix::invert() const { - TypeMask mask = this->getType(); +bool SkMatrix::invertNonIdentity(SkMatrix* inv) const { + SkASSERT(!this->isIdentity()); - if (mask == kIdentity_Mask) { - return *this; - } + TypeMask mask = this->getType(); // Optimized invert for only scale and/or translation matrices. if (0 == (mask & ~(kScale_Mask | kTranslate_Mask))) { @@ -831,63 +824,74 @@ std::optional<SkMatrix> SkMatrix::invert() const { // 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 {}; + 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 {}; + return false; } - SkMatrix inv; - inv.fMat[kMSkewX] = inv.fMat[kMSkewY] = - inv.fMat[kMPersp0] = inv.fMat[kMPersp1] = 0; + // 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] = invSX; - inv.fMat[kMScaleY] = invSY; - inv.fMat[kMPersp2] = 1; - inv.fMat[kMTransX] = invTX; - inv.fMat[kMTransY] = invTY; + inv->fMat[kMScaleX] = invSX; + inv->fMat[kMScaleY] = invSY; + inv->fMat[kMPersp2] = 1; + inv->fMat[kMTransX] = invTX; + inv->fMat[kMTransY] = invTY; - inv.setTypeMask(mask | kRectStaysRect_Mask); - return inv; + inv->setTypeMask(mask | kRectStaysRect_Mask); + } + + return true; } // Translate-only if (!SkIsFinite(fMat[kMTransX], fMat[kMTransY])) { // Translation components aren't finite, so inverse isn't possible - return {}; + return false; } - return SkMatrix::Translate(-fMat[kMTransX], -fMat[kMTransY]); + if (inv) { + inv->setTranslate(-fMat[kMTransX], -fMat[kMTransY]); + } + return true; } int isPersp = mask & kPerspective_Mask; double invDet = sk_inv_determinant(fMat, isPersp); if (invDet == 0) { // underflow - return {}; + return false; } - SkMatrix inv; - ComputeInv(inv.fMat, fMat, invDet, isPersp); - if (!inv.isFinite()) { - return {}; + bool applyingInPlace = (inv == this); + + SkMatrix* tmp = inv; + + SkMatrix storage; + if (applyingInPlace || nullptr == tmp) { + tmp = &storage; // we either need to avoid trampling memory or have no memory } - inv.setTypeMask(fTypeMask); - return inv; -} -SkPoint SkMatrix::mapPointPerspective(SkPoint p) const { - SkScalar x = sdot(p.fX, fMat[kMScaleX], p.fY, fMat[kMSkewX]) + fMat[kMTransX]; - SkScalar y = sdot(p.fX, fMat[kMSkewY], p.fY, fMat[kMScaleY]) + fMat[kMTransY]; - SkScalar z = sdot(p.fX, fMat[kMPersp0], p.fY, fMat[kMPersp1]) + fMat[kMPersp2]; - if (z) { - z = 1 / z; + ComputeInv(tmp->fMat, fMat, invDet, isPersp); + if (!tmp->isFinite()) { + return false; } - return {x * z, y * z}; + + tmp->setTypeMask(fTypeMask); + + if (applyingInPlace) { + *inv = storage; // need to copy answer back + } + + return true; } /////////////////////////////////////////////////////////////////////////////// @@ -1071,15 +1075,12 @@ void SkMatrixPriv::MapHomogeneousPointsWithStride(const SkMatrix& mx, SkPoint3 d } } -void SkMatrix::mapHomogeneousPoints(SkSpan<SkPoint3> dst, SkSpan<const SkPoint3> src) const { - const auto count = min_count(dst, src); - SkMatrixPriv::MapHomogeneousPointsWithStride(*this, dst.data(), sizeof(SkPoint3), src.data(), +void SkMatrix::mapHomogeneousPoints(SkPoint3 dst[], const SkPoint3 src[], int count) const { + SkMatrixPriv::MapHomogeneousPointsWithStride(*this, dst, sizeof(SkPoint3), src, sizeof(SkPoint3), count); } -void SkMatrix::mapPointsToHomogeneous(SkSpan<SkPoint3> dst, SkSpan<const SkPoint> src) const { - const auto count = min_count(dst, src); - +void SkMatrix::mapHomogeneousPoints(SkPoint3 dst[], const SkPoint src[], int count) const { if (this->isIdentity()) { for (int i = 0; i < count; ++i) { dst[i] = { src[i].fX, src[i].fY, 1 }; @@ -1105,19 +1106,25 @@ void SkMatrix::mapPointsToHomogeneous(SkSpan<SkPoint3> dst, SkSpan<const SkPoint /////////////////////////////////////////////////////////////////////////////// -void SkMatrix::mapVectors(SkSpan<SkPoint> dst, SkSpan<const SkPoint> src) const { +void SkMatrix::mapVectors(SkPoint dst[], const SkPoint src[], int count) const { if (this->hasPerspective()) { - const SkPoint origin = this->mapPointPerspective({0, 0}); + SkPoint origin; - for (int i = min_count(dst, src) - 1; i >= 0; --i) { - dst[i] = this->mapPointPerspective(src[i]) - origin; + MapXYProc proc = this->getMapXYProc(); + proc(*this, 0, 0, &origin); + + for (int i = count - 1; i >= 0; --i) { + SkPoint tmp; + + proc(*this, src[i].fX, src[i].fY, &tmp); + dst[i].set(tmp.fX - origin.fX, tmp.fY - origin.fY); } } else { SkMatrix tmp = *this; tmp.fMat[kMTransX] = tmp.fMat[kMTransY] = 0; tmp.clearTypeMask(kTranslate_Mask); - tmp.mapPoints(dst, src); + tmp.mapPoints(dst, src, count); } } @@ -1143,7 +1150,7 @@ void SkMatrix::mapRectScaleTranslate(SkRect* dst, const SkRect& src) const { sort_as_rect(skvx::float4::Load(&src.fLeft) * scale + trans).store(&dst->fLeft); } -bool SkMatrix::mapRect(SkRect* dst, const SkRect& src) const { +bool SkMatrix::mapRect(SkRect* dst, const SkRect& src, SkApplyPerspectiveClip pc) const { SkASSERT(dst); if (this->getType() <= kTranslate_Mask) { @@ -1156,16 +1163,18 @@ bool SkMatrix::mapRect(SkRect* dst, const SkRect& src) const { if (this->isScaleTranslate()) { this->mapRectScaleTranslate(dst, src); return true; - } else if (this->hasPerspective()) { - SkPathBuilder builder; - builder.addRect(src); - builder.transform(*this); - *dst = builder.computeBounds(); + } else if (pc == SkApplyPerspectiveClip::kYes && this->hasPerspective()) { + SkPath path; + path.addRect(src); + path.transform(*this); + *dst = path.getBounds(); return false; } else { - std::array<SkPoint, 4> quad = src.toQuad(); - this->mapPoints(quad); - dst->setBoundsNoCheck(quad); + SkPoint quad[4]; + + src.toQuad(quad); + this->mapPoints(quad, quad, 4); + dst->setBoundsNoCheck(quad, 4); return this->rectStaysRect(); // might still return true if rotated by 90, etc. } } @@ -1175,7 +1184,7 @@ SkScalar SkMatrix::mapRadius(SkScalar radius) const { vec[0].set(radius, 0); vec[1].set(0, radius); - this->mapVectors(vec); + this->mapVectors(vec, 2); SkScalar d0 = vec[0].length(); SkScalar d1 = vec[1].length(); @@ -1185,6 +1194,88 @@ SkScalar SkMatrix::mapRadius(SkScalar radius) const { } /////////////////////////////////////////////////////////////////////////////// + +void SkMatrix::Persp_xy(const SkMatrix& m, SkScalar sx, SkScalar sy, + SkPoint* pt) { + SkASSERT(m.hasPerspective()); + + SkScalar x = sdot(sx, m.fMat[kMScaleX], sy, m.fMat[kMSkewX]) + m.fMat[kMTransX]; + SkScalar y = sdot(sx, m.fMat[kMSkewY], sy, m.fMat[kMScaleY]) + m.fMat[kMTransY]; + SkScalar z = sdot(sx, m.fMat[kMPersp0], sy, m.fMat[kMPersp1]) + m.fMat[kMPersp2]; + if (z) { + z = 1 / z; + } + pt->fX = x * z; + pt->fY = y * z; +} + +void SkMatrix::RotTrans_xy(const SkMatrix& m, SkScalar sx, SkScalar sy, + SkPoint* pt) { + SkASSERT((m.getType() & (kAffine_Mask | kPerspective_Mask)) == kAffine_Mask); + + pt->fX = sdot(sx, m.fMat[kMScaleX], sy, m.fMat[kMSkewX]) + m.fMat[kMTransX]; + pt->fY = sdot(sx, m.fMat[kMSkewY], sy, m.fMat[kMScaleY]) + m.fMat[kMTransY]; +} + +void SkMatrix::Rot_xy(const SkMatrix& m, SkScalar sx, SkScalar sy, + SkPoint* pt) { + SkASSERT((m.getType() & (kAffine_Mask | kPerspective_Mask))== kAffine_Mask); + SkASSERT(0 == m.fMat[kMTransX]); + SkASSERT(0 == m.fMat[kMTransY]); + + pt->fX = sdot(sx, m.fMat[kMScaleX], sy, m.fMat[kMSkewX]) + m.fMat[kMTransX]; + pt->fY = sdot(sx, m.fMat[kMSkewY], sy, m.fMat[kMScaleY]) + m.fMat[kMTransY]; +} + +void SkMatrix::ScaleTrans_xy(const SkMatrix& m, SkScalar sx, SkScalar sy, + SkPoint* pt) { + SkASSERT((m.getType() & (kScale_Mask | kAffine_Mask | kPerspective_Mask)) + == kScale_Mask); + + pt->fX = sx * m.fMat[kMScaleX] + m.fMat[kMTransX]; + pt->fY = sy * m.fMat[kMScaleY] + m.fMat[kMTransY]; +} + +void SkMatrix::Scale_xy(const SkMatrix& m, SkScalar sx, SkScalar sy, + SkPoint* pt) { + SkASSERT((m.getType() & (kScale_Mask | kAffine_Mask | kPerspective_Mask)) + == kScale_Mask); + SkASSERT(0 == m.fMat[kMTransX]); + SkASSERT(0 == m.fMat[kMTransY]); + + pt->fX = sx * m.fMat[kMScaleX]; + pt->fY = sy * m.fMat[kMScaleY]; +} + +void SkMatrix::Trans_xy(const SkMatrix& m, SkScalar sx, SkScalar sy, + SkPoint* pt) { + SkASSERT(m.getType() == kTranslate_Mask); + + pt->fX = sx + m.fMat[kMTransX]; + pt->fY = sy + m.fMat[kMTransY]; +} + +void SkMatrix::Identity_xy(const SkMatrix& m, SkScalar sx, SkScalar sy, + SkPoint* pt) { + SkASSERT(0 == m.getType()); + + pt->fX = sx; + pt->fY = sy; +} + +const SkMatrix::MapXYProc SkMatrix::gMapXYProcs[] = { + SkMatrix::Identity_xy, SkMatrix::Trans_xy, + SkMatrix::Scale_xy, SkMatrix::ScaleTrans_xy, + SkMatrix::Rot_xy, SkMatrix::RotTrans_xy, + SkMatrix::Rot_xy, SkMatrix::RotTrans_xy, + // repeat the persp proc 8 times + SkMatrix::Persp_xy, SkMatrix::Persp_xy, + SkMatrix::Persp_xy, SkMatrix::Persp_xy, + SkMatrix::Persp_xy, SkMatrix::Persp_xy, + SkMatrix::Persp_xy, SkMatrix::Persp_xy +}; + +/////////////////////////////////////////////////////////////////////////////// #if 0 // if its nearly zero (just made up 26, perhaps it should be bigger or smaller) #define PerspNearlyZero(x) SkScalarNearlyZero(x, (1.0f / (1 << 26))) @@ -1301,42 +1392,41 @@ bool SkMatrix::Poly4Proc(const SkPoint srcPt[], SkMatrix* dst) { typedef bool (*PolyMapProc)(const SkPoint[], SkMatrix*); -/* Originally adapted from Rob Johnson's original sample code in QuickDraw GX +/* Adapted from Rob Johnson's original sample code in QuickDraw GX */ -std::optional<SkMatrix> SkMatrix::PolyToPoly(SkSpan<const SkPoint> src, SkSpan<const SkPoint> dst) { - if (src.size() != dst.size()) { - return {}; +bool SkMatrix::setPolyToPoly(const SkPoint src[], const SkPoint dst[], int count) { + if ((unsigned)count > 4) { + SkDebugf("--- SkMatrix::setPolyToPoly count out of range %d\n", count); + return false; + } + + if (0 == count) { + this->reset(); + return true; + } + if (1 == count) { + this->setTranslate(dst[0].fX - src[0].fX, dst[0].fY - src[0].fY); + return true; } const PolyMapProc gPolyMapProcs[] = { SkMatrix::Poly2Proc, SkMatrix::Poly3Proc, SkMatrix::Poly4Proc }; + PolyMapProc proc = gPolyMapProcs[count - 2]; - switch (src.size()) { - case 0: return SkMatrix::I(); - case 1: return SkMatrix::Translate(dst[0] - src[0]); - case 2: [[fallthrough]]; - case 3: [[fallthrough]]; - case 4: { - PolyMapProc proc = gPolyMapProcs[src.size() - 2]; - - SkMatrix tempMap; - if (!proc(src.data(), &tempMap)) { - return {}; - } - auto inverse = tempMap.invert(); - if (!inverse) { - return {}; - } - if (!proc(dst.data(), &tempMap)) { - return {}; - } - return tempMap * inverse.value(); - } break; - default: - break; + SkMatrix tempMap, result; + + if (!proc(src, &tempMap)) { + return false; + } + if (!tempMap.invert(&result)) { + return false; } - return {}; + if (!proc(dst, &tempMap)) { + return false; + } + this->setConcat(tempMap, result); + return true; } /////////////////////////////////////////////////////////////////////////////// @@ -1723,7 +1813,8 @@ SkScalar SkMatrixPriv::DifferentialAreaScale(const SkMatrix& m, const SkPoint& p // [x y w ] [x y w ] // J' = [dx/du dy/du dw/du] = [m00 m10 m20] // [dx/dv dy/dv dw/dv] [m01 m11 m21] - SkPoint3 xyw = m.mapPointToHomogeneous(p); + SkPoint3 xyw; + m.mapHomogeneousPoints(&xyw, &p, 1); if (xyw.fZ < SK_ScalarNearlyZero) { // Reaching the discontinuity of xy/w and where the point would clip to w >= 0 @@ -1750,8 +1841,10 @@ bool SkMatrixPriv::NearlyAffine(const SkMatrix& m, // that the transformation is nearly affine. // We can map the four points simultaneously. + SkPoint quad[4]; + bounds.toQuad(quad); SkPoint3 xyw[4]; - m.mapPointsToHomogeneous(xyw, bounds.toQuad()); + m.mapHomogeneousPoints(xyw, quad, 4); // Since the Jacobian is a 3x3 matrix, the determinant is a scalar triple product, // and the initial cross product is constant across all four points. diff --git a/gfx/skia/skia/src/core/SkMatrixPriv.h b/gfx/skia/skia/src/core/SkMatrixPriv.h @@ -35,6 +35,7 @@ public: return matrix->readFromMemory(buffer, length); } + typedef SkMatrix::MapXYProc MapXYProc; typedef SkMatrix::MapPtsProc MapPtsProc; @@ -42,6 +43,10 @@ public: return SkMatrix::GetMapPtsProc(matrix.getType()); } + static MapXYProc GetMapXYProc(const SkMatrix& matrix) { + return SkMatrix::GetMapXYProc(matrix.getType()); + } + /** * Attempt to map the rect through the inverse of the matrix. If it is not invertible, * then this returns false and dst is unchanged. @@ -78,8 +83,9 @@ public: } // general case - if (auto inverse = mx.invert()) { - inverse->mapRect(dst, src); + SkMatrix inverse; + if (mx.invert(&inverse)) { + inverse.mapRect(dst, src); return true; } return false; @@ -125,16 +131,10 @@ public: // Insert other special-cases here (e.g. scale+translate) // general case - if (mx.hasPerspective()) { - for (int i = 0; i < count; ++i) { - *pts = mx.mapPointPerspective(*pts); - pts = (SkPoint*)((intptr_t)pts + stride); - } - } else { - for (int i = 0; i < count; ++i) { - *pts = mx.mapPointAffine(*pts); - pts = (SkPoint*)((intptr_t)pts + stride); - } + SkMatrix::MapXYProc proc = mx.getMapXYProc(); + for (int i = 0; i < count; ++i) { + proc(mx, pts->fX, pts->fY, pts); + pts = (SkPoint*)((intptr_t)pts + stride); } } @@ -165,7 +165,7 @@ public: SkASSERT(0 == srcStride % sizeof(SkScalar)); SkASSERT(0 == dstStride % sizeof(SkScalar)); for (int i = 0; i < count; ++i) { - *dst = mx.mapPoint(*src); + mx.mapPoints(dst, src, 1); src = (SkPoint*)((intptr_t)src + srcStride); dst = (SkPoint*)((intptr_t)dst + dstStride); } diff --git a/gfx/skia/skia/src/core/SkMesh.cpp b/gfx/skia/skia/src/core/SkMesh.cpp @@ -509,7 +509,7 @@ SkMeshSpecification::Result SkMeshSpecification::MakeFromSourceWithStructs( SkSL::ProgramSettings settings; settings.fUseMemoryPool = false; - // TODO(skbug.com/40042585): Add SkCapabilities to the API, check against required version. + // TODO(skia:11209): Add SkCapabilities to the API, check against required version. std::unique_ptr<SkSL::Program> vsProgram = compiler.convertProgram( SkSL::ProgramKind::kMeshVertex, std::string(vs.c_str()), diff --git a/gfx/skia/skia/src/core/SkOverdrawCanvas.cpp b/gfx/skia/skia/src/core/SkOverdrawCanvas.cpp @@ -23,7 +23,6 @@ #include "include/private/base/SkTDArray.h" #include "src/base/SkZip.h" #include "src/core/SkDevice.h" -#include "src/core/SkDraw.h" #include "src/core/SkDrawShadowInfo.h" #include "src/core/SkGlyph.h" #include "src/core/SkGlyphRunPainter.h" @@ -59,7 +58,7 @@ SkOverdrawCanvas::SkOverdrawCanvas(SkCanvas* canvas) } namespace { -class TextDevice : public SkNoPixelsDevice, public skcpu::BitmapDevicePainter { +class TextDevice : public SkNoPixelsDevice, public SkGlyphRunListPainterCPU::BitmapDevicePainter { public: TextDevice(SkCanvas* overdrawCanvas, const SkSurfaceProps& props) : SkNoPixelsDevice{SkIRect::MakeWH(32767, 32767), props}, @@ -70,7 +69,7 @@ public: for (auto [glyph, pos] : accepted) { SkMask mask = glyph->mask(pos); // We need to ignore any matrix on the overdraw canvas (it's already been baked into - // our glyph positions). Otherwise, the CTM is double-applied. (skbug.com/40044818) + // our glyph positions). Otherwise, the CTM is double-applied. (skbug.com/13732) fOverdrawCanvas->save(); fOverdrawCanvas->resetMatrix(); fOverdrawCanvas->drawRect(SkRect::Make(mask.fBounds), SkPaint()); @@ -91,7 +90,7 @@ public: private: SkCanvas* const fOverdrawCanvas; - skcpu::GlyphRunListPainter fPainter; + SkGlyphRunListPainterCPU fPainter; }; } // namespace @@ -233,7 +232,7 @@ void SkOverdrawCanvas::onDrawShadowRec(const SkPath& path, const SkDrawShadowRec void SkOverdrawCanvas::onDrawEdgeAAQuad(const SkRect& rect, const SkPoint clip[4], QuadAAFlags aa, const SkColor4f& color, SkBlendMode mode) { if (clip) { - fList[0]->onDrawPath(SkPath::Polygon({clip, 4}, true), fPaint); + fList[0]->onDrawPath(SkPath::Polygon(clip, 4, true), fPaint); } else { fList[0]->onDrawRect(rect, fPaint); } @@ -252,7 +251,7 @@ void SkOverdrawCanvas::onDrawEdgeAAImageSet2(const ImageSetEntry set[], int coun fList[0]->concat(preViewMatrices[set[i].fMatrixIndex]); } if (set[i].fHasClip) { - fList[0]->onDrawPath(SkPath::Polygon({dstClips + clipIndex, 4}, true), fPaint); + fList[0]->onDrawPath(SkPath::Polygon(dstClips + clipIndex, 4, true), fPaint); clipIndex += 4; } else { fList[0]->onDrawRect(set[i].fDstRect, fPaint); diff --git a/gfx/skia/skia/src/core/SkPath.cpp b/gfx/skia/skia/src/core/SkPath.cpp @@ -9,32 +9,33 @@ #include "include/core/SkArc.h" #include "include/core/SkPathBuilder.h" -#include "include/core/SkPathTypes.h" #include "include/core/SkRRect.h" -#include "include/core/SkSpan.h" #include "include/core/SkStream.h" #include "include/core/SkString.h" #include "include/private/SkPathRef.h" #include "include/private/base/SkFloatingPoint.h" #include "include/private/base/SkMalloc.h" +#include "include/private/base/SkSpan_impl.h" #include "include/private/base/SkTArray.h" #include "include/private/base/SkTDArray.h" #include "include/private/base/SkTo.h" #include "src/base/SkFloatBits.h" +#include "src/base/SkTLazy.h" #include "src/base/SkVx.h" #include "src/core/SkCubicClipper.h" #include "src/core/SkEdgeClipper.h" #include "src/core/SkGeometry.h" #include "src/core/SkMatrixPriv.h" #include "src/core/SkPathEnums.h" +#include "src/core/SkPathMakers.h" #include "src/core/SkPathPriv.h" -#include "src/core/SkPathRawShapes.h" #include "src/core/SkPointPriv.h" #include "src/core/SkStringUtils.h" #include <algorithm> #include <cmath> #include <cstring> +#include <iterator> #include <limits.h> #include <utility> @@ -57,24 +58,78 @@ static float poly_eval(float A, float B, float C, float D, float t) { //////////////////////////////////////////////////////////////////////////// -class SkAutoAddSimpleShape { +/** + * Path.bounds is defined to be the bounds of all the control points. + * If we called bounds.join(r) we would skip r if r was empty, which breaks + * our promise. Hence we have a custom joiner that doesn't look at emptiness + */ +static void joinNoEmptyChecks(SkRect* dst, const SkRect& src) { + dst->fLeft = std::min(dst->fLeft, src.fLeft); + dst->fTop = std::min(dst->fTop, src.fTop); + dst->fRight = std::max(dst->fRight, src.fRight); + dst->fBottom = std::max(dst->fBottom, src.fBottom); +} + +static bool is_degenerate(const SkPath& path) { + return (path.countVerbs() - SkPathPriv::LeadingMoveToCount(path)) == 0; +} + +class SkAutoDisableDirectionCheck { public: - SkAutoAddSimpleShape(SkPath* path, SkPathDirection dir) - : fPath(path) - , fDirection(dir) - { - fIsEffectivelyEmpty = SkPathPriv::IsEffectivelyEmpty(*path); + SkAutoDisableDirectionCheck(SkPath* path) : fPath(path) { + fSaved = static_cast<SkPathFirstDirection>(fPath->getFirstDirection()); + } + + ~SkAutoDisableDirectionCheck() { + fPath->setFirstDirection(fSaved); } - ~SkAutoAddSimpleShape() { - fPath->setConvexity(fIsEffectivelyEmpty ? SkPathDirection_ToConvexity(fDirection) - : SkPathConvexity::kUnknown); +private: + SkPath* fPath; + SkPathFirstDirection fSaved; +}; + +/* This class's constructor/destructor bracket a path editing operation. It is + used when we know the bounds of the amount we are going to add to the path + (usually a new contour, but not required). + + It captures some state about the path up front (i.e. if it already has a + cached bounds), and then if it can, it updates the cache bounds explicitly, + avoiding the need to revisit all of the points in getBounds(). + + It also notes if the path was originally degenerate, and if so, sets + isConvex to true. Thus it can only be used if the contour being added is + convex. + */ +class SkAutoPathBoundsUpdate { +public: + SkAutoPathBoundsUpdate(SkPath* path, const SkRect& r) : fPath(path), fRect(r) { + // Cannot use fRect for our bounds unless we know it is sorted + fRect.sort(); + // Mark the path's bounds as dirty if (1) they are, or (2) the path + // is non-finite, and therefore its bounds are not meaningful + fHasValidBounds = path->hasComputedBounds() && path->isFinite(); + fEmpty = path->isEmpty(); + if (fHasValidBounds && !fEmpty) { + joinNoEmptyChecks(&fRect, fPath->getBounds()); + } + fDegenerate = is_degenerate(*path); + } + + ~SkAutoPathBoundsUpdate() { + fPath->setConvexity(fDegenerate ? SkPathConvexity::kConvex + : SkPathConvexity::kUnknown); + if ((fEmpty || fHasValidBounds) && fRect.isFinite()) { + fPath->setBounds(fRect); + } } private: - SkPath* fPath; - bool fIsEffectivelyEmpty; - SkPathDirection fDirection; + SkPath* fPath; + SkRect fRect; + bool fHasValidBounds; + bool fDegenerate; + bool fEmpty; }; //////////////////////////////////////////////////////////////////////////// @@ -104,10 +159,12 @@ SkPath::SkPath() fIsVolatile = false; } -SkPath::SkPath(sk_sp<SkPathRef> pr, SkPathFillType ft, bool isVolatile, SkPathConvexity ct) +SkPath::SkPath(sk_sp<SkPathRef> pr, SkPathFillType ft, bool isVolatile, SkPathConvexity ct, + SkPathFirstDirection firstDirection) : fPathRef(std::move(pr)) , fLastMoveToIndex(INITIAL_LASTMOVETOINDEX_VALUE) , fConvexity((uint8_t)ct) + , fFirstDirection((uint8_t)firstDirection) , fFillType((unsigned)ft) , fIsVolatile(isVolatile) {} @@ -115,8 +172,9 @@ SkPath::SkPath(sk_sp<SkPathRef> pr, SkPathFillType ft, bool isVolatile, SkPathCo void SkPath::resetFields() { //fPathRef is assumed to have been emptied by the caller. fLastMoveToIndex = INITIAL_LASTMOVETOINDEX_VALUE; - fFillType = SkToU8(SkPathFillType::kDefault); + fFillType = SkToU8(SkPathFillType::kWinding); this->setConvexity(SkPathConvexity::kUnknown); + this->setFirstDirection(SkPathFirstDirection::kUnknown); } SkPath::SkPath(const SkPath& that) @@ -148,6 +206,7 @@ void SkPath::copyFields(const SkPath& that) { // Non-atomic assignment of atomic values. this->setConvexity(that.getConvexityOrUnknown()); + this->setFirstDirection(that.getFirstDirection()); } bool operator==(const SkPath& a, const SkPath& b) { @@ -174,6 +233,10 @@ void SkPath::swap(SkPath& that) { SkPathConvexity c = this->getConvexityOrUnknown(); this->setConvexity(that.getConvexityOrUnknown()); that.setConvexity(c); + + SkPathFirstDirection fd = this->getFirstDirection(); + this->setFirstDirection(that.getFirstDirection()); + that.setFirstDirection(fd); } } @@ -199,19 +262,13 @@ bool SkPath::interpolate(const SkPath& ending, SkScalar weight, SkPath* out) con return true; } -SkPath SkPath::makeInterpolate(const SkPath& ending, SkScalar weight) const { - SkPath out; - this->interpolate(ending, weight, &out); - return out; -} - static inline bool check_edge_against_rect(const SkPoint& p0, const SkPoint& p1, const SkRect& rect, - SkPathDirection dir) { + SkPathFirstDirection dir) { const SkPoint* edgeBegin; SkVector v; - if (SkPathDirection::kCW == dir) { + if (SkPathFirstDirection::kCW == dir) { v = p1 - p0; edgeBegin = &p0; } else { @@ -232,13 +289,13 @@ static inline bool check_edge_against_rect(const SkPoint& p0, } bool SkPath::conservativelyContainsRect(const SkRect& rect) const { - const SkPathConvexity convexity = this->getConvexity(); - if (!SkPathConvexity_IsConvex(convexity)) { + // This only handles non-degenerate convex paths currently. + if (!this->isConvex()) { return false; } - const auto direction = SkPathConvexity_ToDirection(convexity); - if (!direction) { + SkPathFirstDirection direction = SkPathPriv::ComputeFirstDirection(*this); + if (direction == SkPathFirstDirection::kUnknown) { return false; } @@ -264,7 +321,7 @@ bool SkPath::conservativelyContainsRect(const SkRect& rect) const { int pointCount = SkPathPriv::PtsInVerb((unsigned) verb); SkASSERT(pointCount > 0); - if (!SkPathPriv::AllPointsEq({pts, pointCount + 1})) { + if (!SkPathPriv::AllPointsEq(pts, pointCount + 1)) { SkASSERT(moveCnt); int nextPt = pointCount; segmentCount++; @@ -284,14 +341,14 @@ bool SkPath::conservativelyContainsRect(const SkRect& rect) const { int count = orig.chopIntoQuadsPOW2(quadPts, 1); SkASSERT_RELEASE(2 == count); - if (!check_edge_against_rect(quadPts[0], quadPts[2], rect, *direction)) { + if (!check_edge_against_rect(quadPts[0], quadPts[2], rect, direction)) { return false; } - if (!check_edge_against_rect(quadPts[2], quadPts[4], rect, *direction)) { + if (!check_edge_against_rect(quadPts[2], quadPts[4], rect, direction)) { return false; } } else { - if (!check_edge_against_rect(prevPt, pts[nextPt], rect, *direction)) { + if (!check_edge_against_rect(prevPt, pts[nextPt], rect, direction)) { return false; } } @@ -301,7 +358,7 @@ bool SkPath::conservativelyContainsRect(const SkRect& rect) const { } if (segmentCount) { - return check_edge_against_rect(prevPt, firstPt, rect, *direction); + return check_edge_against_rect(prevPt, firstPt, rect, direction); } return false; } @@ -335,15 +392,15 @@ bool SkPath::isLastContourClosed() const { if (0 == verbCount) { return false; } - return SkPathVerb::kClose == fPathRef->atVerb(verbCount - 1); + return kClose_Verb == fPathRef->atVerb(verbCount - 1); } bool SkPath::isLine(SkPoint line[2]) const { int verbCount = fPathRef->countVerbs(); if (2 == verbCount) { - SkASSERT(SkPathVerb::kMove == fPathRef->verbs()[0]); - if (SkPathVerb::kLine == fPathRef->verbs()[1]) { + SkASSERT(kMove_Verb == fPathRef->atVerb(0)); + if (kLine_Verb == fPathRef->atVerb(1)) { SkASSERT(2 == fPathRef->countPoints()); if (line) { const SkPoint* pts = fPathRef->points(); @@ -367,7 +424,7 @@ bool SkPath::isFinite() const { } bool SkPath::isConvex() const { - return SkPathConvexity_IsConvex(this->getConvexity()); + return SkPathConvexity::kConvex == this->getConvexity(); } const SkRect& SkPath::getBounds() const { @@ -396,27 +453,6 @@ SkPathConvexity SkPath::getConvexityOrUnknown() const { return (SkPathConvexity)fConvexity.load(std::memory_order_relaxed); } -SkPath SkPath::makeFillType(SkPathFillType ft) const { - return SkPath(fPathRef, - ft, - fIsVolatile, - this->getConvexityOrUnknown()); -} - -SkPath SkPath::makeToggleInverseFillType() const { - return SkPath(fPathRef, - static_cast<SkPathFillType>(fFillType ^ 2), - fIsVolatile, - this->getConvexityOrUnknown()); -} - -SkPath SkPath::makeIsVolatile(bool v) const { - return SkPath(fPathRef, - static_cast<SkPathFillType>(fFillType), - v, - this->getConvexityOrUnknown()); -} - #ifdef SK_DEBUG void SkPath::validate() const { SkASSERT(this->isValidImpl()); @@ -427,56 +463,87 @@ void SkPath::validateRef() const { fPathRef->validate(); } #endif +/* + Determines if path is a rect by keeping track of changes in direction + and looking for a loop either clockwise or counterclockwise. + + The direction is computed such that: + 0: vertical up + 1: horizontal left + 2: vertical down + 3: horizontal right + +A rectangle cycles up/right/down/left or up/left/down/right. + +The test fails if: + The path is closed, and followed by a line. + A second move creates a new endpoint. + A diagonal line is parsed. + There's more than four changes of direction. + There's a discontinuity on the line (e.g., a move in the middle) + The line reverses direction. + The path contains a quadratic or cubic. + The path contains fewer than four points. + *The rectangle doesn't complete a cycle. + *The final point isn't equal to the first point. + + *These last two conditions we relax if we have a 3-edge path that would + form a rectangle if it were closed (as we do when we fill a path) + +It's OK if the path has: + Several colinear line segments composing a rectangle side. + Single points on the rectangle side. + +The direction takes advantage of the corners found since opposite sides +must travel in opposite directions. + +FIXME: Allow colinear quads and cubics to be treated like lines. +FIXME: If the API passes fill-only, return true if the filled stroke + is a rectangle, though the caller failed to close the path. + + directions values: + 0x1 is set if the segment is horizontal + 0x2 is set if the segment is moving to the right or down + thus: + two directions are opposites iff (dirA ^ dirB) == 0x2 + two directions are perpendicular iff (dirA ^ dirB) == 0x1 + + */ +static int rect_make_dir(SkScalar dx, SkScalar dy) { + return ((0 != dx) << 0) | ((dx > 0 || dy > 0) << 1); +} + bool SkPath::isRect(SkRect* rect, bool* isClosed, SkPathDirection* direction) const { SkDEBUGCODE(this->validate();) - SkSpan<const SkPoint> pts = {fPathRef->points(), fPathRef->countPoints()}; - SkSpan<const SkPathVerb> vbs = fPathRef->verbs(); - if (auto rc = SkPathPriv::IsRectContour(pts, vbs, false)) { - if (rect) { - *rect = rc->fRect; - } - if (isClosed) { - *isClosed = rc->fIsClosed; - } - if (direction) { - *direction = rc->fDirection; - } - return true; - } - return false; + int currVerb = 0; + const SkPoint* pts = fPathRef->points(); + return SkPathPriv::IsRectContour(*this, false, &currVerb, &pts, isClosed, direction, rect); } bool SkPath::isOval(SkRect* bounds) const { - if (auto info = fPathRef->isOval()) { - if (bounds) { - *bounds = info->fBounds; - } - return true; - } - return false; + return SkPathPriv::IsOval(*this, bounds, nullptr, nullptr); } bool SkPath::isRRect(SkRRect* rrect) const { - if (auto info = fPathRef->isRRect()) { - if (rrect) { - *rrect = info->fRRect; - } - return true; - } - return false; + return SkPathPriv::IsRRect(*this, rrect, nullptr, nullptr); +} + +bool SkPath::isArc(SkArc* arc) const { + return fPathRef->isArc(arc); } int SkPath::countPoints() const { return fPathRef->countPoints(); } -size_t SkPath::getPoints(SkSpan<SkPoint> dst) const { +int SkPath::getPoints(SkPoint dst[], int max) const { SkDEBUGCODE(this->validate();) - const size_t ptCount = fPathRef->countPoints(); - const size_t n = std::min(dst.size(), ptCount); - sk_careful_memcpy(dst.data(), fPathRef->points(), n * sizeof(SkPoint)); - return ptCount; + SkASSERT(max >= 0); + SkASSERT(!max || dst); + int count = std::min(max, fPathRef->countPoints()); + sk_careful_memcpy(dst, fPathRef->points(), count * sizeof(SkPoint)); + return fPathRef->countPoints(); } SkPoint SkPath::getPoint(int index) const { @@ -490,13 +557,16 @@ int SkPath::countVerbs() const { return fPathRef->countVerbs(); } -size_t SkPath::getVerbs(SkSpan<uint8_t> dst) const { +int SkPath::getVerbs(uint8_t dst[], int max) const { SkDEBUGCODE(this->validate();) - const size_t vbCount = fPathRef->countVerbs(); - const size_t n = std::min(dst.size(), vbCount); - sk_careful_memcpy(dst.data(), fPathRef->verbsBegin(), n); - return vbCount; + SkASSERT(max >= 0); + SkASSERT(!max || dst); + int count = std::min(max, fPathRef->countVerbs()); + if (count) { + memcpy(dst, fPathRef->verbsBegin(), count); + } + return fPathRef->countVerbs(); } size_t SkPath::approximateBytesUsed() const { @@ -507,13 +577,20 @@ size_t SkPath::approximateBytesUsed() const { return size; } -std::optional<SkPoint> SkPath::getLastPt() const { +bool SkPath::getLastPt(SkPoint* lastPt) const { SkDEBUGCODE(this->validate();) - if (const int count = fPathRef->countPoints()) { - return fPathRef->atPoint(count - 1); + int count = fPathRef->countPoints(); + if (count > 0) { + if (lastPt) { + *lastPt = fPathRef->atPoint(count - 1); + } + return true; } - return {}; + if (lastPt) { + lastPt->set(0, 0); + } + return false; } void SkPath::setPt(int index, SkScalar x, SkScalar y) { @@ -549,6 +626,12 @@ void SkPath::setConvexity(SkPathConvexity c) { void SkPath::setConvexity(SkPathConvexity c) const { fConvexity.store((uint8_t)c, std::memory_order_relaxed); } +void SkPath::setFirstDirection(SkPathFirstDirection d) const { + fFirstDirection.store((uint8_t)d, std::memory_order_relaxed); +} +SkPathFirstDirection SkPath::getFirstDirection() const { + return (SkPathFirstDirection)fFirstDirection.load(std::memory_order_relaxed); +} bool SkPath::isConvexityAccurate() const { SkPathConvexity convexity = this->getConvexityOrUnknown(); @@ -578,6 +661,7 @@ SkPathConvexity SkPath::getConvexity() const { SkPath& SkPath::dirtyAfterEdit() { this->setConvexity(SkPathConvexity::kUnknown); + this->setFirstDirection(SkPathFirstDirection::kUnknown); #ifdef SK_DEBUG // enable this as needed for testing, but it slows down some chrome tests so much @@ -606,14 +690,10 @@ SkPath& SkPath::moveTo(SkScalar x, SkScalar y) { SkPathRef::Editor ed(&fPathRef); - if (!fPathRef->fVerbs.empty() && fPathRef->fVerbs.back() == SkPathVerb::kMove) { - fPathRef->fPoints.back() = {x, y}; - } else { - // remember our index - fLastMoveToIndex = fPathRef->countPoints(); + // remember our index + fLastMoveToIndex = fPathRef->countPoints(); - ed.growForVerb(SkPathVerb::kMove)->set(x, y); - } + ed.growForVerb(kMove_Verb)->set(x, y); return this->dirtyAfterEdit(); } @@ -651,14 +731,15 @@ SkPath& SkPath::lineTo(SkScalar x, SkScalar y) { this->injectMoveToIfNeeded(); SkPathRef::Editor ed(&fPathRef); - ed.growForVerb(SkPathVerb::kLine)->set(x, y); + ed.growForVerb(kLine_Verb)->set(x, y); return this->dirtyAfterEdit(); } SkPath& SkPath::rLineTo(SkScalar x, SkScalar y) { this->injectMoveToIfNeeded(); // This can change the result of this->getLastPt(). - SkPoint pt = this->getLastPt().value_or(SkPoint{0, 0}); + SkPoint pt; + this->getLastPt(&pt); return this->lineTo(pt.fX + x, pt.fY + y); } @@ -668,7 +749,7 @@ SkPath& SkPath::quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) { this->injectMoveToIfNeeded(); SkPathRef::Editor ed(&fPathRef); - SkPoint* pts = ed.growForVerb(SkPathVerb::kQuad); + SkPoint* pts = ed.growForVerb(kQuad_Verb); pts[0].set(x1, y1); pts[1].set(x2, y2); @@ -677,7 +758,8 @@ SkPath& SkPath::quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) { SkPath& SkPath::rQuadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) { this->injectMoveToIfNeeded(); // This can change the result of this->getLastPt(). - SkPoint pt = this->getLastPt().value_or(SkPoint{0, 0}); + SkPoint pt; + this->getLastPt(&pt); return this->quadTo(pt.fX + x1, pt.fY + y1, pt.fX + x2, pt.fY + y2); } @@ -697,7 +779,7 @@ SkPath& SkPath::conicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, this->injectMoveToIfNeeded(); SkPathRef::Editor ed(&fPathRef); - SkPoint* pts = ed.growForVerb(SkPathVerb::kConic, w); + SkPoint* pts = ed.growForVerb(kConic_Verb, w); pts[0].set(x1, y1); pts[1].set(x2, y2); @@ -709,7 +791,8 @@ SkPath& SkPath::conicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkPath& SkPath::rConicTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2, SkScalar w) { this->injectMoveToIfNeeded(); // This can change the result of this->getLastPt(). - SkPoint pt = this->getLastPt().value_or(SkPoint{0, 0}); + SkPoint pt; + this->getLastPt(&pt); return this->conicTo(pt.fX + dx1, pt.fY + dy1, pt.fX + dx2, pt.fY + dy2, w); } @@ -720,7 +803,7 @@ SkPath& SkPath::cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, this->injectMoveToIfNeeded(); SkPathRef::Editor ed(&fPathRef); - SkPoint* pts = ed.growForVerb(SkPathVerb::kCubic); + SkPoint* pts = ed.growForVerb(kCubic_Verb); pts[0].set(x1, y1); pts[1].set(x2, y2); pts[2].set(x3, y3); @@ -731,7 +814,8 @@ SkPath& SkPath::cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkPath& SkPath::rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar x3, SkScalar y3) { this->injectMoveToIfNeeded(); // This can change the result of this->getLastPt(). - SkPoint pt = this->getLastPt().value_or(SkPoint{0, 0}); + SkPoint pt; + this->getLastPt(&pt); return this->cubicTo(pt.fX + x1, pt.fY + y1, pt.fX + x2, pt.fY + y2, pt.fX + x3, pt.fY + y3); } @@ -739,20 +823,24 @@ SkPath& SkPath::rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkPath& SkPath::close() { SkDEBUGCODE(this->validate();) - if (!fPathRef->verbs().empty()) { - switch (fPathRef->verbs().back()) { - case SkPathVerb::kLine: - case SkPathVerb::kQuad: - case SkPathVerb::kConic: - case SkPathVerb::kCubic: - case SkPathVerb::kMove: { + int count = fPathRef->countVerbs(); + if (count > 0) { + switch (fPathRef->atVerb(count - 1)) { + case kLine_Verb: + case kQuad_Verb: + case kConic_Verb: + case kCubic_Verb: + case kMove_Verb: { SkPathRef::Editor ed(&fPathRef); - ed.growForVerb(SkPathVerb::kClose); + ed.growForVerb(kClose_Verb); break; } - case SkPathVerb::kClose: + case kClose_Verb: // don't add a close if it's the first verb or a repeat break; + default: + SkDEBUGFAIL("unexpected verb"); + break; } } @@ -775,34 +863,49 @@ static void assert_known_direction(SkPathDirection dir) { SkPath& SkPath::addRect(const SkRect &rect, SkPathDirection dir, unsigned startIndex) { assert_known_direction(dir); + this->setFirstDirection(this->hasOnlyMoveTos() ? (SkPathFirstDirection)dir + : SkPathFirstDirection::kUnknown); + SkAutoDisableDirectionCheck addc(this); + SkAutoPathBoundsUpdate apbu(this, rect); - SkAutoAddSimpleShape addc(this, dir); + SkDEBUGCODE(int initialVerbCount = this->countVerbs()); - this->addRaw(SkPathRawShapes::Rect(rect, dir, startIndex)); + const int kVerbs = 5; // moveTo + 3x lineTo + close + SkPathRef::Editor ed(&fPathRef, kVerbs, /* points */ 4); + SkPath_RectPointIterator iter(rect, dir, startIndex); + fLastMoveToIndex = fPathRef->countPoints(); + + *ed.growForVerb(kMove_Verb) = iter.current(); + *ed.growForVerb(kLine_Verb) = iter.next(); + *ed.growForVerb(kLine_Verb) = iter.next(); + *ed.growForVerb(kLine_Verb) = iter.next(); + this->close(); + (void)this->dirtyAfterEdit(); + + SkASSERT(this->countVerbs() == initialVerbCount + kVerbs); return *this; } -SkPath& SkPath::addPoly(SkSpan<const SkPoint> pts, bool close) { +SkPath& SkPath::addPoly(const SkPoint pts[], int count, bool close) { SkDEBUGCODE(this->validate();) - if (pts.empty()) { + if (count <= 0) { return *this; } - const int count = SkToInt(pts.size()); fLastMoveToIndex = fPathRef->countPoints(); - // +close makes room for the extra SkPathVerb::kClose + // +close makes room for the extra kClose_Verb SkPathRef::Editor ed(&fPathRef, count+close, count); - ed.growForVerb(SkPathVerb::kMove)->set(pts[0].fX, pts[0].fY); + ed.growForVerb(kMove_Verb)->set(pts[0].fX, pts[0].fY); if (count > 1) { - SkPoint* p = ed.growForRepeatedVerb(SkPathVerb::kLine, count - 1); + SkPoint* p = ed.growForRepeatedVerb(kLine_Verb, count - 1); memcpy(p, &pts[1], (count-1) * sizeof(SkPoint)); } if (close) { - ed.growForVerb(SkPathVerb::kClose); + ed.growForVerb(kClose_Verb); fLastMoveToIndex ^= ~fLastMoveToIndex >> (8 * sizeof(fLastMoveToIndex) - 1); } @@ -811,26 +914,6 @@ SkPath& SkPath::addPoly(SkSpan<const SkPoint> pts, bool close) { return *this; } -void SkPath::addRaw(const SkPathRaw& raw) { - this->incReserve(raw.points().size(), raw.verbs().size()); - - for (auto iter = raw.iter(); auto rec = iter.next();) { - const auto pts = rec->fPoints; - switch (rec->fVerb) { - case SkPathVerb::kMove: this->moveTo( pts[0]); break; - case SkPathVerb::kLine: this->lineTo( pts[1]); break; - case SkPathVerb::kQuad: this->quadTo( pts[1], pts[2]); break; - case SkPathVerb::kConic: this->conicTo(pts[1], pts[2], rec->fConicWeight); break; - case SkPathVerb::kCubic: this->cubicTo(pts[1], pts[2], pts[3]); break; - case SkPathVerb::kClose: this->close(); break; - } - } -} - -SkPathIter SkPath::iter() const { - return { fPathRef->fPoints, fPathRef->verbs(), fPathRef->fConicWeights }; -} - static bool arc_is_lone_point(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, SkPoint* pt) { if (0 == sweepAngle && (0 == startAngle || SkIntToScalar(360) == startAngle)) { @@ -855,7 +938,7 @@ static bool arc_is_lone_point(const SkRect& oval, SkScalar startAngle, SkScalar // Return the unit vectors pointing at the start/stop points for the given start/sweep angles // static void angles_to_unit_vectors(SkScalar startAngle, SkScalar sweepAngle, - SkVector* startV, SkVector* stopV, SkPathDirection* dir) { + SkVector* startV, SkVector* stopV, SkRotationDirection* dir) { SkScalar startRad = SkDegreesToRadians(startAngle), stopRad = SkDegreesToRadians(startAngle + sweepAngle); @@ -885,7 +968,7 @@ static void angles_to_unit_vectors(SkScalar startAngle, SkScalar sweepAngle, } while (*startV == *stopV); } } - *dir = sweepAngle > 0 ? SkPathDirection::kCW : SkPathDirection::kCCW; + *dir = sweepAngle > 0 ? kCW_SkRotationDirection : kCCW_SkRotationDirection; } /** @@ -893,7 +976,7 @@ static void angles_to_unit_vectors(SkScalar startAngle, SkScalar sweepAngle, * ignore singlePt and append the specified number of conics. */ static int build_arc_conics(const SkRect& oval, const SkVector& start, const SkVector& stop, - SkPathDirection dir, SkConic conics[SkConic::kMaxConicsForArc], + SkRotationDirection dir, SkConic conics[SkConic::kMaxConicsForArc], SkPoint* singlePt) { SkMatrix matrix; @@ -902,16 +985,15 @@ static int build_arc_conics(const SkRect& oval, const SkVector& start, const SkV int count = SkConic::BuildUnitArc(start, stop, dir, &matrix, conics); if (0 == count) { - *singlePt = matrix.mapPoint(stop); + matrix.mapXY(stop.x(), stop.y(), singlePt); } return count; } -SkPath& SkPath::addRoundRect(const SkRect& rect, SkSpan<const SkScalar> radii, - SkPathDirection dir) { - SkASSERT(radii.size() >= 8); +SkPath& SkPath::addRoundRect(const SkRect& rect, const SkScalar radii[], + SkPathDirection dir) { SkRRect rrect; - rrect.setRectRadii(rect, (const SkVector*) radii.data()); + rrect.setRectRadii(rect, (const SkVector*) radii); return this->addRRect(rrect, dir); } @@ -933,14 +1015,58 @@ SkPath& SkPath::addRRect(const SkRRect &rrect, SkPathDirection dir, unsigned sta // degenerate(oval) => line points are collapsing this->addOval(bounds, dir, startIndex / 2); } else { - SkAutoAddSimpleShape addc(this, dir); - - this->addRaw(SkPathRawShapes::RRect(rrect, dir, startIndex)); + this->setFirstDirection(this->hasOnlyMoveTos() ? (SkPathFirstDirection)dir + : SkPathFirstDirection::kUnknown); + + SkAutoPathBoundsUpdate apbu(this, bounds); + SkAutoDisableDirectionCheck addc(this); + + // we start with a conic on odd indices when moving CW vs. even indices when moving CCW + const bool startsWithConic = ((startIndex & 1) == (dir == SkPathDirection::kCW)); + const SkScalar weight = SK_ScalarRoot2Over2; + + SkDEBUGCODE(int initialVerbCount = fPathRef->countVerbs()); + SkDEBUGCODE(int initialPointCount = fPathRef->countPoints()); + SkDEBUGCODE(int initialWeightCount = fPathRef->countWeights()); + const int kVerbs = startsWithConic + ? 9 // moveTo + 4x conicTo + 3x lineTo + close + : 10; // moveTo + 4x lineTo + 4x conicTo + close + const int kPoints = startsWithConic + ? 12 // moveTo (1) + 4x conicTo (2) + 3x lineTo (1) + close + : 13; // moveTo (1) + 4x lineTo (1) + 4x conicTo (2) + close + const int kWeights = 4; // 4x conicTo + this->incReserve(kPoints, kVerbs, kWeights); + + SkPath_RRectPointIterator rrectIter(rrect, dir, startIndex); + // Corner iterator indices follow the collapsed radii model, + // adjusted such that the start pt is "behind" the radii start pt. + const unsigned rectStartIndex = startIndex / 2 + (dir == SkPathDirection::kCW ? 0 : 1); + SkPath_RectPointIterator rectIter(bounds, dir, rectStartIndex); + + this->moveTo(rrectIter.current()); + if (startsWithConic) { + for (unsigned i = 0; i < 3; ++i) { + this->conicTo(rectIter.next(), rrectIter.next(), weight); + this->lineTo(rrectIter.next()); + } + this->conicTo(rectIter.next(), rrectIter.next(), weight); + // final lineTo handled by close(). + } else { + for (unsigned i = 0; i < 4; ++i) { + this->lineTo(rrectIter.next()); + this->conicTo(rectIter.next(), rrectIter.next(), weight); + } + } + this->close(); if (isRRect) { SkPathRef::Editor ed(&fPathRef); - ed.setIsRRect(dir, startIndex % 8); + ed.setIsRRect(dir == SkPathDirection::kCCW, startIndex % 8); } + + SkASSERT(fPathRef->countVerbs() == initialVerbCount + kVerbs); + SkASSERT(fPathRef->countPoints() == initialPointCount + kPoints); + SkASSERT(fPathRef->countWeights() == initialWeightCount + kWeights); } SkDEBUGCODE(fPathRef->validate();) @@ -992,14 +1118,41 @@ SkPath& SkPath::addOval(const SkRect &oval, SkPathDirection dir, unsigned startP moveTo() would mark the path non empty. */ bool isOval = hasOnlyMoveTos(); + if (isOval) { + this->setFirstDirection((SkPathFirstDirection)dir); + } else { + this->setFirstDirection(SkPathFirstDirection::kUnknown); + } + + SkAutoDisableDirectionCheck addc(this); + SkAutoPathBoundsUpdate apbu(this, oval); + + SkDEBUGCODE(int initialVerbCount = fPathRef->countVerbs()); + SkDEBUGCODE(int initialPointCount = fPathRef->countPoints()); + SkDEBUGCODE(int initialWeightCount = fPathRef->countWeights()); + const int kVerbs = 6; // moveTo + 4x conicTo + close + const int kPoints = 9; + const int kWeights = 4; + this->incReserve(kPoints, kVerbs, kWeights); + + SkPath_OvalPointIterator ovalIter(oval, dir, startPointIndex); + // The corner iterator pts are tracking "behind" the oval/radii pts. + SkPath_RectPointIterator rectIter(oval, dir, startPointIndex + (dir == SkPathDirection::kCW ? 0 : 1)); + const SkScalar weight = SK_ScalarRoot2Over2; - SkAutoAddSimpleShape addc(this, dir); + this->moveTo(ovalIter.current()); + for (unsigned i = 0; i < 4; ++i) { + this->conicTo(rectIter.next(), ovalIter.next(), weight); + } + this->close(); - this->addRaw(SkPathRawShapes::Oval(oval, dir, startPointIndex)); + SkASSERT(fPathRef->countVerbs() == initialVerbCount + kVerbs); + SkASSERT(fPathRef->countPoints() == initialPointCount + kPoints); + SkASSERT(fPathRef->countWeights() == initialWeightCount + kWeights); if (isOval) { SkPathRef::Editor ed(&fPathRef); - ed.setIsOval(dir, startPointIndex % 4); + ed.setIsOval(SkPathDirection::kCCW == dir, startPointIndex % 4); } return *this; } @@ -1029,24 +1182,25 @@ SkPath& SkPath::arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAng } SkVector startV, stopV; - SkPathDirection dir; + SkRotationDirection dir; angles_to_unit_vectors(startAngle, sweepAngle, &startV, &stopV, &dir); SkPoint singlePt; + bool isArc = this->hasOnlyMoveTos(); + // Adds a move-to to 'pt' if forceMoveTo is true. Otherwise a lineTo unless we're sufficiently // close to 'pt' currently. This prevents spurious lineTos when adding a series of contiguous // arcs from the same oval. - auto addPt = [&forceMoveTo, this](const SkPoint& pt) { + auto addPt = [&forceMoveTo, &isArc, this](const SkPoint& pt) { + SkPoint lastPt; if (forceMoveTo) { this->moveTo(pt); - } else { - auto lastPt = this->getLastPt(); - if (!lastPt || - !SkScalarNearlyEqual(lastPt->fX, pt.fX) || - !SkScalarNearlyEqual(lastPt->fY, pt.fY)) { - this->lineTo(pt); - } + } else if (!this->getLastPt(&lastPt) || + !SkScalarNearlyEqual(lastPt.fX, pt.fX) || + !SkScalarNearlyEqual(lastPt.fY, pt.fY)) { + this->lineTo(pt); + isArc = false; } }; @@ -1076,6 +1230,10 @@ SkPath& SkPath::arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAng for (int i = 0; i < count; ++i) { this->conicTo(conics[i].fPts[1], conics[i].fPts[2], conics[i].fW); } + if (isArc) { + SkPathRef::Editor ed(&fPathRef); + ed.setIsArc(SkArc::Make(oval, startAngle, sweepAngle, SkArc::Type::kArc)); + } } else { addPt(singlePt); } @@ -1092,7 +1250,7 @@ SkPath& SkPath::arcTo(SkScalar rx, SkScalar ry, SkScalar angle, SkPath::ArcSize SkPathDirection arcSweep, SkScalar x, SkScalar y) { this->injectMoveToIfNeeded(); SkPoint srcPts[2]; - srcPts[0] = *this->getLastPt(); + this->getLastPt(&srcPts[0]); // If rx = 0 or ry = 0 then this arc is treated as a straight line segment (a "lineto") // joining the endpoints. // http://www.w3.org/TR/SVG/implnote.html#ArcOutOfRangeParameters @@ -1113,7 +1271,8 @@ SkPath& SkPath::arcTo(SkScalar rx, SkScalar ry, SkScalar angle, SkPath::ArcSize SkMatrix pointTransform; pointTransform.setRotate(-angle); - SkPoint transformedMidPoint = pointTransform.mapPoint(midPointDistance); + SkPoint transformedMidPoint; + pointTransform.mapPoints(&transformedMidPoint, &midPointDistance, 1); SkScalar squareRx = rx * rx; SkScalar squareRy = ry * ry; SkScalar squareX = transformedMidPoint.fX * transformedMidPoint.fX; @@ -1132,7 +1291,7 @@ SkPath& SkPath::arcTo(SkScalar rx, SkScalar ry, SkScalar angle, SkPath::ArcSize pointTransform.preRotate(-angle); SkPoint unitPts[2]; - pointTransform.mapPoints(unitPts, srcPts); + pointTransform.mapPoints(unitPts, srcPts, (int) std::size(unitPts)); SkVector delta = unitPts[1] - unitPts[0]; SkScalar d = delta.fX * delta.fX + delta.fY * delta.fY; @@ -1157,7 +1316,7 @@ SkPath& SkPath::arcTo(SkScalar rx, SkScalar ry, SkScalar angle, SkPath::ArcSize thetaArc -= SK_ScalarPI * 2; } - // Very tiny angles cause our subsequent math to go wonky (skbug.com/40040578) + // Very tiny angles cause our subsequent math to go wonky (skbug.com/9272) // so we do a quick check here. The precise tolerance amount is just made up. // PI/million happens to fix the bug in 9272, but a larger value is probably // ok too. @@ -1194,7 +1353,7 @@ SkPath& SkPath::arcTo(SkScalar rx, SkScalar ry, SkScalar angle, SkPath::ArcSize unitPts[0] = unitPts[1]; unitPts[0].offset(t * sinEndTheta, -t * cosEndTheta); SkPoint mapped[2]; - pointTransform.mapPoints(mapped, unitPts); + pointTransform.mapPoints(mapped, unitPts, (int) std::size(unitPts)); /* Computing the arc width introduces rounding errors that cause arcs to start outside their marks. A round rect may lose convexity as a result. If the input @@ -1218,7 +1377,8 @@ SkPath& SkPath::arcTo(SkScalar rx, SkScalar ry, SkScalar angle, SkPath::ArcSize SkPath& SkPath::rArcTo(SkScalar rx, SkScalar ry, SkScalar xAxisRotate, SkPath::ArcSize largeArc, SkPathDirection sweep, SkScalar dx, SkScalar dy) { - SkPoint currentPoint = this->getLastPt().value_or(SkPoint{0, 0}); + SkPoint currentPoint; + this->getLastPt(&currentPoint); return this->arcTo(rx, ry, xAxisRotate, largeArc, sweep, currentPoint.fX + dx, currentPoint.fY + dy); } @@ -1259,7 +1419,8 @@ SkPath& SkPath::arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScal } // need to know our prev pt so we can construct tangent vectors - SkPoint start = this->getLastPt().value_or(SkPoint{0, 0}); + SkPoint start; + this->getLastPt(&start); // need double precision for these calcs. skvx::double2 befored = normalize(skvx::double2{x1 - start.fX, y1 - start.fY}); @@ -1301,10 +1462,7 @@ SkPath& SkPath::addPath(const SkPath& srcPath, const SkMatrix& matrix, AddPathMo return *this; } - const bool canReplaceThis = (mode == kAppend_AddPathMode && - SkPathPriv::IsEffectivelyEmpty(*this)) - || this->countVerbs() == 0; - if (canReplaceThis && matrix.isIdentity()) { + if (this->isEmpty() && matrix.isIdentity()) { const uint8_t fillType = fFillType; *this = srcPath; fFillType = fillType; @@ -1313,10 +1471,9 @@ SkPath& SkPath::addPath(const SkPath& srcPath, const SkMatrix& matrix, AddPathMo // Detect if we're trying to add ourself const SkPath* src = &srcPath; - std::optional<SkPath> tmp; + SkTLazy<SkPath> tmp; if (this == src) { - tmp = srcPath; - src = &tmp.value(); + src = tmp.set(srcPath); } if (kAppend_AddPathMode == mode && !matrix.hasPerspective()) { @@ -1327,8 +1484,7 @@ SkPath& SkPath::addPath(const SkPath& srcPath, const SkMatrix& matrix, AddPathMo } SkPathRef::Editor ed(&fPathRef); auto [newPts, newWeights] = ed.growForVerbsInPath(*src->fPathRef); - const size_t N = src->countPoints(); - matrix.mapPoints({newPts, N}, {src->fPathRef->points(), N}); + matrix.mapPoints(newPts, src->fPathRef->points(), src->countPoints()); if (int numWeights = src->fPathRef->countWeights()) { memcpy(newWeights, src->fPathRef->conicWeights(), numWeights * sizeof(newWeights[0])); } @@ -1344,9 +1500,9 @@ SkPath& SkPath::addPath(const SkPath& srcPath, const SkMatrix& matrix, AddPathMo mapPtsProc(matrix, mappedPts, &pts[0], 1); if (firstVerb && mode == kExtend_AddPathMode && !isEmpty()) { injectMoveToIfNeeded(); // In case last contour is closed - auto lastPt = this->getLastPt(); + SkPoint lastPt; // don't add lineTo if it is degenerate - if (!lastPt.has_value() || *lastPt != mappedPts[0]) { + if (!this->getLastPt(&lastPt) || lastPt != mappedPts[0]) { this->lineTo(mappedPts[0]); } } else { @@ -1386,32 +1542,35 @@ SkPath& SkPath::reversePathTo(const SkPath& path) { return *this; } - const SkPathVerb* verbs = path.fPathRef->verbsEnd(); - const SkPathVerb* verbsBegin = path.fPathRef->verbsBegin(); - SkASSERT(verbsBegin[0] == SkPathVerb::kMove); + const uint8_t* verbs = path.fPathRef->verbsEnd(); + const uint8_t* verbsBegin = path.fPathRef->verbsBegin(); + SkASSERT(verbsBegin[0] == kMove_Verb); const SkPoint* pts = path.fPathRef->pointsEnd() - 1; const SkScalar* conicWeights = path.fPathRef->conicWeightsEnd(); while (verbs > verbsBegin) { - SkPathVerb v = *--verbs; + uint8_t v = *--verbs; pts -= SkPathPriv::PtsInVerb(v); switch (v) { - case SkPathVerb::kMove: + case kMove_Verb: // if the path has multiple contours, stop after reversing the last return *this; - case SkPathVerb::kLine: + case kLine_Verb: this->lineTo(pts[0]); break; - case SkPathVerb::kQuad: + case kQuad_Verb: this->quadTo(pts[1], pts[0]); break; - case SkPathVerb::kConic: + case kConic_Verb: this->conicTo(pts[1], pts[0], *--conicWeights); break; - case SkPathVerb::kCubic: + case kCubic_Verb: this->cubicTo(pts[2], pts[1], pts[0]); break; - case SkPathVerb::kClose: + case kClose_Verb: + break; + default: + SkDEBUGFAIL("bad verb"); break; } } @@ -1421,21 +1580,20 @@ SkPath& SkPath::reversePathTo(const SkPath& path) { SkPath& SkPath::reverseAddPath(const SkPath& srcPath) { // Detect if we're trying to add ourself const SkPath* src = &srcPath; - std::optional<SkPath> tmp; + SkTLazy<SkPath> tmp; if (this == src) { - tmp = srcPath; - src = &tmp.value(); + src = tmp.set(srcPath); } - const SkPathVerb* verbsBegin = src->fPathRef->verbsBegin(); - const SkPathVerb* verbs = src->fPathRef->verbsEnd(); + const uint8_t* verbsBegin = src->fPathRef->verbsBegin(); + const uint8_t* verbs = src->fPathRef->verbsEnd(); const SkPoint* pts = src->fPathRef->pointsEnd(); const SkScalar* conicWeights = src->fPathRef->conicWeightsEnd(); bool needMove = true; bool needClose = false; while (verbs > verbsBegin) { - SkPathVerb v = *--verbs; + uint8_t v = *--verbs; int n = SkPathPriv::PtsInVerb(v); if (needMove) { @@ -1445,7 +1603,7 @@ SkPath& SkPath::reverseAddPath(const SkPath& srcPath) { } pts -= n; switch (v) { - case SkPathVerb::kMove: + case kMove_Verb: if (needClose) { this->close(); needClose = false; @@ -1453,21 +1611,23 @@ SkPath& SkPath::reverseAddPath(const SkPath& srcPath) { needMove = true; pts += 1; // so we see the point in "if (needMove)" above break; - case SkPathVerb::kLine: + case kLine_Verb: this->lineTo(pts[0]); break; - case SkPathVerb::kQuad: + case kQuad_Verb: this->quadTo(pts[1], pts[0]); break; - case SkPathVerb::kConic: + case kConic_Verb: this->conicTo(pts[1], pts[0], *--conicWeights); break; - case SkPathVerb::kCubic: + case kCubic_Verb: this->cubicTo(pts[2], pts[1], pts[0]); break; - case SkPathVerb::kClose: + case kClose_Verb: needClose = true; break; + default: + SkDEBUGFAIL("unexpected verb"); } } return *this; @@ -1482,20 +1642,20 @@ void SkPath::offset(SkScalar dx, SkScalar dy, SkPath* dst) const { this->transform(matrix, dst); } -static void subdivide_cubic_to(SkPathBuilder* builder, const SkPoint pts[4], +static void subdivide_cubic_to(SkPath* path, const SkPoint pts[4], int level = 2) { if (--level >= 0) { SkPoint tmp[7]; SkChopCubicAtHalf(pts, tmp); - subdivide_cubic_to(builder, &tmp[0], level); - subdivide_cubic_to(builder, &tmp[3], level); + subdivide_cubic_to(path, &tmp[0], level); + subdivide_cubic_to(path, &tmp[3], level); } else { - builder->cubicTo(pts[1], pts[2], pts[3]); + path->cubicTo(pts[1], pts[2], pts[3]); } } -void SkPath::transform(const SkMatrix& matrix, SkPath* dst) const { +void SkPath::transform(const SkMatrix& matrix, SkPath* dst, SkApplyPerspectiveClip pc) const { if (matrix.isIdentity()) { if (dst != nullptr && dst != this) { *dst = *this; @@ -1509,42 +1669,54 @@ void SkPath::transform(const SkMatrix& matrix, SkPath* dst) const { } if (matrix.hasPerspective()) { + SkPath tmp; + tmp.fFillType = fFillType; SkPath clipped; const SkPath* src = this; - if (SkPathPriv::PerspectiveClip(*this, matrix, &clipped)) { + if (pc == SkApplyPerspectiveClip::kYes && + SkPathPriv::PerspectiveClip(*this, matrix, &clipped)) + { src = &clipped; } - SkPathBuilder tmp(this->getFillType()); - SkPath::Iter iter(*src, false); - while (auto rec = iter.next()) { - const SkSpan<const SkPoint> pts = rec->fPoints; - switch (rec->fVerb) { - case SkPathVerb::kMove: + SkPath::Iter iter(*src, false); + SkPoint pts[4]; + SkPath::Verb verb; + + while ((verb = iter.next(pts)) != kDone_Verb) { + switch (verb) { + case kMove_Verb: tmp.moveTo(pts[0]); break; - case SkPathVerb::kLine: + case kLine_Verb: tmp.lineTo(pts[1]); break; - case SkPathVerb::kQuad: + case kQuad_Verb: // promote the quad to a conic tmp.conicTo(pts[1], pts[2], - SkConic::TransformW(pts.data(), SK_Scalar1, matrix)); + SkConic::TransformW(pts, SK_Scalar1, matrix)); break; - case SkPathVerb::kConic: + case kConic_Verb: tmp.conicTo(pts[1], pts[2], - SkConic::TransformW(pts.data(), rec->conicWeight(), matrix)); + SkConic::TransformW(pts, iter.conicWeight(), matrix)); break; - case SkPathVerb::kCubic: - subdivide_cubic_to(&tmp, pts.data()); + case kCubic_Verb: + subdivide_cubic_to(&tmp, pts); break; - case SkPathVerb::kClose: + case kClose_Verb: tmp.close(); break; + default: + SkDEBUGFAIL("unknown verb"); + break; } } - *dst = tmp.detach(&matrix); + + dst->swap(tmp); + SkPathRef::Editor ed(&dst->fPathRef); + matrix.mapPoints(ed.writablePoints(), ed.pathRef()->countPoints()); + dst->setFirstDirection(SkPathFirstDirection::kUnknown); } else { SkPathConvexity convexity = this->getConvexityOrUnknown(); @@ -1561,24 +1733,29 @@ void SkPath::transform(const SkMatrix& matrix, SkPath* dst) const { // However, some transformations are thought to be safe: // axis-aligned values under scale/translate. // - if (SkPathConvexity_IsConvex(convexity)) { - if (!matrix.isScaleTranslate() || !SkPathPriv::IsAxisAligned(*this)) { - // Not safe to still assume we're convex... - convexity = SkPathConvexity::kUnknown; + if (convexity == SkPathConvexity::kConvex && + (!matrix.isScaleTranslate() || !SkPathPriv::IsAxisAligned(*this))) { + // Not safe to still assume we're convex... + convexity = SkPathConvexity::kUnknown; + } + dst->setConvexity(convexity); + + if (this->getFirstDirection() == SkPathFirstDirection::kUnknown) { + dst->setFirstDirection(SkPathFirstDirection::kUnknown); + } else { + SkScalar det2x2 = + matrix.get(SkMatrix::kMScaleX) * matrix.get(SkMatrix::kMScaleY) - + matrix.get(SkMatrix::kMSkewX) * matrix.get(SkMatrix::kMSkewY); + if (det2x2 < 0) { + dst->setFirstDirection( + SkPathPriv::OppositeFirstDirection( + (SkPathFirstDirection)this->getFirstDirection())); + } else if (det2x2 > 0) { + dst->setFirstDirection(this->getFirstDirection()); } else { - SkScalar det2x2 = - matrix.get(SkMatrix::kMScaleX) * matrix.get(SkMatrix::kMScaleY) - - matrix.get(SkMatrix::kMSkewX) * matrix.get(SkMatrix::kMSkewY); - if (det2x2 < 0) { - convexity = SkPathConvexity_OppositeConvexDirection(convexity); - } else if (det2x2 > 0) { - // we keep our direction - } else /* det2x == 0 */ { - convexity = SkPathConvexity::kConvex_Degenerate; - } + dst->setFirstDirection(SkPathFirstDirection::kUnknown); } } - dst->setConvexity(convexity); SkDEBUGCODE(dst->validate();) } @@ -1626,27 +1803,27 @@ bool SkPath::Iter::isClosedContour() const { return true; } - const SkPathVerb* verbs = fVerbs; - const SkPathVerb* stop = fVerbStop; + const uint8_t* verbs = fVerbs; + const uint8_t* stop = fVerbStop; - if (SkPathVerb::kMove == *verbs) { + if (kMove_Verb == *verbs) { verbs += 1; // skip the initial moveto } while (verbs < stop) { // verbs points one beyond the current verb, decrement first. - SkPathVerb v = *verbs++; - if (SkPathVerb::kMove == v) { + unsigned v = *verbs++; + if (kMove_Verb == v) { break; } - if (SkPathVerb::kClose == v) { + if (kClose_Verb == v) { return true; } } return false; } -SkPathVerb SkPath::Iter::autoClose(SkPoint pts[2]) { +SkPath::Verb SkPath::Iter::autoClose(SkPoint pts[2]) { SkASSERT(pts); if (fLastPt != fMoveTo) { // A special case: if both points are NaN, SkPoint::operation== returns @@ -1654,17 +1831,18 @@ SkPathVerb SkPath::Iter::autoClose(SkPoint pts[2]) { // (consider SkPoint is a 2-dimension float point). if (SkIsNaN(fLastPt.fX) || SkIsNaN(fLastPt.fY) || SkIsNaN(fMoveTo.fX) || SkIsNaN(fMoveTo.fY)) { - return SkPathVerb::kClose; + return kClose_Verb; } pts[0] = fLastPt; pts[1] = fMoveTo; fLastPt = fMoveTo; fCloseLine = true; - return SkPathVerb::kLine; + return kLine_Verb; + } else { + pts[0] = fMoveTo; + return kClose_Verb; } - pts[0] = fMoveTo; - return SkPathVerb::kClose; } SkPath::Verb SkPath::Iter::next(SkPoint ptsParam[4]) { @@ -1673,7 +1851,7 @@ SkPath::Verb SkPath::Iter::next(SkPoint ptsParam[4]) { if (fVerbs == fVerbStop) { // Close the curve if requested and if there is some curve to close if (fNeedClose) { - if (SkPathVerb::kLine == this->autoClose(ptsParam)) { + if (kLine_Verb == this->autoClose(ptsParam)) { return kLine_Verb; } fNeedClose = false; @@ -1682,16 +1860,16 @@ SkPath::Verb SkPath::Iter::next(SkPoint ptsParam[4]) { return kDone_Verb; } - SkPathVerb verb = *fVerbs++; + unsigned verb = *fVerbs++; const SkPoint* SK_RESTRICT srcPts = fPts; SkPoint* SK_RESTRICT pts = ptsParam; switch (verb) { - case SkPathVerb::kMove: + case kMove_Verb: if (fNeedClose) { fVerbs--; // move back one verb verb = this->autoClose(pts); - if (verb == SkPathVerb::kClose) { + if (verb == kClose_Verb) { fNeedClose = false; } return (Verb)verb; @@ -1705,31 +1883,31 @@ SkPath::Verb SkPath::Iter::next(SkPoint ptsParam[4]) { fLastPt = fMoveTo; fNeedClose = fForceClose; break; - case SkPathVerb::kLine: + case kLine_Verb: pts[0] = fLastPt; pts[1] = srcPts[0]; fLastPt = srcPts[0]; fCloseLine = false; srcPts += 1; break; - case SkPathVerb::kConic: + case kConic_Verb: fConicWeights += 1; [[fallthrough]]; - case SkPathVerb::kQuad: + case kQuad_Verb: pts[0] = fLastPt; memcpy(&pts[1], srcPts, 2 * sizeof(SkPoint)); fLastPt = srcPts[1]; srcPts += 2; break; - case SkPathVerb::kCubic: + case kCubic_Verb: pts[0] = fLastPt; memcpy(&pts[1], srcPts, 3 * sizeof(SkPoint)); fLastPt = srcPts[2]; srcPts += 3; break; - case SkPathVerb::kClose: + case kClose_Verb: verb = this->autoClose(pts); - if (verb == SkPathVerb::kLine) { + if (verb == kLine_Verb) { fVerbs--; // move back one verb } else { fNeedClose = false; @@ -1741,27 +1919,6 @@ SkPath::Verb SkPath::Iter::next(SkPoint ptsParam[4]) { return (Verb)verb; } -static inline uint8_t SkPathIterPointsPerVerb(SkPathVerb verb) { - static const uint8_t gCounts[] = { 1, 2, 3, 3, 4, 0 }; - unsigned index = static_cast<unsigned>(verb); - SkASSERT(index < std::size(gCounts)); - return gCounts[index]; -} - -std::optional<SkPath::IterRec> SkPath::Iter::next() { - auto legacyVerb = this->next(fStorage.data()); - if (legacyVerb == kDone_Verb) { - return {}; - } - - SkPathVerb verb = static_cast<SkPathVerb>(legacyVerb); - return {{ - verb, - {fStorage.data(), SkPathIterPointsPerVerb(verb)}, - verb == SkPathVerb::kConic ? *fConicWeights : 1, - }}; -} - void SkPath::RawIter::setPath(const SkPath& path) { SkPathPriv::Iterate iterate(path); fIter = iterate.begin(); @@ -1790,19 +1947,6 @@ SkPath::Verb SkPath::RawIter::next(SkPoint pts[4]) { return (Verb) verb; } -std::optional<SkPath::IterRec> SkPath::RawIter::next() { - if (fIter == fEnd) { - return {}; - } - - auto [verb, iterPts, weights] = *fIter++; - return {{ - verb, - {iterPts, SkPathIterPointsPerVerb(verb)}, - verb == SkPathVerb::kConic ? *weights : 1 - }}; -} - /////////////////////////////////////////////////////////////////////////////// static void append_params(SkString* str, const char label[], const SkPoint pts[], @@ -1842,6 +1986,9 @@ static void append_params(SkString* str, const char label[], const SkPoint pts[] void SkPath::dump(SkWStream* wStream, bool dumpAsHex) const { SkScalarAsStringType asType = dumpAsHex ? kHex_SkScalarAsStringType : kDec_SkScalarAsStringType; + Iter iter(*this, false); + SkPoint pts[4]; + Verb verb; SkString builder; char const * const gFillTypeStrs[] = { @@ -1852,29 +1999,30 @@ void SkPath::dump(SkWStream* wStream, bool dumpAsHex) const { }; builder.printf("path.setFillType(SkPathFillType::k%s);\n", gFillTypeStrs[(int) this->getFillType()]); - - Iter iter(*this, false); - while (auto rec = iter.next()) { - switch (rec->fVerb) { - case SkPathVerb::kMove: - append_params(&builder, "path.moveTo", &rec->fPoints[0], 1, asType); + while ((verb = iter.next(pts)) != kDone_Verb) { + switch (verb) { + case kMove_Verb: + append_params(&builder, "path.moveTo", &pts[0], 1, asType); break; - case SkPathVerb::kLine: - append_params(&builder, "path.lineTo", &rec->fPoints[1], 1, asType); + case kLine_Verb: + append_params(&builder, "path.lineTo", &pts[1], 1, asType); break; - case SkPathVerb::kQuad: - append_params(&builder, "path.quadTo", &rec->fPoints[1], 2, asType); + case kQuad_Verb: + append_params(&builder, "path.quadTo", &pts[1], 2, asType); break; - case SkPathVerb::kConic: - append_params(&builder, "path.conicTo", &rec->fPoints[1], 2, asType, - rec->conicWeight()); + case kConic_Verb: + append_params(&builder, "path.conicTo", &pts[1], 2, asType, iter.conicWeight()); break; - case SkPathVerb::kCubic: - append_params(&builder, "path.cubicTo", &rec->fPoints[1], 3, asType); + case kCubic_Verb: + append_params(&builder, "path.cubicTo", &pts[1], 3, asType); break; - case SkPathVerb::kClose: + case kClose_Verb: builder.append("path.close();\n"); break; + default: + SkDebugf(" path: UNKNOWN VERB %d, aborting dump...\n", verb); + verb = kDone_Verb; // stop the loop + break; } if (!wStream && builder.size()) { SkDebugf("%s", builder.c_str()); @@ -1924,7 +2072,7 @@ void SkPath::dumpArrays(SkWStream* wStream, bool dumpAsHex) const { }; builder.append("const uint8_t path_verbs[] = {\n "); for (auto v = fPathRef->verbsBegin(); v != fPathRef->verbsEnd(); ++v) { - builder.appendf("(uint8_t)SkPathVerb::k%s, ", gVerbStrs[(unsigned)*v]); + builder.appendf("(uint8_t)SkPathVerb::k%s, ", gVerbStrs[*v]); } builder.append("\n};\n"); @@ -2040,10 +2188,10 @@ struct Convexicator { return true; } - static bool IsConcaveBySign(const SkPoint points[], int count) { + static SkPathConvexity BySign(const SkPoint points[], int count) { if (count <= 3) { // point, line, or triangle are always convex - return false; + return SkPathConvexity::kConvex; } const SkPoint* last = points + count; @@ -2059,14 +2207,14 @@ struct Convexicator { if (!vec.isZero()) { // give up if vector construction failed if (!vec.isFinite()) { - return true; // treat as concave + return SkPathConvexity::kUnknown; } int sx = sign(vec.fX); int sy = sign(vec.fY); dxes += (sx != lastSx); dyes += (sy != lastSy); if (dxes > 3 || dyes > 3) { - return true; + return SkPathConvexity::kConcave; } lastSx = sx; lastSy = sy; @@ -2078,7 +2226,7 @@ struct Convexicator { } points = &firstPt; } - return false; // that is, it may be convex, don't know yet + return SkPathConvexity::kConvex; // that is, it may be convex, don't know yet } bool close() { @@ -2153,44 +2301,55 @@ private: bool fIsFinite { true }; }; -static void trim_trailing_moves(SkSpan<const SkPoint>& pts, SkSpan<const SkPathVerb>& vbs) { - size_t vbCount = vbs.size(); - while (vbCount > 0 && vbs[vbCount - 1] == SkPathVerb::kMove) { - vbCount -= 1; - } - if (size_t delta = vbs.size() - vbCount) { - SkASSERT(pts.size() >= delta); - pts = {pts.data(), pts.size() - delta}; - vbs = {vbs.data(), vbs.size() - delta}; - } -} +SkPathConvexity SkPath::computeConvexity() const { + auto setComputedConvexity = [&](SkPathConvexity convexity) { + SkASSERT(SkPathConvexity::kUnknown != convexity); + this->setConvexity(convexity); + return convexity; + }; -SkPathConvexity SkPathPriv::ComputeConvexity(SkSpan<const SkPoint> points, - SkSpan<const SkPathVerb> vbs, - SkSpan<const float> conicWeights) { - // callers need to give us finite values - SkASSERT(SkRect::Bounds(points).has_value()); + auto setFail = [&]() { return setComputedConvexity(SkPathConvexity::kConcave); }; + + if (!this->isFinite()) { + return setFail(); + } - trim_trailing_moves(points, vbs); + // pointCount potentially includes a block of leading moveTos and trailing moveTos. Convexity + // only cares about the last of the initial moveTos and the verbs before the final moveTos. + int pointCount = this->countPoints(); + int skipCount = SkPathPriv::LeadingMoveToCount(*this) - 1; - if (vbs.empty()) { - return SkPathConvexity::kConvex_Degenerate; + if (fLastMoveToIndex >= 0) { + if (fLastMoveToIndex == pointCount - 1) { + // Find the last real verb that affects convexity + auto verbs = fPathRef->verbsEnd() - 1; + while(verbs > fPathRef->verbsBegin() && *verbs == Verb::kMove_Verb) { + verbs--; + pointCount--; + } + } else if (fLastMoveToIndex != skipCount) { + // There's an additional moveTo between two blocks of other verbs, so the path must have + // more than one contour and cannot be convex. + return setComputedConvexity(SkPathConvexity::kConcave); + } // else no trailing or intermediate moveTos to worry about + } + const SkPoint* points = fPathRef->points(); + if (skipCount > 0) { + points += skipCount; + pointCount -= skipCount; } // Check to see if path changes direction more than three times as quick concave test - if (Convexicator::IsConcaveBySign(points.data(), points.size())) { - return SkPathConvexity::kConcave; + SkPathConvexity convexity = Convexicator::BySign(points, pointCount); + if (SkPathConvexity::kConvex != convexity) { + return setComputedConvexity(SkPathConvexity::kConcave); } int contourCount = 0; bool needsClose = false; Convexicator state; - auto iter = SkPathIter(points, vbs, conicWeights); - while (auto rec = iter.next()) { - auto verb = rec->fVerb; - auto pts = rec->fPoints; - + for (auto [verb, pts, wt] : SkPathPriv::Iterate(*this)) { // Looking for the last moveTo before non-move verbs start if (contourCount == 0) { if (verb == SkPathVerb::kMove) { @@ -2205,7 +2364,7 @@ SkPathConvexity SkPathPriv::ComputeConvexity(SkSpan<const SkPoint> points, if (contourCount == 1) { if (verb == SkPathVerb::kClose || verb == SkPathVerb::kMove) { if (!state.close()) { - return SkPathConvexity::kConcave; + return setFail(); } needsClose = false; contourCount++; @@ -2215,7 +2374,7 @@ SkPathConvexity SkPathPriv::ComputeConvexity(SkSpan<const SkPoint> points, SkASSERT(count > 0); for (int i = 1; i <= count; ++i) { if (!state.addPt(pts[i])) { - return SkPathConvexity::kConcave; + return setFail(); } } } @@ -2223,47 +2382,32 @@ SkPathConvexity SkPathPriv::ComputeConvexity(SkSpan<const SkPoint> points, // The first contour has closed and anything other than spurious trailing moves means // there's multiple contours and the path can't be convex if (verb != SkPathVerb::kMove) { - return SkPathConvexity::kConcave; + return setFail(); } } } // If the path isn't explicitly closed do so implicitly if (needsClose && !state.close()) { - return SkPathConvexity::kConcave; - } - - const auto firstDir = state.getFirstDirection(); - if (firstDir == SkPathFirstDirection::kUnknown && state.reversals() >= 3) { - return SkPathConvexity::kConcave; - } - return SkPathFirstDirection_ToConvexity(firstDir); -} - - -SkPathConvexity SkPath::computeConvexity() const { - if (auto c = this->getConvexityOrUnknown(); c != SkPathConvexity::kUnknown) { - return c; + return setFail(); } - SkPathConvexity convexity = SkPathConvexity::kConcave; - - if (this->isFinite()) { - convexity = SkPathPriv::ComputeConvexity(fPathRef->pointSpan(), - fPathRef->verbs(), - fPathRef->conicSpan()); + if (this->getFirstDirection() == SkPathFirstDirection::kUnknown) { + if (state.getFirstDirection() == SkPathFirstDirection::kUnknown + && !this->getBounds().isEmpty()) { + return setComputedConvexity(state.reversals() < 3 ? + SkPathConvexity::kConvex : SkPathConvexity::kConcave); + } + this->setFirstDirection(state.getFirstDirection()); } - - SkASSERT(convexity != SkPathConvexity::kUnknown); - this->setConvexity(convexity); - return convexity; + return setComputedConvexity(SkPathConvexity::kConvex); } /////////////////////////////////////////////////////////////////////////////// class ContourIter { public: - ContourIter(SkSpan<const SkPoint>, SkSpan<const SkPathVerb>, SkSpan<const float> conicWeights); + ContourIter(const SkPathRef& pathRef); bool done() const { return fDone; } // if !done() then these may be called @@ -2274,20 +2418,19 @@ public: private: int fCurrPtCount; const SkPoint* fCurrPt; - const SkPathVerb* fCurrVerb; - const SkPathVerb* fStopVerbs; + const uint8_t* fCurrVerb; + const uint8_t* fStopVerbs; const SkScalar* fCurrConicWeight; bool fDone; SkDEBUGCODE(int fContourCounter;) }; -ContourIter::ContourIter(SkSpan<const SkPoint> pts, SkSpan<const SkPathVerb> vbs, - SkSpan<const float> conicWeights) { - fStopVerbs = vbs.end(); +ContourIter::ContourIter(const SkPathRef& pathRef) { + fStopVerbs = pathRef.verbsEnd(); fDone = false; - fCurrPt = pts.data(); - fCurrVerb = vbs.data(); - fCurrConicWeight = conicWeights.data(); + fCurrPt = pathRef.points(); + fCurrVerb = pathRef.verbsBegin(); + fCurrConicWeight = pathRef.conicWeights(); fCurrPtCount = 0; SkDEBUGCODE(fContourCounter = 0;) this->next(); @@ -2304,27 +2447,30 @@ void ContourIter::next() { // skip pts of prev contour fCurrPt += fCurrPtCount; - SkASSERT(SkPathVerb::kMove == fCurrVerb[0]); + SkASSERT(SkPath::kMove_Verb == fCurrVerb[0]); int ptCount = 1; // moveTo - const SkPathVerb* verbs = fCurrVerb; + const uint8_t* verbs = fCurrVerb; for (verbs++; verbs < fStopVerbs; verbs++) { switch (*verbs) { - case SkPathVerb::kMove: + case SkPath::kMove_Verb: goto CONTOUR_END; - case SkPathVerb::kLine: + case SkPath::kLine_Verb: ptCount += 1; break; - case SkPathVerb::kConic: + case SkPath::kConic_Verb: fCurrConicWeight += 1; [[fallthrough]]; - case SkPathVerb::kQuad: + case SkPath::kQuad_Verb: ptCount += 2; break; - case SkPathVerb::kCubic: + case SkPath::kCubic_Verb: ptCount += 3; break; - case SkPathVerb::kClose: + case SkPath::kClose_Verb: + break; + default: + SkDEBUGFAIL("unexpected verb"); break; } } @@ -2425,11 +2571,23 @@ static SkPathFirstDirection crossToDir(SkScalar cross) { * that is outer most (or at least has the global y-max) before we can consider * its cross product. */ -SkPathFirstDirection SkPathPriv::ComputeFirstDirection(const SkPathRaw& raw) { - ContourIter iter(raw.points(), raw.verbs(), raw.conics()); +SkPathFirstDirection SkPathPriv::ComputeFirstDirection(const SkPath& path) { + auto d = path.getFirstDirection(); + if (d != SkPathFirstDirection::kUnknown) { + return d; + } + + // We don't want to pay the cost for computing convexity if it is unknown, + // so we call getConvexityOrUnknown() instead of isConvex(). + if (path.getConvexityOrUnknown() == SkPathConvexity::kConvex) { + SkASSERT(d == SkPathFirstDirection::kUnknown); + return d; + } + + ContourIter iter(*path.fPathRef); // initialize with our logical y-min - SkScalar ymax = raw.bounds().fTop; + SkScalar ymax = path.getBounds().fTop; SkScalar ymaxCross = 0; for (; !iter.done(); iter.next()) { @@ -2493,21 +2651,11 @@ SkPathFirstDirection SkPathPriv::ComputeFirstDirection(const SkPathRaw& raw) { ymaxCross = cross; } } - - return ymaxCross ? crossToDir(ymaxCross) : SkPathFirstDirection::kUnknown; -} - -SkPathFirstDirection SkPathPriv::ComputeFirstDirection(const SkPath& path) { - auto convexity = path.getConvexityOrUnknown(); - if (SkPathConvexity_IsConvex(convexity)) { - // Note, this can return kUnknown. That is valid. If we've determined that the - // path is convex, then we've already tried to compute its first-direction. If - // that failed, then kUnknown is the right answer. - return SkPathConvexity_ToFirstDirection(convexity); + if (ymaxCross) { + d = crossToDir(ymaxCross); + path.setFirstDirection(d); } - - // Note, this can compute a 'first' direction, even for non-convex shapes. - return ComputeFirstDirection(SkPathPriv::Raw(path)); + return d; // may still be kUnknown } /////////////////////////////////////////////////////////////////////////////// @@ -2593,9 +2741,9 @@ static int winding_mono_cubic(const SkPoint pts[], SkScalar x, SkScalar y, int* return xt < x ? dir : 0; } -static int winding_cubic(SkSpan<const SkPoint> pts, SkScalar x, SkScalar y, int* onCurveCount) { +static int winding_cubic(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) { SkPoint dst[10]; - int n = SkChopCubicAtYExtrema(pts.data(), dst); + int n = SkChopCubicAtYExtrema(pts, dst); int w = 0; for (int i = 0; i <= n; ++i) { w += winding_mono_cubic(&dst[i * 3], x, y, onCurveCount); @@ -2683,9 +2831,9 @@ static bool is_mono_quad(SkScalar y0, SkScalar y1, SkScalar y2) { } } -static int winding_conic(SkSpan<const SkPoint> pts, SkScalar x, SkScalar y, SkScalar weight, +static int winding_conic(const SkPoint pts[], SkScalar x, SkScalar y, SkScalar weight, int* onCurveCount) { - SkConic conic(pts.data(), weight); + SkConic conic(pts, weight); SkConic chopped[2]; // If the data points are very large, the conic may not be monotonic but may also // fail to chop. Then, the chopper does not split the original conic in two. @@ -2697,7 +2845,7 @@ static int winding_conic(SkSpan<const SkPoint> pts, SkScalar x, SkScalar y, SkSc return w; } -static int winding_mono_quad(SkSpan<const SkPoint> pts, SkScalar x, SkScalar y, int* onCurveCount) { +static int winding_mono_quad(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) { SkScalar y0 = pts[0].fY; SkScalar y2 = pts[2].fY; @@ -2752,23 +2900,22 @@ static int winding_mono_quad(SkSpan<const SkPoint> pts, SkScalar x, SkScalar y, return xt < x ? dir : 0; } -static int winding_quad(SkSpan<const SkPoint> pts, SkScalar x, SkScalar y, int* onCurveCount) { - SkPoint spanStorage[5]; - SkSpan<const SkPoint> span = pts; - int n = 0; +static int winding_quad(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) { + SkPoint dst[5]; + int n = 0; if (!is_mono_quad(pts[0].fY, pts[1].fY, pts[2].fY)) { - n = SkChopQuadAtYExtrema(pts.data(), spanStorage); - span = spanStorage; + n = SkChopQuadAtYExtrema(pts, dst); + pts = dst; } - int w = winding_mono_quad(span, x, y, onCurveCount); + int w = winding_mono_quad(pts, x, y, onCurveCount); if (n > 0) { - w += winding_mono_quad(span.subspan(2), x, y, onCurveCount); + w += winding_mono_quad(&pts[2], x, y, onCurveCount); } return w; } -static int winding_line(SkSpan<const SkPoint> pts, SkScalar x, SkScalar y, int* onCurveCount) { +static int winding_line(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) { SkScalar x0 = pts[0].fX; SkScalar y0 = pts[0].fY; SkScalar x1 = pts[1].fX; @@ -2808,7 +2955,7 @@ static int winding_line(SkSpan<const SkPoint> pts, SkScalar x, SkScalar y, int* return dir; } -static void tangent_cubic(SkSpan<const SkPoint> pts, SkScalar x, SkScalar y, +static void tangent_cubic(const SkPoint pts[], SkScalar x, SkScalar y, SkTDArray<SkVector>* tangents) { if (!between(pts[0].fY, y, pts[1].fY) && !between(pts[1].fY, y, pts[2].fY) && !between(pts[2].fY, y, pts[3].fY)) { @@ -2819,7 +2966,7 @@ static void tangent_cubic(SkSpan<const SkPoint> pts, SkScalar x, SkScalar y, return; } SkPoint dst[10]; - int n = SkChopCubicAtYExtrema(pts.data(), dst); + int n = SkChopCubicAtYExtrema(pts, dst); for (int i = 0; i <= n; ++i) { SkPoint* c = &dst[i * 3]; SkScalar t; @@ -2836,7 +2983,7 @@ static void tangent_cubic(SkSpan<const SkPoint> pts, SkScalar x, SkScalar y, } } -static void tangent_conic(SkSpan<const SkPoint> pts, SkScalar x, SkScalar y, SkScalar w, +static void tangent_conic(const SkPoint pts[], SkScalar x, SkScalar y, SkScalar w, SkTDArray<SkVector>* tangents) { if (!between(pts[0].fY, y, pts[1].fY) && !between(pts[1].fY, y, pts[2].fY)) { return; @@ -2858,12 +3005,12 @@ static void tangent_conic(SkSpan<const SkPoint> pts, SkScalar x, SkScalar y, SkS if (!SkScalarNearlyEqual(x, xt)) { continue; } - SkConic conic(pts.data(), w); + SkConic conic(pts, w); tangents->push_back(conic.evalTangentAt(t)); } } -static void tangent_quad(SkSpan<const SkPoint> pts, SkScalar x, SkScalar y, +static void tangent_quad(const SkPoint pts[], SkScalar x, SkScalar y, SkTDArray<SkVector>* tangents) { if (!between(pts[0].fY, y, pts[1].fY) && !between(pts[1].fY, y, pts[2].fY)) { return; @@ -2885,11 +3032,11 @@ static void tangent_quad(SkSpan<const SkPoint> pts, SkScalar x, SkScalar y, if (!SkScalarNearlyEqual(x, xt)) { continue; } - tangents->push_back(SkEvalQuadTangentAt(pts.data(), t)); + tangents->push_back(SkEvalQuadTangentAt(pts, t)); } } -static void tangent_line(SkSpan<const SkPoint> pts, SkScalar x, SkScalar y, +static void tangent_line(const SkPoint pts[], SkScalar x, SkScalar y, SkTDArray<SkVector>* tangents) { SkScalar y0 = pts[0].fY; SkScalar y1 = pts[1].fY; @@ -2926,27 +3073,32 @@ bool SkPath::contains(SkScalar x, SkScalar y) const { } SkPath::Iter iter(*this, true); + bool done = false; int w = 0; int onCurveCount = 0; - while (auto rec = iter.next()) { - switch (rec->fVerb) { - case SkPathVerb::kMove: - case SkPathVerb::kClose: + do { + SkPoint pts[4]; + switch (iter.next(pts)) { + case SkPath::kMove_Verb: + case SkPath::kClose_Verb: break; - case SkPathVerb::kLine: - w += winding_line(rec->fPoints, x, y, &onCurveCount); + case SkPath::kLine_Verb: + w += winding_line(pts, x, y, &onCurveCount); break; - case SkPathVerb::kQuad: - w += winding_quad(rec->fPoints, x, y, &onCurveCount); + case SkPath::kQuad_Verb: + w += winding_quad(pts, x, y, &onCurveCount); break; - case SkPathVerb::kConic: - w += winding_conic(rec->fPoints, x, y, rec->conicWeight(), &onCurveCount); + case SkPath::kConic_Verb: + w += winding_conic(pts, x, y, iter.conicWeight(), &onCurveCount); break; - case SkPathVerb::kCubic: - w += winding_cubic(rec->fPoints, x, y, &onCurveCount); + case SkPath::kCubic_Verb: + w += winding_cubic(pts, x, y, &onCurveCount); + break; + case SkPath::kDone_Verb: + done = true; break; } - } + } while (!done); bool evenOddFill = SkPathFillType::kEvenOdd == this->getFillType() || SkPathFillType::kInverseEvenOdd == this->getFillType(); if (evenOddFill) { @@ -2964,24 +3116,29 @@ bool SkPath::contains(SkScalar x, SkScalar y) const { // If the point touches an even number of curves, and the fill is winding, check for // coincidence. Count coincidence as places where the on curve points have identical tangents. iter.setPath(*this, true); + done = false; SkTDArray<SkVector> tangents; - while (auto rec = iter.next()) { + do { + SkPoint pts[4]; int oldCount = tangents.size(); - switch (rec->fVerb) { - case SkPathVerb::kMove: - case SkPathVerb::kClose: + switch (iter.next(pts)) { + case SkPath::kMove_Verb: + case SkPath::kClose_Verb: break; - case SkPathVerb::kLine: - tangent_line(rec->fPoints, x, y, &tangents); + case SkPath::kLine_Verb: + tangent_line(pts, x, y, &tangents); break; - case SkPathVerb::kQuad: - tangent_quad(rec->fPoints, x, y, &tangents); + case SkPath::kQuad_Verb: + tangent_quad(pts, x, y, &tangents); break; - case SkPathVerb::kConic: - tangent_conic(rec->fPoints, x, y, rec->conicWeight(), &tangents); + case SkPath::kConic_Verb: + tangent_conic(pts, x, y, iter.conicWeight(), &tangents); break; - case SkPathVerb::kCubic: - tangent_cubic(rec->fPoints, x, y, &tangents); + case SkPath::kCubic_Verb: + tangent_cubic(pts, x, y, &tangents); + break; + case SkPath::kDone_Verb: + done = true; break; } if (tangents.size() > oldCount) { @@ -3002,7 +3159,7 @@ bool SkPath::contains(SkScalar x, SkScalar y) const { } } } - } + } while (!done); return SkToBool(tangents.size()) ^ isInverse; } @@ -3031,9 +3188,10 @@ int SkPath::ConvertConicToQuads(const SkPoint& p0, const SkPoint& p1, const SkPo return conic.chopIntoQuadsPOW2(pts, pow2); } -std::optional<SkPathRectInfo> SkPathPriv::IsSimpleRect(const SkPath& path, bool isSimpleFill) { +bool SkPathPriv::IsSimpleRect(const SkPath& path, bool isSimpleFill, SkRect* rect, + SkPathDirection* direction, unsigned* start) { if (path.getSegmentMasks() != SkPath::kLine_SegmentMask) { - return {}; + return false; } SkPoint rectPts[5]; int rectPtCnt = 0; @@ -3042,14 +3200,14 @@ std::optional<SkPathRectInfo> SkPathPriv::IsSimpleRect(const SkPath& path, bool switch (v) { case SkPathVerb::kMove: if (0 != rectPtCnt) { - return {}; + return false; } rectPts[0] = verbPts[0]; ++rectPtCnt; break; case SkPathVerb::kLine: if (5 == rectPtCnt) { - return {}; + return false; } rectPts[rectPtCnt] = verbPts[1]; ++rectPtCnt; @@ -3064,17 +3222,17 @@ std::optional<SkPathRectInfo> SkPathPriv::IsSimpleRect(const SkPath& path, bool case SkPathVerb::kQuad: case SkPathVerb::kConic: case SkPathVerb::kCubic: - return {}; + return false; } } if (needsClose) { - return {}; + return false; } if (rectPtCnt < 5) { - return {}; + return false; } if (rectPts[0] != rectPts[4]) { - return {}; + return false; } // Check for two cases of rectangles: pts 0 and 3 form a vertical edge or a horizontal edge ( // and pts 1 and 2 the opposite vertical or horizontal edge). @@ -3083,22 +3241,19 @@ std::optional<SkPathRectInfo> SkPathPriv::IsSimpleRect(const SkPath& path, bool rectPts[0].fY == rectPts[1].fY && rectPts[3].fY == rectPts[2].fY) { // Make sure it has non-zero width and height if (rectPts[0].fX == rectPts[1].fX || rectPts[0].fY == rectPts[3].fY) { - return {}; + return false; } vec03IsVertical = true; } else if (rectPts[0].fY == rectPts[3].fY && rectPts[1].fY == rectPts[2].fY && rectPts[0].fX == rectPts[1].fX && rectPts[3].fX == rectPts[2].fX) { // Make sure it has non-zero width and height if (rectPts[0].fY == rectPts[1].fY || rectPts[0].fX == rectPts[3].fX) { - return {}; + return false; } vec03IsVertical = false; } else { - return {}; + return false; } - - SkPathRectInfo info; - // Set sortFlags so that it has the low bit set if pt index 0 is on right edge and second bit // set if it is on the bottom edge. unsigned sortFlags = @@ -3106,27 +3261,27 @@ std::optional<SkPathRectInfo> SkPathPriv::IsSimpleRect(const SkPath& path, bool ((rectPts[0].fY < rectPts[2].fY) ? 0b00 : 0b10); switch (sortFlags) { case 0b00: - info.fRect.setLTRB(rectPts[0].fX, rectPts[0].fY, rectPts[2].fX, rectPts[2].fY); - info.fDirection = vec03IsVertical ? SkPathDirection::kCW : SkPathDirection::kCCW; - info.fStartIndex = 0; + rect->setLTRB(rectPts[0].fX, rectPts[0].fY, rectPts[2].fX, rectPts[2].fY); + *direction = vec03IsVertical ? SkPathDirection::kCW : SkPathDirection::kCCW; + *start = 0; break; case 0b01: - info.fRect.setLTRB(rectPts[2].fX, rectPts[0].fY, rectPts[0].fX, rectPts[2].fY); - info.fDirection = vec03IsVertical ? SkPathDirection::kCCW : SkPathDirection::kCW; - info.fStartIndex = 1; + rect->setLTRB(rectPts[2].fX, rectPts[0].fY, rectPts[0].fX, rectPts[2].fY); + *direction = vec03IsVertical ? SkPathDirection::kCCW : SkPathDirection::kCW; + *start = 1; break; case 0b10: - info.fRect.setLTRB(rectPts[0].fX, rectPts[2].fY, rectPts[2].fX, rectPts[0].fY); - info.fDirection = vec03IsVertical ? SkPathDirection::kCCW : SkPathDirection::kCW; - info.fStartIndex = 3; + rect->setLTRB(rectPts[0].fX, rectPts[2].fY, rectPts[2].fX, rectPts[0].fY); + *direction = vec03IsVertical ? SkPathDirection::kCCW : SkPathDirection::kCW; + *start = 3; break; case 0b11: - info.fRect.setLTRB(rectPts[2].fX, rectPts[2].fY, rectPts[0].fX, rectPts[0].fY); - info.fDirection = vec03IsVertical ? SkPathDirection::kCW : SkPathDirection::kCCW; - info.fStartIndex = 2; + rect->setLTRB(rectPts[2].fX, rectPts[2].fY, rectPts[0].fX, rectPts[0].fY); + *direction = vec03IsVertical ? SkPathDirection::kCW : SkPathDirection::kCCW; + *start = 2; break; } - return info; + return true; } bool SkPathPriv::DrawArcIsConvex(SkScalar sweepAngle, @@ -3145,7 +3300,7 @@ bool SkPathPriv::DrawArcIsConvex(SkScalar sweepAngle, return SkScalarAbs(sweepAngle) <= 360.f; } -SkPath SkPathPriv::CreateDrawArcPath(const SkArc& arc, bool isFillNoPathEffect) { +void SkPathPriv::CreateDrawArcPath(SkPath* path, const SkArc& arc, bool isFillNoPathEffect) { SkRect oval = arc.fOval; SkScalar startAngle = arc.fStartAngle, sweepAngle = arc.fSweepAngle; SkASSERT(!oval.isEmpty()); @@ -3155,18 +3310,17 @@ SkPath SkPathPriv::CreateDrawArcPath(const SkArc& arc, bool isFillNoPathEffect) if (SkScalarAbs(sweepAngle) > 3600.0f) { sweepAngle = std::copysign(3600.0f, sweepAngle) + std::fmod(sweepAngle, 360.0f); } - - SkPathBuilder builder(SkPathFillType::kWinding); - builder.setIsVolatile(true); - + path->reset(); + path->setIsVolatile(true); + path->setFillType(SkPathFillType::kWinding); if (isFillNoPathEffect && SkScalarAbs(sweepAngle) >= 360.f) { - builder.addOval(oval); - SkASSERT(DrawArcIsConvex(sweepAngle, SkArc::Type::kArc, isFillNoPathEffect)); - return builder.detach(); + path->addOval(oval); + SkASSERT(path->isConvex() && + DrawArcIsConvex(sweepAngle, SkArc::Type::kArc, isFillNoPathEffect)); + return; } - if (arc.isWedge()) { - builder.moveTo(oval.centerX(), oval.centerY()); + path->moveTo(oval.centerX(), oval.centerY()); } auto firstDir = sweepAngle > 0 ? SkPathFirstDirection::kCW : SkPathFirstDirection::kCCW; @@ -3174,31 +3328,27 @@ SkPath SkPathPriv::CreateDrawArcPath(const SkArc& arc, bool isFillNoPathEffect) // Arc to mods at 360 and drawArc is not supposed to. bool forceMoveTo = !arc.isWedge(); while (sweepAngle <= -360.f) { - builder.arcTo(oval, startAngle, -180.f, forceMoveTo); + path->arcTo(oval, startAngle, -180.f, forceMoveTo); startAngle -= 180.f; - builder.arcTo(oval, startAngle, -180.f, false); + path->arcTo(oval, startAngle, -180.f, false); startAngle -= 180.f; forceMoveTo = false; sweepAngle += 360.f; } while (sweepAngle >= 360.f) { - builder.arcTo(oval, startAngle, 180.f, forceMoveTo); + path->arcTo(oval, startAngle, 180.f, forceMoveTo); startAngle += 180.f; - builder.arcTo(oval, startAngle, 180.f, false); + path->arcTo(oval, startAngle, 180.f, false); startAngle += 180.f; forceMoveTo = false; sweepAngle -= 360.f; } - builder.arcTo(oval, startAngle, sweepAngle, forceMoveTo); + path->arcTo(oval, startAngle, sweepAngle, forceMoveTo); if (arc.isWedge()) { - builder.close(); + path->close(); } - - auto path = builder.detach(); - const auto convexity = convex ? SkPathFirstDirection_ToConvexity(firstDir) - : SkPathConvexity::kConcave; - path.setConvexity(convexity); - return path; + path->setConvexity(convex ? SkPathConvexity::kConvex : SkPathConvexity::kConcave); + path->setFirstDirection(firstDir); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -3309,12 +3459,12 @@ bool SkPath::IsCubicDegenerate(const SkPoint& p1, const SkPoint& p2, ////////////////////////////////////////////////////////////////////////////////////////////////// -SkPathVerbAnalysis SkPathPriv::AnalyzeVerbs(SkSpan<const SkPathVerb> vbs) { +SkPathVerbAnalysis SkPathPriv::AnalyzeVerbs(const uint8_t vbs[], int verbCount) { SkPathVerbAnalysis info = {false, 0, 0, 0}; bool needMove = true; bool invalid = false; - if (vbs.size() >= (INT_MAX / 3)) SK_UNLIKELY { + if (verbCount >= (INT_MAX / 3)) SK_UNLIKELY { // A path with an extremely high number of quad, conic or cubic verbs could cause // `info.points` to overflow. To prevent against this, we reject extremely large paths. This // check is conservative and assumes the worst case (in particular, it assumes that every @@ -3322,8 +3472,8 @@ SkPathVerbAnalysis SkPathPriv::AnalyzeVerbs(SkSpan<const SkPathVerb> vbs) { // This limits us to 700 million verbs, which is large enough for any reasonable use case. invalid = true; } else { - for (auto v : vbs) { - switch (v) { + for (int i = 0; i < verbCount; ++i) { + switch ((SkPathVerb)vbs[i]) { case SkPathVerb::kMove: needMove = false; info.points += 1; @@ -3363,19 +3513,21 @@ SkPathVerbAnalysis SkPathPriv::AnalyzeVerbs(SkSpan<const SkPathVerb> vbs) { return info; } -SkPath SkPath::Raw(SkSpan<const SkPoint> pts, SkSpan<const SkPathVerb> vbs, - SkSpan<const float> ws, SkPathFillType ft, bool isVolatile) { - if (vbs.empty()) { +SkPath SkPath::Make(const SkPoint pts[], int pointCount, + const uint8_t vbs[], int verbCount, + const SkScalar ws[], int wCount, + SkPathFillType ft, bool isVolatile) { + if (verbCount <= 0) { return SkPath(); } - const auto info = SkPathPriv::AnalyzeVerbs(vbs); - if (!info.valid || info.points > pts.size() || info.weights > ws.size()) { + 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(); } - return MakeInternal(info, pts.data(), vbs, ws.data(), ft, isVolatile); + return MakeInternal(info, pts, vbs, verbCount, ws, ft, isVolatile); } SkPath SkPath::Rect(const SkRect& r, SkPathDirection dir, unsigned startIndex) { @@ -3406,9 +3558,9 @@ SkPath SkPath::RRect(const SkRect& r, SkScalar rx, SkScalar ry, SkPathDirection return SkPathBuilder().addRRect(SkRRect::MakeRectXY(r, rx, ry), dir).detach(); } -SkPath SkPath::Polygon(SkSpan<const SkPoint> pts, bool isClosed, +SkPath SkPath::Polygon(const SkPoint pts[], int count, bool isClosed, SkPathFillType ft, bool isVolatile) { - return SkPathBuilder().addPolygon(pts, isClosed) + return SkPathBuilder().addPolygon(pts, count, isClosed) .setFillType(ft) .setIsVolatile(isVolatile) .detach(); @@ -3416,17 +3568,199 @@ SkPath SkPath::Polygon(SkSpan<const SkPoint> pts, bool isClosed, SkPath SkPath::MakeInternal(const SkPathVerbAnalysis& analysis, const SkPoint points[], - SkSpan<const SkPathVerb> verbs, + const uint8_t verbs[], + int verbCount, const SkScalar conics[], SkPathFillType fillType, bool isVolatile) { return SkPath(sk_sp<SkPathRef>(new SkPathRef( SkSpan(points, analysis.points), - verbs, + SkSpan(verbs, verbCount), SkSpan(conics, analysis.weights), - analysis.segmentMask, - nullptr)), - fillType, isVolatile, SkPathConvexity::kUnknown); + analysis.segmentMask)), + fillType, isVolatile, SkPathConvexity::kUnknown, SkPathFirstDirection::kUnknown); +} + +////////////////////////////////////////////////////////////////////////////////////////////////// + +bool SkPathPriv::IsRectContour(const SkPath& path, bool allowPartial, int* currVerb, + const SkPoint** ptsPtr, bool* isClosed, SkPathDirection* direction, + SkRect* rect) { + int corners = 0; + SkPoint closeXY; // used to determine if final line falls on a diagonal + SkPoint lineStart; // used to construct line from previous point + const SkPoint* firstPt = nullptr; // first point in the rect (last of first moves) + const SkPoint* lastPt = nullptr; // last point in the rect (last of lines or first if closed) + SkPoint firstCorner; + SkPoint thirdCorner; + const SkPoint* pts = *ptsPtr; + const SkPoint* savePts = nullptr; // used to allow caller to iterate through a pair of rects + lineStart.set(0, 0); + signed char directions[] = {-1, -1, -1, -1, -1}; // -1 to 3; -1 is uninitialized + bool closedOrMoved = false; + bool autoClose = false; + bool insertClose = false; + int verbCnt = path.fPathRef->countVerbs(); + while (*currVerb < verbCnt && (!allowPartial || !autoClose)) { + uint8_t verb = insertClose ? (uint8_t) SkPath::kClose_Verb : path.fPathRef->atVerb(*currVerb); + switch (verb) { + case SkPath::kClose_Verb: + savePts = pts; + autoClose = true; + insertClose = false; + [[fallthrough]]; + case SkPath::kLine_Verb: { + if (SkPath::kClose_Verb != verb) { + lastPt = pts; + } + SkPoint lineEnd = SkPath::kClose_Verb == verb ? *firstPt : *pts++; + SkVector lineDelta = lineEnd - lineStart; + if (lineDelta.fX && lineDelta.fY) { + return false; // diagonal + } + if (!lineDelta.isFinite()) { + return false; // path contains infinity or NaN + } + if (lineStart == lineEnd) { + break; // single point on side OK + } + int nextDirection = rect_make_dir(lineDelta.fX, lineDelta.fY); // 0 to 3 + if (0 == corners) { + directions[0] = nextDirection; + corners = 1; + closedOrMoved = false; + lineStart = lineEnd; + break; + } + if (closedOrMoved) { + return false; // closed followed by a line + } + if (autoClose && nextDirection == directions[0]) { + break; // colinear with first + } + closedOrMoved = autoClose; + if (directions[corners - 1] == nextDirection) { + if (3 == corners && SkPath::kLine_Verb == verb) { + thirdCorner = lineEnd; + } + lineStart = lineEnd; + break; // colinear segment + } + directions[corners++] = nextDirection; + // opposite lines must point in opposite directions; xoring them should equal 2 + switch (corners) { + case 2: + firstCorner = lineStart; + break; + case 3: + if ((directions[0] ^ directions[2]) != 2) { + return false; + } + thirdCorner = lineEnd; + break; + case 4: + if ((directions[1] ^ directions[3]) != 2) { + return false; + } + break; + default: + return false; // too many direction changes + } + lineStart = lineEnd; + break; + } + case SkPath::kQuad_Verb: + case SkPath::kConic_Verb: + case SkPath::kCubic_Verb: + return false; // quadratic, cubic not allowed + case SkPath::kMove_Verb: + if (allowPartial && !autoClose && directions[0] >= 0) { + insertClose = true; + *currVerb -= 1; // try move again afterwards + goto addMissingClose; + } + if (pts != *ptsPtr) { + return false; + } + if (!corners) { + firstPt = pts; + } else { + closeXY = *firstPt - *lastPt; + if (closeXY.fX && closeXY.fY) { + return false; // we're diagonal, abort + } + } + lineStart = *pts++; + closedOrMoved = true; + break; + default: + SkDEBUGFAIL("unexpected verb"); + break; + } + *currVerb += 1; + addMissingClose: + ; + } + // Success if 4 corners and first point equals last + if (corners < 3 || corners > 4) { + return false; + } + if (savePts) { + *ptsPtr = savePts; + } + // check if close generates diagonal + closeXY = *firstPt - *lastPt; + if (closeXY.fX && closeXY.fY) { + return false; + } + if (rect) { + rect->set(firstCorner, thirdCorner); + } + if (isClosed) { + *isClosed = autoClose; + } + if (direction) { + *direction = directions[0] == ((directions[1] + 1) & 3) ? + SkPathDirection::kCW : SkPathDirection::kCCW; + } + return true; +} + + +bool SkPathPriv::IsNestedFillRects(const SkPath& path, SkRect rects[2], SkPathDirection dirs[2]) { + SkDEBUGCODE(path.validate();) + int currVerb = 0; + const SkPoint* pts = path.fPathRef->points(); + SkPathDirection testDirs[2]; + SkRect testRects[2]; + if (!IsRectContour(path, true, &currVerb, &pts, nullptr, &testDirs[0], &testRects[0])) { + return false; + } + if (IsRectContour(path, false, &currVerb, &pts, nullptr, &testDirs[1], &testRects[1])) { + if (testRects[0].contains(testRects[1])) { + if (rects) { + rects[0] = testRects[0]; + rects[1] = testRects[1]; + } + if (dirs) { + dirs[0] = testDirs[0]; + dirs[1] = testDirs[1]; + } + return true; + } + if (testRects[1].contains(testRects[0])) { + if (rects) { + rects[0] = testRects[1]; + rects[1] = testRects[0]; + } + if (dirs) { + dirs[0] = testDirs[1]; + dirs[1] = testDirs[0]; + } + return true; + } + } + return false; } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -3504,20 +3838,21 @@ struct SkHalfPlane { }; // assumes plane is pre-normalized -static std::optional<SkPath> clip(const SkPath& path, const SkHalfPlane& plane) { - SkMatrix mx; +// If we fail in our calculations, we return the empty path +static SkPath clip(const SkPath& path, const SkHalfPlane& plane) { + SkMatrix mx, inv; SkPoint p0 = { -plane.fA*plane.fC, -plane.fB*plane.fC }; mx.setAll( plane.fB, plane.fA, p0.fX, -plane.fA, plane.fB, p0.fY, 0, 0, 1); - auto inv = mx.invert(); - if (!inv) { - return {}; + if (!mx.invert(&inv)) { + return SkPath(); } - SkPath rotated = path.makeTransform(*inv); + SkPath rotated; + path.transform(inv, &rotated); if (!rotated.isFinite()) { - return {}; + return SkPath(); } SkScalar big = SK_ScalarMax; @@ -3528,13 +3863,14 @@ static std::optional<SkPath> clip(const SkPath& path, const SkHalfPlane& plane) SkPoint fPrev = {0,0}; } rec; - SkEdgeClipper::ClipPath(SkPathPriv::Raw(rotated), clip, false, + SkEdgeClipper::ClipPath(rotated, clip, false, [](SkEdgeClipper* clipper, bool newCtr, void* ctx) { Rec* rec = (Rec*)ctx; bool addLineTo = false; SkPoint pts[4]; - while (auto verb = clipper->next(pts)) { + SkPath::Verb verb; + while ((verb = clipper->next(pts)) != SkPath::kDone_Verb) { if (newCtr) { rec->fResult.moveTo(pts[0]); rec->fPrev = pts[0]; @@ -3545,16 +3881,16 @@ static std::optional<SkPath> clip(const SkPath& path, const SkHalfPlane& plane) rec->fResult.lineTo(pts[0]); } - switch (*verb) { - case SkPathVerb::kLine: + switch (verb) { + case SkPath::kLine_Verb: rec->fResult.lineTo(pts[1]); rec->fPrev = pts[1]; break; - case SkPathVerb::kQuad: + case SkPath::kQuad_Verb: rec->fResult.quadTo(pts[1], pts[2]); rec->fPrev = pts[2]; break; - case SkPathVerb::kCubic: + case SkPath::kCubic_Verb: rec->fResult.cubicTo(pts[1], pts[2], pts[3]); rec->fPrev = pts[3]; break; @@ -3565,9 +3901,9 @@ static std::optional<SkPath> clip(const SkPath& path, const SkHalfPlane& plane) }, &rec); rec.fResult.setFillType(path.getFillType()); - SkPath result = rec.fResult.detach(&mx); + SkPath result = rec.fResult.detach().makeTransform(mx); if (!result.isFinite()) { - return {}; + result = SkPath(); } return result; } @@ -3588,11 +3924,7 @@ bool SkPathPriv::PerspectiveClip(const SkPath& path, const SkMatrix& matrix, SkP case SkHalfPlane::kAllPositive: return false; case SkHalfPlane::kMixed: { - if (auto result = clip(path, plane)) { - *clippedPath = *result; - } else { - *clippedPath = SkPath(); // clipped out (or failed) - } + *clippedPath = clip(path, plane); return true; } default: break; // handled outside of the switch @@ -3607,12 +3939,15 @@ int SkPathPriv::GenIDChangeListenersCount(const SkPath& path) { return path.fPathRef->genIDChangeListenerCount(); } -bool SkPathPriv::IsAxisAligned(SkSpan<const SkPoint> pts) { +bool SkPathPriv::IsAxisAligned(const SkPath& path) { // Conservative (quick) test to see if all segments are axis-aligned. // Multiple contours might give a false-negative, but for speed, we ignore that // and just look at the raw points. - for (size_t i = 1; i < pts.size(); ++i) { + const SkPoint* pts = path.fPathRef->points(); + const int count = path.fPathRef->countPoints(); + + for (int i = 1; i < count; ++i) { if (pts[i-1].fX != pts[i].fX && pts[i-1].fY != pts[i].fY) { return false; } @@ -3620,17 +3955,13 @@ bool SkPathPriv::IsAxisAligned(SkSpan<const SkPoint> pts) { return true; } -bool SkPathPriv::IsAxisAligned(const SkPath& path) { - return IsAxisAligned(path.fPathRef->pointSpan()); -} - ////////////////////////////////////////////////////////////////////////////////////////////////// -SkPathEdgeIter::SkPathEdgeIter(const SkPathRaw& raw) { - fMoveToPtr = fPts = raw.fPoints.begin(); - fVerbs = raw.fVerbs.begin(); - fVerbsStop = raw.fVerbs.end(); - fConicWeights = raw.fConics.begin(); +SkPathEdgeIter::SkPathEdgeIter(const SkPath& path) { + fMoveToPtr = fPts = path.fPathRef->points(); + fVerbs = path.fPathRef->verbsBegin(); + fVerbsStop = path.fPathRef->verbsEnd(); + fConicWeights = path.fPathRef->conicWeights(); if (fConicWeights) { fConicWeights -= 1; // begin one behind } @@ -3639,5 +3970,3 @@ SkPathEdgeIter::SkPathEdgeIter(const SkPathRaw& raw) { fNextIsNewContour = false; SkDEBUGCODE(fIsConic = false;) } - -SkPathEdgeIter::SkPathEdgeIter(const SkPath& path) : SkPathEdgeIter(SkPathPriv::Raw(path)) {} diff --git a/gfx/skia/skia/src/core/SkPathBuilder.cpp b/gfx/skia/skia/src/core/SkPathBuilder.cpp @@ -8,45 +8,22 @@ #include "include/core/SkPathBuilder.h" #include "include/core/SkMatrix.h" -#include "include/core/SkPath.h" -#include "include/core/SkPathTypes.h" #include "include/core/SkRRect.h" -#include "include/core/SkTypes.h" #include "include/private/SkPathRef.h" -#include "include/private/base/SkAssert.h" // IWYU pragma: keep #include "include/private/base/SkFloatingPoint.h" #include "include/private/base/SkSafe32.h" -#include "include/private/base/SkTArray.h" -#include "include/private/base/SkTo.h" #include "src/base/SkVx.h" #include "src/core/SkGeometry.h" -#include "src/core/SkMatrixPriv.h" #include "src/core/SkPathEnums.h" #include "src/core/SkPathPriv.h" -#include "src/core/SkPathRawShapes.h" #include <algorithm> #include <cmath> #include <cstdint> #include <cstring> +#include <iterator> #include <utility> -namespace { - -void subdivide_cubic_to(SkPathBuilder* path, const SkPoint pts[4], int level = 2) { - if (--level >= 0) { - SkPoint tmp[7]; - - SkChopCubicAtHalf(pts, tmp); - subdivide_cubic_to(path, &tmp[0], level); - subdivide_cubic_to(path, &tmp[3], level); - } else { - path->cubicTo(pts[1], pts[2], pts[3]); - } -} - -} // namespace - SkPathBuilder::SkPathBuilder() { this->reset(); } @@ -67,7 +44,7 @@ SkPathBuilder& SkPathBuilder::reset() { fPts.clear(); fVerbs.clear(); fConicWeights.clear(); - fFillType = SkPathFillType::kDefault; + fFillType = SkPathFillType::kWinding; fIsVolatile = false; // these are internal state @@ -77,31 +54,22 @@ SkPathBuilder& SkPathBuilder::reset() { fLastMoveIndex = -1; // illegal fNeedsMoveVerb = true; - fType = SkPathIsAType::kGeneral; - fConvexity = SkPathConvexity::kUnknown; - return *this; } SkPathBuilder& SkPathBuilder::operator=(const SkPath& src) { this->reset().setFillType(src.getFillType()); - this->setIsVolatile(src.isVolatile()); - - const sk_sp<SkPathRef>& ref = src.fPathRef; - fVerbs = ref->fVerbs; - fPts = ref->fPoints; - fConicWeights = ref->fConicWeights; - - fSegmentMask = ref->fSegmentMask; - fLastMoveIndex = src.fLastMoveToIndex < 0 ? ~src.fLastMoveToIndex : src.fLastMoveToIndex; - fLastMovePoint = fPts.empty() ? SkPoint{0, 0} : fPts[fLastMoveIndex]; - fNeedsMoveVerb = src.fLastMoveToIndex < 0; - - fType = ref->fType; - fIsA = ref->fIsA; - - fConvexity = src.getConvexityOrUnknown(); + for (auto [verb, pts, w] : SkPathPriv::Iterate(src)) { + switch (verb) { + case SkPathVerb::kMove: this->moveTo(pts[0]); break; + case SkPathVerb::kLine: this->lineTo(pts[1]); break; + case SkPathVerb::kQuad: this->quadTo(pts[1], pts[2]); break; + case SkPathVerb::kConic: this->conicTo(pts[1], pts[2], w[0]); break; + case SkPathVerb::kCubic: this->cubicTo(pts[1], pts[2], pts[3]); break; + case SkPathVerb::kClose: this->close(); break; + } + } return *this; } @@ -110,31 +78,10 @@ void SkPathBuilder::incReserve(int extraPtCount, int extraVbCount) { fVerbs.reserve_exact(Sk32_sat_add(fVerbs.size(), extraVbCount)); } -std::tuple<SkPoint*, SkScalar*> SkPathBuilder::growForVerbsInPath(const SkPathRef& path) { - fSegmentMask |= path.fSegmentMask; - - if (int numVerbs = path.countVerbs()) { - // TODO(borenet): If the current builder is empty or JustMoves, we can use the type of the - // path. If the path is empty, we can keep the current type. - fType = SkPathIsAType::kGeneral; - memcpy(fVerbs.push_back_n(numVerbs), path.fVerbs.begin(), numVerbs * sizeof(fVerbs[0])); - } - - SkPoint* pts = nullptr; - if (int numPts = path.countPoints()) { - pts = fPts.push_back_n(numPts); - } - - SkScalar* weights = nullptr; - if (int numConics = path.countWeights()) { - weights = fConicWeights.push_back_n(numConics); - } - - return {pts, weights}; -} - SkRect SkPathBuilder::computeBounds() const { - return SkRect::BoundsOrEmpty(fPts); + SkRect bounds; + bounds.setBounds(fPts.begin(), fPts.size()); + return bounds; } /* @@ -146,27 +93,14 @@ SkRect SkPathBuilder::computeBounds() const { */ SkPathBuilder& SkPathBuilder::moveTo(SkPoint pt) { - if (!fVerbs.empty() && fVerbs.back() == SkPathVerb::kMove) { - fPts.back() = pt; + // only needed while SkPath is mutable + fLastMoveIndex = SkToInt(fPts.size()); - SkASSERT(fType != SkPathIsAType::kOval && fType != SkPathIsAType::kRRect); - SkASSERT(fNeedsMoveVerb == false); - SkASSERT(fConvexity == SkPathConvexity::kUnknown); - SkASSERT(fLastMoveIndex == SkToInt(fPts.size()) - 1); - } else { - fLastMoveIndex = SkToInt(fPts.size()); - - fPts.push_back(pt); - fVerbs.push_back(SkPathVerb::kMove); + fPts.push_back(pt); + fVerbs.push_back((uint8_t)SkPathVerb::kMove); - fNeedsMoveVerb = false; - if (fType == SkPathIsAType::kOval || fType == SkPathIsAType::kRRect) { - fType = SkPathIsAType::kGeneral; - } - fConvexity = SkPathConvexity::kUnknown; - } fLastMovePoint = pt; - + fNeedsMoveVerb = false; return *this; } @@ -174,7 +108,7 @@ SkPathBuilder& SkPathBuilder::lineTo(SkPoint pt) { this->ensureMove(); fPts.push_back(pt); - fVerbs.push_back(SkPathVerb::kLine); + fVerbs.push_back((uint8_t)SkPathVerb::kLine); fSegmentMask |= kLine_SkPathSegmentMask; return *this; @@ -186,7 +120,7 @@ SkPathBuilder& SkPathBuilder::quadTo(SkPoint pt1, SkPoint pt2) { SkPoint* p = fPts.push_back_n(2); p[0] = pt1; p[1] = pt2; - fVerbs.push_back(SkPathVerb::kQuad); + fVerbs.push_back((uint8_t)SkPathVerb::kQuad); fSegmentMask |= kQuad_SkPathSegmentMask; return *this; @@ -198,15 +132,10 @@ SkPathBuilder& SkPathBuilder::conicTo(SkPoint pt1, SkPoint pt2, SkScalar w) { SkPoint* p = fPts.push_back_n(2); p[0] = pt1; p[1] = pt2; - if (w == 1) { - fVerbs.push_back(SkPathVerb::kQuad); - fSegmentMask |= kQuad_SkPathSegmentMask; - } else { - fVerbs.push_back(SkPathVerb::kConic); - fConicWeights.push_back(w); - fSegmentMask |= kConic_SkPathSegmentMask; - } + fVerbs.push_back((uint8_t)SkPathVerb::kConic); + fConicWeights.push_back(w); + fSegmentMask |= kConic_SkPathSegmentMask; return *this; } @@ -217,18 +146,17 @@ SkPathBuilder& SkPathBuilder::cubicTo(SkPoint pt1, SkPoint pt2, SkPoint pt3) { p[0] = pt1; p[1] = pt2; p[2] = pt3; - fVerbs.push_back(SkPathVerb::kCubic); + fVerbs.push_back((uint8_t)SkPathVerb::kCubic); fSegmentMask |= kCubic_SkPathSegmentMask; return *this; } SkPathBuilder& SkPathBuilder::close() { - // If this is a 2nd 'close', we just ignore it - if (!fVerbs.empty() && fVerbs.back() != SkPathVerb::kClose) { + if (!fVerbs.empty()) { this->ensureMove(); - fVerbs.push_back(SkPathVerb::kClose); + fVerbs.push_back((uint8_t)SkPathVerb::kClose); // fLastMovePoint stays where it is -- the previous moveTo fNeedsMoveVerb = true; @@ -238,19 +166,6 @@ SkPathBuilder& SkPathBuilder::close() { /////////////////////////////////////////////////////////////////////////////////////////// -SkPathBuilder& SkPathBuilder::rMoveTo(SkPoint pt) { - SkPoint lastPt = {0,0}; - int count = fPts.size(); - if (count > 0) { - if (!fNeedsMoveVerb) { - lastPt = fPts[count - 1]; - } else { - lastPt = fPts[fLastMoveIndex]; - } - } - return this->moveTo(lastPt.fX + pt.fX, lastPt.fY + pt.fY); -} - SkPathBuilder& SkPathBuilder::rLineTo(SkPoint p1) { this->ensureMove(); return this->lineTo(fPts.back() + p1); @@ -277,52 +192,55 @@ SkPathBuilder& SkPathBuilder::rCubicTo(SkPoint p1, SkPoint p2, SkPoint p3) { /////////////////////////////////////////////////////////////////////////////////////////// SkPath SkPathBuilder::make(sk_sp<SkPathRef> pr) const { - switch (fType) { - case SkPathIsAType::kGeneral: + auto convexity = SkPathConvexity::kUnknown; + SkPathFirstDirection dir = SkPathFirstDirection::kUnknown; + + switch (fIsA) { + case kIsA_Oval: + pr->setIsOval(fIsACCW, fIsAStart); + convexity = SkPathConvexity::kConvex; + dir = fIsACCW ? SkPathFirstDirection::kCCW : SkPathFirstDirection::kCW; break; - case SkPathIsAType::kOval: - pr->setIsOval(fIsA.fDirection, fIsA.fStartIndex); - SkASSERT(SkPathConvexity_IsConvex(fConvexity)); - break; - case SkPathIsAType::kRRect: - pr->setIsRRect(fIsA.fDirection, fIsA.fStartIndex); - SkASSERT(SkPathConvexity_IsConvex(fConvexity)); + case kIsA_RRect: + pr->setIsRRect(fIsACCW, fIsAStart); + convexity = SkPathConvexity::kConvex; + dir = fIsACCW ? SkPathFirstDirection::kCCW : SkPathFirstDirection::kCW; break; + default: break; } // Wonder if we can combine convexity and dir internally... // unknown, convex_cw, convex_ccw, concave // Do we ever have direction w/o convexity, or viceversa (inside path)? // - auto path = SkPath(std::move(pr), fFillType, fIsVolatile, fConvexity); + auto path = SkPath(std::move(pr), fFillType, fIsVolatile, convexity, dir); // This hopefully can go away in the future when Paths are immutable, // but if while they are still editable, we need to correctly set this. - SkSpan<const SkPathVerb> verbs = path.fPathRef->verbs(); - if (!verbs.empty()) { + const uint8_t* start = path.fPathRef->verbsBegin(); + const uint8_t* stop = path.fPathRef->verbsEnd(); + if (start < stop) { SkASSERT(fLastMoveIndex >= 0); // peek at the last verb, to know if our last contour is closed - const bool isClosed = (verbs.back() == SkPathVerb::kClose); + const bool isClosed = (stop[-1] == (uint8_t)SkPathVerb::kClose); path.fLastMoveToIndex = isClosed ? ~fLastMoveIndex : fLastMoveIndex; } return path; } -SkPath SkPathBuilder::snapshot(const SkMatrix* mx) const { +SkPath SkPathBuilder::snapshot() const { return this->make(sk_sp<SkPathRef>(new SkPathRef(fPts, fVerbs, fConicWeights, - fSegmentMask, - mx))); + fSegmentMask))); } -SkPath SkPathBuilder::detach(const SkMatrix* mx) { +SkPath SkPathBuilder::detach() { auto path = this->make(sk_sp<SkPathRef>(new SkPathRef(std::move(fPts), std::move(fVerbs), std::move(fConicWeights), - fSegmentMask, - mx))); + fSegmentMask))); this->reset(); return path; } @@ -353,7 +271,7 @@ static bool arc_is_lone_point(const SkRect& oval, SkScalar startAngle, SkScalar // Return the unit vectors pointing at the start/stop points for the given start/sweep angles // static void angles_to_unit_vectors(SkScalar startAngle, SkScalar sweepAngle, - SkVector* startV, SkVector* stopV, SkPathDirection* dir) { + SkVector* startV, SkVector* stopV, SkRotationDirection* dir) { SkScalar startRad = SkDegreesToRadians(startAngle), stopRad = SkDegreesToRadians(startAngle + sweepAngle); @@ -383,7 +301,7 @@ static void angles_to_unit_vectors(SkScalar startAngle, SkScalar sweepAngle, } while (*startV == *stopV); } } - *dir = sweepAngle > 0 ? SkPathDirection::kCW : SkPathDirection::kCCW; + *dir = sweepAngle > 0 ? kCW_SkRotationDirection : kCCW_SkRotationDirection; } /** @@ -391,7 +309,7 @@ static void angles_to_unit_vectors(SkScalar startAngle, SkScalar sweepAngle, * ignore singlePt and append the specified number of conics. */ static int build_arc_conics(const SkRect& oval, const SkVector& start, const SkVector& stop, - SkPathDirection dir, SkConic conics[SkConic::kMaxConicsForArc], + SkRotationDirection dir, SkConic conics[SkConic::kMaxConicsForArc], SkPoint* singlePt) { SkMatrix matrix; @@ -400,7 +318,7 @@ static int build_arc_conics(const SkRect& oval, const SkVector& start, const SkV int count = SkConic::BuildUnitArc(start, stop, dir, &matrix, conics); if (0 == count) { - *singlePt = matrix.mapPoint(stop); + matrix.mapXY(stop.x(), stop.y(), singlePt); } return count; } @@ -416,8 +334,6 @@ SkPathBuilder& SkPathBuilder::arcTo(const SkRect& oval, SkScalar startAngle, SkS return *this; } - startAngle = SkScalarMod(startAngle, 360.0f); - if (fVerbs.empty()) { forceMoveTo = true; } @@ -428,7 +344,7 @@ SkPathBuilder& SkPathBuilder::arcTo(const SkRect& oval, SkScalar startAngle, SkS } SkVector startV, stopV; - SkPathDirection dir; + SkRotationDirection dir; angles_to_unit_vectors(startAngle, sweepAngle, &startV, &stopV, &dir); SkPoint singlePt; @@ -475,14 +391,6 @@ SkPathBuilder& SkPathBuilder::arcTo(const SkRect& oval, SkScalar startAngle, SkS return *this; } -SkPathBuilder& SkPathBuilder::rArcTo(SkScalar rx, SkScalar ry, SkScalar xAxisRotate, - SkPathBuilder::ArcSize largeArc, - SkPathDirection sweep, SkScalar dx, SkScalar dy) { - const SkPoint currentPoint = this->getLastPt().value_or(SkPoint{0, 0}); - return this->arcTo({rx, ry}, xAxisRotate, largeArc, sweep, - {currentPoint.fX + dx, currentPoint.fY + dy}); -} - SkPathBuilder& SkPathBuilder::addArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle) { if (oval.isEmpty() || 0 == sweepAngle) { return *this; @@ -553,7 +461,7 @@ SkPathBuilder& SkPathBuilder::arcTo(SkPoint rad, SkScalar angle, SkPathBuilder:: SkPathDirection arcSweep, SkPoint endPt) { this->ensureMove(); - const SkPoint srcPts[2] = { fPts.back(), endPt }; + SkPoint srcPts[2] = { fPts.back(), endPt }; // If rx = 0 or ry = 0 then this arc is treated as a straight line segment (a "lineto") // joining the endpoints. @@ -574,7 +482,8 @@ SkPathBuilder& SkPathBuilder::arcTo(SkPoint rad, SkScalar angle, SkPathBuilder:: SkMatrix pointTransform; pointTransform.setRotate(-angle); - SkPoint transformedMidPoint = pointTransform.mapPoint(midPointDistance); + SkPoint transformedMidPoint; + pointTransform.mapPoints(&transformedMidPoint, &midPointDistance, 1); SkScalar squareRx = rx * rx; SkScalar squareRy = ry * ry; SkScalar squareX = transformedMidPoint.fX * transformedMidPoint.fX; @@ -593,7 +502,7 @@ SkPathBuilder& SkPathBuilder::arcTo(SkPoint rad, SkScalar angle, SkPathBuilder:: pointTransform.preRotate(-angle); SkPoint unitPts[2]; - pointTransform.mapPoints(unitPts, srcPts); + pointTransform.mapPoints(unitPts, srcPts, (int) std::size(unitPts)); SkVector delta = unitPts[1] - unitPts[0]; SkScalar d = delta.fX * delta.fX + delta.fY * delta.fY; @@ -618,7 +527,7 @@ SkPathBuilder& SkPathBuilder::arcTo(SkPoint rad, SkScalar angle, SkPathBuilder:: thetaArc -= SK_ScalarPI * 2; } - // Very tiny angles cause our subsequent math to go wonky (skbug.com/40040578) + // Very tiny angles cause our subsequent math to go wonky (skbug.com/9272) // so we do a quick check here. The precise tolerance amount is just made up. // PI/million happens to fix the bug in 9272, but a larger value is probably // ok too. @@ -655,7 +564,7 @@ SkPathBuilder& SkPathBuilder::arcTo(SkPoint rad, SkScalar angle, SkPathBuilder:: unitPts[0] = unitPts[1]; unitPts[0].offset(t * sinEndTheta, -t * cosEndTheta); SkPoint mapped[2]; - pointTransform.mapPoints(mapped, unitPts); + pointTransform.mapPoints(mapped, unitPts, (int) std::size(unitPts)); /* Computing the arc width introduces rounding errors that cause arcs to start outside their marks. A round rect may lose convexity as a result. If the input @@ -679,75 +588,170 @@ SkPathBuilder& SkPathBuilder::arcTo(SkPoint rad, SkScalar angle, SkPathBuilder:: /////////////////////////////////////////////////////////////////////////////////////////// -SkPathIter SkPathBuilder::iter() const { - return SkPathIter(fPts, fVerbs, fConicWeights); -} +namespace { + template <unsigned N> class PointIterator { + public: + PointIterator(SkPathDirection dir, unsigned startIndex) + : fCurrent(startIndex % N) + , fAdvance(dir == SkPathDirection::kCW ? 1 : N - 1) + {} + + const SkPoint& current() const { + SkASSERT(fCurrent < N); + return fPts[fCurrent]; + } -SkPathBuilder& SkPathBuilder::addRaw(const SkPathRaw& raw) { - this->incReserve(raw.points().size(), raw.verbs().size()); + const SkPoint& next() { + fCurrent = (fCurrent + fAdvance) % N; + return this->current(); + } - for (auto iter = raw.iter(); auto rec = iter.next();) { - const auto pts = rec->fPoints; - switch (rec->fVerb) { - case SkPathVerb::kMove: this->moveTo( pts[0]); break; - case SkPathVerb::kLine: this->lineTo( pts[1]); break; - case SkPathVerb::kQuad: this->quadTo( pts[1], pts[2]); break; - case SkPathVerb::kConic: this->conicTo(pts[1], pts[2], rec->fConicWeight); break; - case SkPathVerb::kCubic: this->cubicTo(pts[1], pts[2], pts[3]); break; - case SkPathVerb::kClose: this->close(); break; + protected: + SkPoint fPts[N]; + + private: + unsigned fCurrent; + unsigned fAdvance; + }; + + class RectPointIterator : public PointIterator<4> { + public: + RectPointIterator(const SkRect& rect, SkPathDirection dir, unsigned startIndex) + : PointIterator(dir, startIndex) { + + fPts[0] = SkPoint::Make(rect.fLeft, rect.fTop); + fPts[1] = SkPoint::Make(rect.fRight, rect.fTop); + fPts[2] = SkPoint::Make(rect.fRight, rect.fBottom); + fPts[3] = SkPoint::Make(rect.fLeft, rect.fBottom); } - } - return *this; -} + }; + + class OvalPointIterator : public PointIterator<4> { + public: + OvalPointIterator(const SkRect& oval, SkPathDirection dir, unsigned startIndex) + : PointIterator(dir, startIndex) { + + const SkScalar cx = oval.centerX(); + const SkScalar cy = oval.centerY(); + + fPts[0] = SkPoint::Make(cx, oval.fTop); + fPts[1] = SkPoint::Make(oval.fRight, cy); + fPts[2] = SkPoint::Make(cx, oval.fBottom); + fPts[3] = SkPoint::Make(oval.fLeft, cy); + } + }; + + class RRectPointIterator : public PointIterator<8> { + public: + RRectPointIterator(const SkRRect& rrect, SkPathDirection dir, unsigned startIndex) + : PointIterator(dir, startIndex) + { + const SkRect& bounds = rrect.getBounds(); + const SkScalar L = bounds.fLeft; + const SkScalar T = bounds.fTop; + const SkScalar R = bounds.fRight; + const SkScalar B = bounds.fBottom; + + fPts[0] = SkPoint::Make(L + rrect.radii(SkRRect::kUpperLeft_Corner).fX, T); + fPts[1] = SkPoint::Make(R - rrect.radii(SkRRect::kUpperRight_Corner).fX, T); + fPts[2] = SkPoint::Make(R, T + rrect.radii(SkRRect::kUpperRight_Corner).fY); + fPts[3] = SkPoint::Make(R, B - rrect.radii(SkRRect::kLowerRight_Corner).fY); + fPts[4] = SkPoint::Make(R - rrect.radii(SkRRect::kLowerRight_Corner).fX, B); + fPts[5] = SkPoint::Make(L + rrect.radii(SkRRect::kLowerLeft_Corner).fX, B); + fPts[6] = SkPoint::Make(L, B - rrect.radii(SkRRect::kLowerLeft_Corner).fY); + fPts[7] = SkPoint::Make(L, T + rrect.radii(SkRRect::kUpperLeft_Corner).fY); + } + }; +} // anonymous namespace + SkPathBuilder& SkPathBuilder::addRect(const SkRect& rect, SkPathDirection dir, unsigned index) { - const bool wasEmpty = this->isEmpty(); + const int kPts = 4; // moveTo + 3 lines + const int kVerbs = 5; // moveTo + 3 lines + close + this->incReserve(kPts, kVerbs); - this->addRaw(SkPathRawShapes::Rect(rect, dir, index)); + RectPointIterator iter(rect, dir, index); - if (wasEmpty) { - // now we're a rect - fConvexity = SkPathDirection_ToConvexity(dir); - } - return *this; + this->moveTo(iter.current()); + this->lineTo(iter.next()); + this->lineTo(iter.next()); + this->lineTo(iter.next()); + return this->close(); } SkPathBuilder& SkPathBuilder::addOval(const SkRect& oval, SkPathDirection dir, unsigned index) { - const bool wasEmpty = this->isEmpty(); + const IsA prevIsA = fIsA; + + const int kPts = 9; // moveTo + 4 conics(2 pts each) + const int kVerbs = 6; // moveTo + 4 conics + close + this->incReserve(kPts, kVerbs); - this->addRaw(SkPathRawShapes::Oval(oval, dir, index)); + OvalPointIterator ovalIter(oval, dir, index); + RectPointIterator rectIter(oval, dir, index + (dir == SkPathDirection::kCW ? 0 : 1)); - if (wasEmpty) { - fType = SkPathIsAType::kOval; - fIsA.fDirection = dir; - fIsA.fStartIndex = index % 4; - fConvexity = SkPathDirection_ToConvexity(dir); + // The corner iterator pts are tracking "behind" the oval/radii pts. + + this->moveTo(ovalIter.current()); + for (unsigned i = 0; i < 4; ++i) { + this->conicTo(rectIter.next(), ovalIter.next(), SK_ScalarRoot2Over2); } + this->close(); + if (prevIsA == kIsA_JustMoves) { + fIsA = kIsA_Oval; + fIsACCW = (dir == SkPathDirection::kCCW); + fIsAStart = index % 4; + } return *this; } SkPathBuilder& SkPathBuilder::addRRect(const SkRRect& rrect, SkPathDirection dir, unsigned index) { + const IsA prevIsA = fIsA; const SkRect& bounds = rrect.getBounds(); if (rrect.isRect() || rrect.isEmpty()) { // degenerate(rect) => radii points are collapsing - return this->addRect(bounds, dir, (index + 1) / 2); - } - if (rrect.isOval()) { + this->addRect(bounds, dir, (index + 1) / 2); + } else if (rrect.isOval()) { // degenerate(oval) => line points are collapsing - return this->addOval(bounds, dir, index / 2); + this->addOval(bounds, dir, index / 2); + } else { + // we start with a conic on odd indices when moving CW vs. even indices when moving CCW + const bool startsWithConic = ((index & 1) == (dir == SkPathDirection::kCW)); + const SkScalar weight = SK_ScalarRoot2Over2; + + const int kVerbs = startsWithConic + ? 9 // moveTo + 4x conicTo + 3x lineTo + close + : 10; // moveTo + 4x lineTo + 4x conicTo + close + this->incReserve(kVerbs); + + RRectPointIterator rrectIter(rrect, dir, index); + // Corner iterator indices follow the collapsed radii model, + // adjusted such that the start pt is "behind" the radii start pt. + const unsigned rectStartIndex = index / 2 + (dir == SkPathDirection::kCW ? 0 : 1); + RectPointIterator rectIter(bounds, dir, rectStartIndex); + + this->moveTo(rrectIter.current()); + if (startsWithConic) { + for (unsigned i = 0; i < 3; ++i) { + this->conicTo(rectIter.next(), rrectIter.next(), weight); + this->lineTo(rrectIter.next()); + } + this->conicTo(rectIter.next(), rrectIter.next(), weight); + // final lineTo handled by close(). + } else { + for (unsigned i = 0; i < 4; ++i) { + this->lineTo(rrectIter.next()); + this->conicTo(rectIter.next(), rrectIter.next(), weight); + } + } + this->close(); } - const bool wasEmpty = this->isEmpty(); - - this->addRaw(SkPathRawShapes::RRect(rrect, dir, index)); - - if (wasEmpty) { - fType = SkPathIsAType::kRRect; - fIsA.fDirection = dir; - fIsA.fStartIndex = index % 8; - fConvexity = SkPathDirection_ToConvexity(dir); + if (prevIsA == kIsA_JustMoves) { + fIsA = kIsA_RRect; + fIsACCW = (dir == SkPathDirection::kCCW); + fIsAStart = index % 8; } return *this; } @@ -759,26 +763,25 @@ SkPathBuilder& SkPathBuilder::addCircle(SkScalar x, SkScalar y, SkScalar r, SkPa return *this; } -SkPathBuilder& SkPathBuilder::addPolygon(SkSpan<const SkPoint> pts, bool isClosed) { - if (pts.empty()) { +SkPathBuilder& SkPathBuilder::addPolygon(const SkPoint pts[], int count, bool isClosed) { + if (count <= 0) { return *this; } this->moveTo(pts[0]); - this->polylineTo(pts.last(pts.size() - 1)); + this->polylineTo(&pts[1], count - 1); if (isClosed) { this->close(); } return *this; } -SkPathBuilder& SkPathBuilder::polylineTo(SkSpan<const SkPoint> pts) { - if (!pts.empty()) { +SkPathBuilder& SkPathBuilder::polylineTo(const SkPoint pts[], int count) { + if (count > 0) { this->ensureMove(); - const auto count = pts.size(); this->incReserve(count, count); - memcpy(fPts.push_back_n(count), pts.data(), count * sizeof(SkPoint)); + memcpy(fPts.push_back_n(count), pts, count * sizeof(SkPoint)); memset(fVerbs.push_back_n(count), (uint8_t)SkPathVerb::kLine, count); fSegmentMask |= kLine_SkPathSegmentMask; } @@ -794,137 +797,37 @@ SkPathBuilder& SkPathBuilder::offset(SkScalar dx, SkScalar dy) { return *this; } -SkPathBuilder& SkPathBuilder::addPath(const SkPath& path, SkScalar dx, SkScalar dy, - SkPath::AddPathMode mode) { - SkMatrix matrix = SkMatrix::Translate(dx, dy); - return this->addPath(path, matrix, mode); -} - -SkPathBuilder& SkPathBuilder::addPath(const SkPath& src, const SkMatrix& matrix, - SkPath::AddPathMode mode) { - if (src.isEmpty()) { - return *this; - } +SkPathBuilder& SkPathBuilder::addPath(const SkPath& src) { + SkPath::RawIter iter(src); + SkPoint pts[4]; + SkPath::Verb verb; - const bool canReplaceThis = (mode == SkPath::AddPathMode::kAppend_AddPathMode && - SkPathPriv::IsEffectivelyEmpty(*this)) - || this->verbs().empty(); - if (canReplaceThis && matrix.isIdentity()) { - const SkPathFillType fillType = fFillType; - *this = src; - fFillType = fillType; - return *this; - } - - if (SkPath::AddPathMode::kAppend_AddPathMode == mode && !matrix.hasPerspective()) { - if (src.fLastMoveToIndex >= 0) { - fLastMoveIndex = src.fLastMoveToIndex + this->countPoints(); - fNeedsMoveVerb = false; - } else { - fLastMoveIndex = ~src.fLastMoveToIndex + this->countPoints(); - fNeedsMoveVerb = true; - } - - auto [newPts, newWeights] = this->growForVerbsInPath(*src.fPathRef); - const auto count = src.countPoints(); - matrix.mapPoints({newPts, count}, {src.fPathRef->points(), count}); - if (int numWeights = src.fPathRef->countWeights()) { - memcpy(newWeights, src.fPathRef->conicWeights(), numWeights * sizeof(newWeights[0])); - } - fLastMovePoint = fPts.at(fLastMoveIndex); - return *this; // TODO(borenet): dirtyAfterEdit sets convexity and firstDirection. - } - - SkMatrixPriv::MapPtsProc mapPtsProc = SkMatrixPriv::GetMapPtsProc(matrix); - bool firstVerb = true; - for (auto [verb, pts, w] : SkPathPriv::Iterate(src)) { - SkPoint mappedPts[3]; + while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { switch (verb) { - case SkPathVerb::kMove: - mapPtsProc(matrix, mappedPts, &pts[0], 1); - if (firstVerb && mode == SkPath::kExtend_AddPathMode && !isEmpty()) { - this->ensureMove(); // In case last contour is closed - std::optional<SkPoint> lastPt = this->getLastPt(); - // don't add lineTo if it is degenerate - if (!lastPt.has_value() || lastPt.value() != mappedPts[0]) { - this->lineTo(mappedPts[0]); - } - } else { - this->moveTo(mappedPts[0]); - } - break; - case SkPathVerb::kLine: - mapPtsProc(matrix, mappedPts, &pts[1], 1); - this->lineTo(mappedPts[0]); - break; - case SkPathVerb::kQuad: - mapPtsProc(matrix, mappedPts, &pts[1], 2); - this->quadTo(mappedPts[0], mappedPts[1]); - break; - case SkPathVerb::kConic: - mapPtsProc(matrix, mappedPts, &pts[1], 2); - this->conicTo(mappedPts[0], mappedPts[1], *w); - break; - case SkPathVerb::kCubic: - mapPtsProc(matrix, mappedPts, &pts[1], 3); - this->cubicTo(mappedPts[0], mappedPts[1], mappedPts[2]); - break; - case SkPathVerb::kClose: - this->close(); - break; + case SkPath::kMove_Verb: this->moveTo (pts[0]); break; + case SkPath::kLine_Verb: this->lineTo (pts[1]); break; + case SkPath::kQuad_Verb: this->quadTo (pts[1], pts[2]); break; + case SkPath::kCubic_Verb: this->cubicTo(pts[1], pts[2], pts[3]); break; + case SkPath::kConic_Verb: this->conicTo(pts[1], pts[2], iter.conicWeight()); break; + case SkPath::kClose_Verb: this->close(); break; + case SkPath::kDone_Verb: SkUNREACHABLE; } - firstVerb = false; - } - return *this; -} - -// ignore the last point of the 1st contour -SkPathBuilder& SkPathBuilder::privateReversePathTo(const SkPath& path) { - if (path.fPathRef->fVerbs.empty()) { - return *this; } - const SkPathVerb* verbs = path.fPathRef->verbsEnd(); - const SkPathVerb* verbsBegin = path.fPathRef->verbsBegin(); - const SkPoint* pts = path.fPathRef->pointsEnd() - 1; - const SkScalar* conicWeights = path.fPathRef->conicWeightsEnd(); - - while (verbs > verbsBegin) { - SkPathVerb v = *--verbs; - pts -= SkPathPriv::PtsInVerb(v); - switch (v) { - case SkPathVerb::kMove: - // if the path has multiple contours, stop after reversing the last - return *this; - case SkPathVerb::kLine: - this->lineTo(pts[0]); - break; - case SkPathVerb::kQuad: - this->quadTo(pts[1], pts[0]); - break; - case SkPathVerb::kConic: - this->conicTo(pts[1], pts[0], *--conicWeights); - break; - case SkPathVerb::kCubic: - this->cubicTo(pts[2], pts[1], pts[0]); - break; - case SkPathVerb::kClose: - break; - } - } return *this; } SkPathBuilder& SkPathBuilder::privateReverseAddPath(const SkPath& src) { - const SkPathVerb* verbsBegin = src.fPathRef->verbsBegin(); - const SkPathVerb* verbs = src.fPathRef->verbsEnd(); + + const uint8_t* verbsBegin = src.fPathRef->verbsBegin(); + const uint8_t* verbs = src.fPathRef->verbsEnd(); const SkPoint* pts = src.fPathRef->pointsEnd(); const SkScalar* conicWeights = src.fPathRef->conicWeightsEnd(); bool needMove = true; bool needClose = false; while (verbs > verbsBegin) { - SkPathVerb v = *--verbs; + uint8_t v = *--verbs; int n = SkPathPriv::PtsInVerb(v); if (needMove) { @@ -957,113 +860,9 @@ SkPathBuilder& SkPathBuilder::privateReverseAddPath(const SkPath& src) { case SkPathVerb::kClose: needClose = true; break; + default: + SkDEBUGFAIL("unexpected verb"); } } return *this; } - -std::optional<SkPoint> SkPathBuilder::getLastPt() const { - int count = this->fPts.size(); - if (count > 0) { - return this->fPts.at(count - 1); - } - return std::nullopt; -}; - -void SkPathBuilder::setLastPt(SkScalar x, SkScalar y) { - int count = fPts.size(); - if (count == 0) { - this->moveTo(x, y); - } else { - fPts.at(count-1).set(x, y); - } -} - -SkPathBuilder& SkPathBuilder::transform(const SkMatrix& matrix) { - if (matrix.isIdentity() || this->isEmpty()) { - return *this; - } - - if (matrix.hasPerspective()) { - SkPath src = this->detach(); - - // remember this from before the detach() - this->setFillType(src.getFillType()); - - SkPath clipped; - if (SkPathPriv::PerspectiveClip(src, matrix, &clipped)) { - src = std::move(clipped); - } - - for (auto [verb, pts, wt] : SkPathPriv::Iterate(src)) { - switch (verb) { - case SkPathVerb::kMove: - this->moveTo(pts[0]); - break; - case SkPathVerb::kLine: - this->lineTo(pts[1]); - break; - case SkPathVerb::kQuad: - // promote the quad to a conic - this->conicTo(pts[1], pts[2], SkConic::TransformW(pts, SK_Scalar1, matrix)); - break; - case SkPathVerb::kConic: - this->conicTo(pts[1], pts[2], SkConic::TransformW(pts, wt[0], matrix)); - break; - case SkPathVerb::kCubic: - subdivide_cubic_to(this, pts); - break; - case SkPathVerb::kClose: - this->close(); - break; - } - } - } else { - - // Can we maintain our special case shape? - if (!matrix.rectStaysRect() || !SkPathPriv::IsAxisAligned(fPts)) { - fType = SkPathIsAType::kGeneral; - // lose convexity (just to be numerically safe) - if (SkPathConvexity_IsConvex(fConvexity)) { - fConvexity = SkPathConvexity::kUnknown; - } - } - - // If we're still a special case, check if we need to reverse our winding - if (fType == SkPathIsAType::kOval || fType == SkPathIsAType::kRRect) { - auto [dir, start] = - SkPathPriv::TransformDirAndStart(matrix, fType == SkPathIsAType::kRRect, - fIsA.fDirection, fIsA.fStartIndex); - fIsA.fDirection = dir; - fIsA.fStartIndex = start; - } - - } - matrix.mapPoints(fPts); - - return *this; -} - -bool SkPathBuilder::isFinite() const { - for (auto p : fPts) { - if (!p.isFinite()) { - return false; - } - } - return true; -} - -bool SkPathBuilder::isZeroLengthSincePoint(int startPtIndex) const { - int count = fPts.size() - startPtIndex; - if (count < 2) { - return true; - } - const SkPoint* pts = fPts.begin() + startPtIndex; - const SkPoint& first = *pts; - for (int index = 1; index < count; ++index) { - if (first != pts[index]) { - return false; - } - } - return true; -} diff --git a/gfx/skia/skia/src/core/SkPathEffect.cpp b/gfx/skia/skia/src/core/SkPathEffect.cpp @@ -9,8 +9,8 @@ #include "include/core/SkFlattenable.h" #include "include/core/SkMatrix.h" #include "include/core/SkPath.h" -#include "include/core/SkPathBuilder.h" #include "include/core/SkRefCnt.h" +#include "include/private/base/SkAssert.h" #include "src/core/SkPathEffectBase.h" #include "src/core/SkReadBuffer.h" #include "src/core/SkWriteBuffer.h" @@ -24,31 +24,25 @@ struct SkRect; /////////////////////////////////////////////////////////////////////////////// -#ifdef SK_SUPPORT_MUTABLE_PATHEFFECT bool SkPathEffect::filterPath(SkPath* dst, const SkPath& src, SkStrokeRec* rec, const SkRect* bounds) const { return this->filterPath(dst, src, rec, bounds, SkMatrix::I()); } -bool SkPathEffect::filterPath(SkPath* dst, const SkPath& src, SkStrokeRec* rec, const SkRect* cullR, - const SkMatrix& ctm) const { - SkPathBuilder builder; - if (this->filterPath(&builder, src, rec, cullR, ctm)) { - *dst = builder.detach(); +bool SkPathEffect::filterPath(SkPath* dst, const SkPath& src, SkStrokeRec* rec, + const SkRect* bounds, const SkMatrix& ctm) const { + SkPath tmp, *tmpDst = dst; + if (dst == &src) { + tmpDst = &tmp; + } + if (as_PEB(this)->onFilterPath(tmpDst, src, rec, bounds, ctm)) { + if (dst == &src) { + *dst = tmp; + } return true; } return false; } -#endif - -bool SkPathEffect::filterPath(SkPathBuilder* dst, const SkPath& src, SkStrokeRec* rec) const { - return this->filterPath(dst, src, rec, nullptr, SkMatrix::I()); -} - -bool SkPathEffect::filterPath(SkPathBuilder* dst, const SkPath& src, SkStrokeRec* rec, - const SkRect* bounds, const SkMatrix& ctm) const { - return as_PEB(this)->onFilterPath(dst, src, rec, bounds, ctm); -} bool SkPathEffectBase::asPoints(PointData* results, const SkPath& src, const SkStrokeRec& rec, const SkMatrix& mx, const SkRect* rect) const { @@ -111,16 +105,15 @@ public: SkComposePathEffect(sk_sp<SkPathEffect> outer, sk_sp<SkPathEffect> inner) : INHERITED(std::move(outer), std::move(inner)) {} - bool onFilterPath(SkPathBuilder* builder, const SkPath& src, SkStrokeRec* rec, + bool onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec* rec, const SkRect* cullRect, const SkMatrix& ctm) const override { SkPath tmp; const SkPath* ptr = &src; - if (fPE1->filterPath(builder, src, rec, cullRect, ctm)) { - tmp = builder->detach(); + if (fPE1->filterPath(&tmp, src, rec, cullRect, ctm)) { ptr = &tmp; } - return fPE0->filterPath(builder, *ptr, rec, cullRect, ctm); + return fPE0->filterPath(dst, *ptr, rec, cullRect, ctm); } SK_FLATTENABLE_HOOKS(SkComposePathEffect) @@ -173,11 +166,11 @@ public: SkSumPathEffect(sk_sp<SkPathEffect> first, sk_sp<SkPathEffect> second) : INHERITED(std::move(first), std::move(second)) {} - bool onFilterPath(SkPathBuilder* builder, const SkPath& src, SkStrokeRec* rec, + bool onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec* rec, const SkRect* cullRect, const SkMatrix& ctm) const override { // always call both, even if the first one succeeds - bool filteredFirst = fPE0->filterPath(builder, src, rec, cullRect, ctm); - bool filteredSecond = fPE1->filterPath(builder, src, rec, cullRect, ctm); + bool filteredFirst = fPE0->filterPath(dst, src, rec, cullRect, ctm); + bool filteredSecond = fPE1->filterPath(dst, src, rec, cullRect, ctm); return filteredFirst || filteredSecond; } diff --git a/gfx/skia/skia/src/core/SkPathEffectBase.h b/gfx/skia/skia/src/core/SkPathEffectBase.h @@ -12,11 +12,8 @@ #include "include/core/SkPathEffect.h" #include "include/core/SkPoint.h" #include "include/core/SkRect.h" -#include "include/core/SkSpan.h" -#include <optional> - -class SkPathBuilder; +class SkPath; class SkStrokeRec; class SkPathEffectBase : public SkPathEffect { @@ -62,8 +59,6 @@ public: SkPath fFirst; // If not empty, contains geometry for first point SkPath fLast; // If not empty, contains geometry for last point - - SkSpan<SkPoint> points() { return {fPoints, fNumPoints}; } }; /** @@ -102,7 +97,7 @@ public: * The output of path effects must always be in the original (input) coordinate system, * regardless of whether the path effect uses the CTM or not. */ - virtual bool onFilterPath(SkPathBuilder*, const SkPath&, SkStrokeRec*, const SkRect*, + virtual bool onFilterPath(SkPath*, const SkPath&, SkStrokeRec*, const SkRect*, const SkMatrix& /* ctm */) const = 0; /** Path effects *requiring* a valid CTM should override to return true. */ @@ -113,13 +108,25 @@ public: return false; } + enum class DashType { + kNone, //!< ignores the info parameter + kDash, //!< fills in all of the info parameter + }; + struct DashInfo { - SkSpan<const SkScalar> fIntervals; - SkScalar fPhase; + DashInfo() : fIntervals(nullptr), fCount(0), fPhase(0) {} + DashInfo(SkScalar* intervals, int32_t count, SkScalar phase) + : fIntervals(intervals), fCount(count), fPhase(phase) {} + + SkScalar* fIntervals; //!< Length of on/off intervals for dashed lines + // Even values represent ons, and odds offs + int32_t fCount; //!< Number of intervals in the dash. Should be even number + SkScalar fPhase; //!< Offset into the dashed interval pattern + // mod the sum of all intervals }; - virtual std::optional<DashInfo> asADash() const { - return {}; + virtual DashType asADash(DashInfo*) const { + return DashType::kNone; } diff --git a/gfx/skia/skia/src/core/SkPathEnums.h b/gfx/skia/skia/src/core/SkPathEnums.h @@ -4,88 +4,22 @@ * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. * - * This file contains private enums related to paths. See also skbug.com/40042016 + * This file contains private enums related to paths. See also skbug.com/10670 */ #ifndef SkPathEnums_DEFINED #define SkPathEnums_DEFINED -#include "include/core/SkPathTypes.h" - -#include <optional> - enum class SkPathConvexity { - kConvex_CW, - kConvex_CCW, - kConvex_Degenerate, // known to not have a determinable direction, but convex - + kConvex, kConcave, - - kUnknown, // todo: can we eliminate this, and use optional or other patterns? + kUnknown, }; -static inline bool SkPathConvexity_IsConvex(SkPathConvexity cv) { - return cv == SkPathConvexity::kConvex_CW - || cv == SkPathConvexity::kConvex_CCW - || cv == SkPathConvexity::kConvex_Degenerate; -} - -static inline SkPathConvexity SkPathConvexity_OppositeConvexDirection(SkPathConvexity cv) { - SkASSERT(SkPathConvexity_IsConvex(cv)); - switch (cv) { - case SkPathConvexity::kConvex_CW: cv = SkPathConvexity::kConvex_CCW; break; - case SkPathConvexity::kConvex_CCW: cv = SkPathConvexity::kConvex_CW; break; - default: break; - } - return cv; -} - enum class SkPathFirstDirection { kCW, // == SkPathDirection::kCW kCCW, // == SkPathDirection::kCCW kUnknown, }; -static inline SkPathConvexity SkPathDirection_ToConvexity(SkPathDirection dir) { - switch (dir) { - case SkPathDirection::kCW: return SkPathConvexity::kConvex_CW; - case SkPathDirection::kCCW: return SkPathConvexity::kConvex_CCW; - } - SkUNREACHABLE; -} - -static inline SkPathConvexity SkPathFirstDirection_ToConvexity(SkPathFirstDirection dir) { - switch (dir) { - case SkPathFirstDirection::kCW: return SkPathConvexity::kConvex_CW; - case SkPathFirstDirection::kCCW: return SkPathConvexity::kConvex_CCW; - case SkPathFirstDirection::kUnknown: return SkPathConvexity::kConvex_Degenerate; - } - SkUNREACHABLE; -} - -static inline std::optional<SkPathDirection> SkPathConvexity_ToDirection(SkPathConvexity cv) { - if (cv == SkPathConvexity::kConvex_CW) { - return SkPathDirection::kCW; - } - if (cv == SkPathConvexity::kConvex_CCW) { - return SkPathDirection::kCCW; - } - return {}; -} - -static inline SkPathFirstDirection SkPathConvexity_ToFirstDirection(SkPathConvexity cv) { - if (cv == SkPathConvexity::kConvex_CW) { - return SkPathFirstDirection::kCW; - } - if (cv == SkPathConvexity::kConvex_CCW) { - return SkPathFirstDirection::kCCW; - } - return SkPathFirstDirection::kUnknown; -} - -static inline SkPathFirstDirection SkPathDirectionToFirst(SkPathDirection dir) { - return dir == SkPathDirection::kCW ? SkPathFirstDirection::kCW - : SkPathFirstDirection::kCCW; -} - #endif diff --git a/gfx/skia/skia/src/core/SkPathIter.cpp b/gfx/skia/skia/src/core/SkPathIter.cpp @@ -1,79 +0,0 @@ -/* - * 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 "include/core/SkPathIter.h" - -/* - * Close is funny -- it has no explicit point data, but we return 2 points, - * the logical 2 points that would make up the line connecting the end of - * the contour, and its beginning. - * - * To do this, we have local storage (fClosePointStorage) - */ -std::optional<SkPathIter::Rec> SkPathIter::next() { - if (vIndex >= fVerbs.size()) { - return {}; - } - - size_t n = 0; - - float w = -1; - SkPathVerb v; - switch (v = fVerbs[vIndex++]) { - case SkPathVerb::kMove: - fClosePointStorage[1] = fPoints[pIndex++]; // remember for close - return Rec{{&fClosePointStorage[1], 1}, w, v}; - case SkPathVerb::kLine: n = 1; break; - case SkPathVerb::kQuad: n = 2; break; - case SkPathVerb::kConic: n = 2; w = fConics[cIndex++]; break; - case SkPathVerb::kCubic: n = 3; break; - case SkPathVerb::kClose: - SkASSERT(pIndex > 0); - fClosePointStorage[0] = fPoints[pIndex-1]; // the last point we saw - return Rec{fClosePointStorage, w, v}; - } - - SkASSERT(pIndex > 0); - auto start = pIndex - 1; - SkASSERT(n >= 1 && n <= 3); - pIndex += n; - return Rec{{&fPoints[start], n+1}, w, v}; - -} - -/////////////////////////////////////////////// - -std::optional<SkPathContourIter::Rec> SkPathContourIter::next() { - if (fVerbs.empty()) { - return {}; - } - - SkASSERT(fVerbs[0] == SkPathVerb::kMove); - size_t npts = 1, nvbs = 1, nws = 0; - - for (size_t i = 1; i < fVerbs.size(); ++i) { - switch (fVerbs[i]) { - case SkPathVerb::kMove: goto DONE; - case SkPathVerb::kLine: npts += 1; break; - case SkPathVerb::kQuad: npts += 2; break; - case SkPathVerb::kConic: npts += 2; nws += 1; break; - case SkPathVerb::kCubic: npts += 3; break; - case SkPathVerb::kClose: nvbs += 1; goto DONE; - } - nvbs += 1; - } -DONE: - Rec rec = { - fPoints.subspan(0, npts), - fVerbs.subspan(0, nvbs), - fConics.subspan(0, nws), - }; - fPoints = fPoints.last(fPoints.size() - npts); - fVerbs = fVerbs.last( fVerbs.size() - nvbs); - fConics = fConics.last(fConics.size() - nws); - return rec; -} diff --git a/gfx/skia/skia/src/core/SkPathMeasure.cpp b/gfx/skia/skia/src/core/SkPathMeasure.cpp @@ -9,7 +9,6 @@ #include "include/core/SkContourMeasure.h" #include "include/core/SkPath.h" -#include "include/core/SkPathBuilder.h" // IWYU pragma: keep #include "include/core/SkRefCnt.h" #include "include/core/SkScalar.h" #include "include/private/base/SkTDArray.h" @@ -46,8 +45,7 @@ bool SkPathMeasure::getMatrix(SkScalar distance, SkMatrix* matrix, MatrixFlags f return fContour && fContour->getMatrix(distance, matrix, (SkContourMeasure::MatrixFlags)flags); } -bool SkPathMeasure::getSegment(SkScalar startD, SkScalar stopD, SkPathBuilder* dst, - bool startWithMoveTo) { +bool SkPathMeasure::getSegment(SkScalar startD, SkScalar stopD, SkPath* dst, bool startWithMoveTo) { return fContour && fContour->getSegment(startD, stopD, dst, startWithMoveTo); } @@ -60,18 +58,6 @@ bool SkPathMeasure::nextContour() { return !!fContour; } -#ifdef SK_SUPPORT_MUTABLE_PATHEFFECT -bool SkPathMeasure::getSegment(SkScalar startD, SkScalar stopD, SkPath* dst, - bool startWithMoveTo) { - SkPathBuilder builder; - if (this->getSegment(startD, stopD, &builder, startWithMoveTo)) { - *dst = builder.detach(); - return true; - } - return false; -} -#endif - #ifdef SK_DEBUG void SkPathMeasure::dump() {} #endif diff --git a/gfx/skia/skia/src/core/SkPathPriv.cpp b/gfx/skia/skia/src/core/SkPathPriv.cpp @@ -1,256 +0,0 @@ -/* - * Copyright 2025 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/SkPathRef.h" -#include "src/core/SkPathPriv.h" - -/* - Determines if path is a rect by keeping track of changes in direction - and looking for a loop either clockwise or counterclockwise. - - The direction is computed such that: - 0: vertical up - 1: horizontal left - 2: vertical down - 3: horizontal right - -A rectangle cycles up/right/down/left or up/left/down/right. - -The test fails if: - The path is closed, and followed by a line. - A second move creates a new endpoint. - A diagonal line is parsed. - There's more than four changes of direction. - There's a discontinuity on the line (e.g., a move in the middle) - The line reverses direction. - The path contains a quadratic or cubic. - The path contains fewer than four points. - *The rectangle doesn't complete a cycle. - *The final point isn't equal to the first point. - - *These last two conditions we relax if we have a 3-edge path that would - form a rectangle if it were closed (as we do when we fill a path) - -It's OK if the path has: - Several colinear line segments composing a rectangle side. - Single points on the rectangle side. - -The direction takes advantage of the corners found since opposite sides -must travel in opposite directions. - -FIXME: Allow colinear quads and cubics to be treated like lines. -FIXME: If the API passes fill-only, return true if the filled stroke - is a rectangle, though the caller failed to close the path. - - directions values: - 0x1 is set if the segment is horizontal - 0x2 is set if the segment is moving to the right or down - thus: - two directions are opposites iff (dirA ^ dirB) == 0x2 - two directions are perpendicular iff (dirA ^ dirB) == 0x1 - - */ -static int rect_make_dir(SkScalar dx, SkScalar dy) { - return ((0 != dx) << 0) | ((dx > 0 || dy > 0) << 1); -} - -std::optional<SkPathPriv::RectContour> SkPathPriv::IsRectContour(SkSpan<const SkPoint> ptSpan, - SkSpan<const SkPathVerb> vbSpan, - bool allowPartial) { - if (ptSpan.size() < 4) { - return {}; - } - - size_t currVerb = 0; - const size_t verbCnt = vbSpan.size(); - - int corners = 0; - SkPoint closeXY; // used to determine if final line falls on a diagonal - SkPoint lineStart; // used to construct line from previous point - const SkPoint* firstPt = nullptr; // first point in the rect (last of first moves) - const SkPoint* lastPt = nullptr; // last point in the rect (last of lines or first if closed) - SkPoint firstCorner; - SkPoint thirdCorner; - const SkPoint* pts = ptSpan.data(); - const SkPoint* savePts = nullptr; // used to allow caller to iterate through a pair of rects - lineStart.set(0, 0); - signed char directions[] = {-1, -1, -1, -1, -1}; // -1 to 3; -1 is uninitialized - bool closedOrMoved = false; - bool autoClose = false; - bool insertClose = false; - while (currVerb < verbCnt && (!allowPartial || !autoClose)) { - SkPathVerb verb = insertClose ? SkPathVerb::kClose : vbSpan[currVerb]; - switch (verb) { - case SkPathVerb::kClose: - savePts = pts; - autoClose = true; - insertClose = false; - [[fallthrough]]; - case SkPathVerb::kLine: { - if (SkPathVerb::kClose != verb) { - lastPt = pts; - } - SkPoint lineEnd = SkPathVerb::kClose == verb ? *firstPt : *pts++; - SkVector lineDelta = lineEnd - lineStart; - if (lineDelta.fX && lineDelta.fY) { - return {}; // diagonal - } - if (!lineDelta.isFinite()) { - return {}; // path contains infinity or NaN - } - if (lineStart == lineEnd) { - break; // single point on side OK - } - int nextDirection = rect_make_dir(lineDelta.fX, lineDelta.fY); // 0 to 3 - if (0 == corners) { - directions[0] = nextDirection; - corners = 1; - closedOrMoved = false; - lineStart = lineEnd; - break; - } - if (closedOrMoved) { - return {}; // closed followed by a line - } - if (autoClose && nextDirection == directions[0]) { - break; // colinear with first - } - closedOrMoved = autoClose; - if (directions[corners - 1] == nextDirection) { - if (3 == corners && SkPathVerb::kLine == verb) { - thirdCorner = lineEnd; - } - lineStart = lineEnd; - break; // colinear segment - } - directions[corners++] = nextDirection; - // opposite lines must point in opposite directions; xoring them should equal 2 - switch (corners) { - case 2: - firstCorner = lineStart; - break; - case 3: - if ((directions[0] ^ directions[2]) != 2) { - return {}; - } - thirdCorner = lineEnd; - break; - case 4: - if ((directions[1] ^ directions[3]) != 2) { - return {}; - } - break; - default: - return {}; // too many direction changes - } - lineStart = lineEnd; - break; - } - case SkPathVerb::kQuad: - case SkPathVerb::kConic: - case SkPathVerb::kCubic: - return {}; // quadratic, cubic not allowed - case SkPathVerb::kMove: - if (allowPartial && !autoClose && directions[0] >= 0) { - insertClose = true; - currVerb -= 1; // try move again afterwards - goto addMissingClose; - } - if (pts != ptSpan.data()) { - return {}; - } - if (!corners) { - firstPt = pts; - } else { - closeXY = *firstPt - *lastPt; - if (closeXY.fX && closeXY.fY) { - return {}; // we're diagonal, abort - } - } - lineStart = *pts++; - closedOrMoved = true; - break; - default: - SkDEBUGFAIL("unexpected verb"); - break; - } - currVerb += 1; - addMissingClose: - ; - } - // Success if 4 corners and first point equals last - if (corners < 3 || corners > 4) { - return {}; - } - // check if close generates diagonal - closeXY = *firstPt - *lastPt; - if (closeXY.fX && closeXY.fY) { - return {}; - } - - auto bounds = [](SkPoint a, SkPoint b) { - SkRect r; - r.set(a, b); - return r; - }; - - return {{ - bounds(firstCorner, thirdCorner), - autoClose, - directions[0] == ((directions[1] + 1) & 3) ? SkPathDirection::kCW - : SkPathDirection::kCCW, - savePts ? size_t(savePts - ptSpan.data()) : 0, - currVerb, - }}; -} - -bool SkPathPriv::IsNestedFillRects(const SkPathRaw& raw, SkRect rects[2], SkPathDirection dirs[2]) { - SkPathDirection testDirs[2]; - SkRect testRects[2]; - - SkSpan<const SkPoint> pts = raw.points(); - SkSpan<const SkPathVerb> vbs = raw.verbs(); - - auto rc = IsRectContour(pts, vbs, true); - if (!rc) { - return false; - } - - testDirs[0] = rc->fDirection; - testRects[0] = rc->fRect; - pts = pts.subspan(rc->fPointsConsumed); - vbs = vbs.subspan(rc->fVerbsConsumed); - - rc = IsRectContour(pts, vbs, false); - if (rc) { - testDirs[1] = rc->fDirection; - testRects[1] = rc->fRect; - if (testRects[0].contains(testRects[1])) { - if (rects) { - rects[0] = testRects[0]; - rects[1] = testRects[1]; - } - if (dirs) { - dirs[0] = testDirs[0]; - dirs[1] = testDirs[1]; - } - return true; - } - if (testRects[1].contains(testRects[0])) { - if (rects) { - rects[0] = testRects[1]; - rects[1] = testRects[0]; - } - if (dirs) { - dirs[0] = testDirs[1]; - dirs[1] = testDirs[0]; - } - return true; - } - } - return false; -} diff --git a/gfx/skia/skia/src/core/SkPathPriv.h b/gfx/skia/skia/src/core/SkPathPriv.h @@ -16,18 +16,14 @@ #include "include/core/SkRect.h" #include "include/core/SkRefCnt.h" #include "include/core/SkScalar.h" -#include "include/core/SkSpan.h" #include "include/core/SkTypes.h" #include "include/private/SkIDChangeListener.h" #include "include/private/SkPathRef.h" #include "include/private/base/SkDebug.h" #include "src/core/SkPathEnums.h" -#include "src/core/SkPathRaw.h" -#include <cstddef> #include <cstdint> #include <iterator> -#include <optional> #include <utility> class SkMatrix; @@ -40,22 +36,16 @@ static_assert(3 == static_cast<int>(SkPathFillType::kInverseEvenOdd), "fill_type // These are computed from a stream of verbs struct SkPathVerbAnalysis { - size_t points, weights; + int points, weights; unsigned segmentMask; bool valid; }; class SkPathPriv { public: - static SkPathConvexity ComputeConvexity(SkSpan<const SkPoint> pts, - SkSpan<const SkPathVerb> points, - SkSpan<const float> conicWeights); + static SkPathVerbAnalysis AnalyzeVerbs(const uint8_t verbs[], int count); - static uint8_t ComputeSegmentMask(SkSpan<const SkPathVerb>); - - static SkPathVerbAnalysis AnalyzeVerbs(SkSpan<const SkPathVerb> verbs); - - // skbug.com/40041027: Not a perfect solution for W plane clipping, but 1/16384 is a + // 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); @@ -81,67 +71,64 @@ public: * or the contour is known to be convex, return kUnknown. If the direction was determined, * it is cached to make subsequent calls return quickly. */ - static SkPathFirstDirection ComputeFirstDirection(const SkPathRaw&); static SkPathFirstDirection ComputeFirstDirection(const SkPath&); - static bool IsClosedSingleContour(SkSpan<const SkPathVerb> verbs) { - if (verbs.empty()) { + static bool IsClosedSingleContour(const SkPath& path) { + int verbCount = path.countVerbs(); + if (verbCount == 0) return false; - } - int moveCount = 0; - for (const auto& verb : verbs) { - switch (verb) { - case SkPathVerb::kMove: - if (++moveCount > 1) { + auto verbs = path.fPathRef->verbsBegin(); + for (int i = 0; i < verbCount; i++) { + switch (verbs[i]) { + case SkPath::Verb::kMove_Verb: + moveCount += 1; + if (moveCount > 1) { return false; } break; - case SkPathVerb::kClose: - return &verb == &verbs.back(); - default: - break; + case SkPath::Verb::kClose_Verb: + if (i == verbCount - 1) { + return true; + } + return false; + default: break; } } return false; } - static bool IsClosedSingleContour(const SkPath& path) { - return IsClosedSingleContour(path.fPathRef->verbs()); + // In some scenarios (e.g. fill or convexity checking all but the last leading move to are + // irrelevant to behavior). SkPath::injectMoveToIfNeeded should ensure that this is always at + // least 1. + static int LeadingMoveToCount(const SkPath& path) { + int verbCount = path.countVerbs(); + auto verbs = path.fPathRef->verbsBegin(); + for (int i = 0; i < verbCount; i++) { + if (verbs[i] != SkPath::Verb::kMove_Verb) { + return i; + } + } + return verbCount; // path is all move verbs } - /* - * If we're transforming a known shape (oval or rrect), this computes what happens to its - * - winding direction - * - start index - */ - static std::pair<SkPathDirection, unsigned> - TransformDirAndStart(const SkMatrix&, bool isRRect, SkPathDirection dir, unsigned start); - static void AddGenIDChangeListener(const SkPath& path, sk_sp<SkIDChangeListener> listener) { path.fPathRef->addGenIDChangeListener(std::move(listener)); } /** - * This returns the info for a rect that has a move followed by 3 or 4 lines and a close. If + * This returns true for a rect that has a move followed by 3 or 4 lines and a close. If * 'isSimpleFill' is true, an uncloseed rect will also be accepted as long as it starts and * ends at the same corner. This does not permit degenerate line or point rectangles. */ - static std::optional<SkPathRectInfo> IsSimpleRect(const SkPath& path, bool isSimpleFill); - - // Asserts the path contour was built from RRect, so it does not return - // an optional. This exists so path's can have a flag that they are really - // a RRect, without having to actually store the 4 radii... since those can - // be deduced from the contour itself. - // - static SkRRect DeduceRRectFromContour(const SkRect& bounds, - SkSpan<const SkPoint>, SkSpan<const SkPathVerb>); + static bool IsSimpleRect(const SkPath& path, bool isSimpleFill, SkRect* rect, + SkPathDirection* direction, unsigned* start); /** * Creates a path from arc params using the semantics of SkCanvas::drawArc. This function * assumes empty ovals and zero sweeps have already been filtered out. */ - static SkPath CreateDrawArcPath(const SkArc& arc, bool isFillNoPathEffect); + static void CreateDrawArcPath(SkPath* path, const SkArc& arc, bool isFillNoPathEffect); /** * Determines whether an arc produced by CreateDrawArcPath will be convex. Assumes a non-empty @@ -154,6 +141,30 @@ public: } /** + * Returns a C++11-iterable object that traverses a path's verbs in order. e.g: + * + * for (SkPath::Verb verb : SkPathPriv::Verbs(path)) { + * ... + * } + */ + struct Verbs { + public: + Verbs(const SkPath& path) : fPathRef(path.fPathRef.get()) {} + struct Iter { + void operator++() { fVerb++; } + bool operator!=(const Iter& b) const { return fVerb != b.fVerb; } + SkPath::Verb operator*() { return static_cast<SkPath::Verb>(*fVerb); } + const uint8_t* fVerb; + }; + Iter begin() { return Iter{fPathRef->verbsBegin()}; } + Iter end() { return Iter{fPathRef->verbsEnd()}; } + private: + Verbs(const Verbs&) = delete; + Verbs& operator=(const Verbs&) = delete; + SkPathRef* fPathRef; + }; + + /** * Iterates through a raw range of path verbs, points, and conics. All values are returned * unaltered. * @@ -177,15 +188,15 @@ public: : path.fPathRef->verbsEnd(), path.fPathRef->points(), path.fPathRef->conicWeights()) { } - Iterate(const SkPathVerb* verbsBegin, const SkPathVerb* verbsEnd, const SkPoint* points, + Iterate(const uint8_t* verbsBegin, const uint8_t* verbsEnd, const SkPoint* points, const SkScalar* weights) : fVerbsBegin(verbsBegin), fVerbsEnd(verbsEnd), fPoints(points), fWeights(weights) { } SkPath::RangeIter begin() { return {fVerbsBegin, fPoints, fWeights}; } SkPath::RangeIter end() { return {fVerbsEnd, nullptr, nullptr}; } private: - const SkPathVerb* fVerbsBegin; - const SkPathVerb* fVerbsEnd; + const uint8_t* fVerbsBegin; + const uint8_t* fVerbsEnd; const SkPoint* fPoints; const SkScalar* fWeights; }; @@ -193,7 +204,7 @@ public: /** * Returns a pointer to the verb data. */ - static const SkPathVerb* VerbData(const SkPath& path) { + static const uint8_t* VerbData(const SkPath& path) { return path.fPathRef->verbsBegin(); } @@ -222,16 +233,59 @@ public: return path.hasComputedBounds(); } - /** Returns the oval info if this path was created as an oval or circle, else returns {}. + /** Returns true if constructed by addCircle(), addOval(); and in some cases, + addRoundRect(), addRRect(). SkPath constructed with conicTo() or rConicTo() will not + return true though SkPath draws oval. + + rect receives bounds of oval. + dir receives SkPathDirection of oval: kCW_Direction if clockwise, kCCW_Direction if + counterclockwise. + start receives start of oval: 0 for top, 1 for right, 2 for bottom, 3 for left. + + rect, dir, and start are unmodified if oval is not found. + + Triggers performance optimizations on some GPU surface implementations. + + @param rect storage for bounding SkRect of oval; may be nullptr + @param dir storage for SkPathDirection; may be nullptr + @param start storage for start of oval; may be nullptr + @return true if SkPath was constructed by method that reduces to oval */ - static std::optional<SkPathOvalInfo> IsOval(const SkPath& path) { - return path.fPathRef->isOval(); + static bool IsOval(const SkPath& path, SkRect* rect, SkPathDirection* dir, unsigned* start) { + bool isCCW = false; + bool result = path.fPathRef->isOval(rect, &isCCW, start); + if (dir && result) { + *dir = isCCW ? SkPathDirection::kCCW : SkPathDirection::kCW; + } + return result; } - /** Returns the rrect info if this path was created as one, else returns {}. + /** Returns true if constructed by addRoundRect(), addRRect(); and if construction + is not empty, not SkRect, and not oval. SkPath constructed with other calls + will not return true though SkPath draws SkRRect. + + rrect receives bounds of SkRRect. + dir receives SkPathDirection of oval: kCW_Direction if clockwise, kCCW_Direction if + counterclockwise. + start receives start of SkRRect: 0 for top, 1 for right, 2 for bottom, 3 for left. + + rrect, dir, and start are unmodified if SkRRect is not found. + + Triggers performance optimizations on some GPU surface implementations. + + @param rrect storage for bounding SkRect of SkRRect; may be nullptr + @param dir storage for SkPathDirection; may be nullptr + @param start storage for start of SkRRect; may be nullptr + @return true if SkPath contains only SkRRect */ - static std::optional<SkPathRRectInfo> IsRRect(const SkPath& path) { - return path.fPathRef->isRRect(); + static bool IsRRect(const SkPath& path, SkRRect* rrect, SkPathDirection* dir, + unsigned* start) { + bool isCCW = false; + bool result = path.fPathRef->isRRect(rrect, &isCCW, start); + if (dir && result) { + *dir = isCCW ? SkPathDirection::kCCW : SkPathDirection::kCW; + } + return result; } /** @@ -252,6 +306,9 @@ public: return !(bounds.fLeft >= -max && bounds.fTop >= -max && bounds.fRight <= max && bounds.fBottom <= max); } + static bool TooBigForMath(const SkPath& path) { + return TooBigForMath(path.getBounds()); + } // Returns number of valid points for each SkPath::Iter verb static int PtsInIter(unsigned verb) { @@ -269,8 +326,6 @@ public: return gPtsInVerb[verb]; } - static int PtsInIter(SkPathVerb verb) { return PtsInIter((unsigned)verb); } - // Returns number of valid points for each verb, not including the "starter" // point that the Iterator adds for line/quad/conic/cubic static int PtsInVerb(unsigned verb) { @@ -288,13 +343,10 @@ public: return gPtsInVerb[verb]; } - static int PtsInVerb(SkPathVerb verb) { return PtsInVerb((unsigned)verb); } - - static bool IsAxisAligned(SkSpan<const SkPoint>); static bool IsAxisAligned(const SkPath& path); - static bool AllPointsEq(SkSpan<const SkPoint> pts) { - for (size_t i = 1; i < pts.size(); ++i) { + static bool AllPointsEq(const SkPoint pts[], int count) { + for (int i = 1; i < count; ++i) { if (pts[0] != pts[i]) { return false; } @@ -304,16 +356,9 @@ public: static int LastMoveToIndex(const SkPath& path) { return path.fLastMoveToIndex; } - struct RectContour { - SkRect fRect; - bool fIsClosed; - SkPathDirection fDirection; - size_t fPointsConsumed, - fVerbsConsumed; - }; - static std::optional<RectContour> IsRectContour(SkSpan<const SkPoint> ptSpan, - SkSpan<const SkPathVerb> vbSpan, - bool allowPartial); + static bool IsRectContour(const SkPath&, bool allowPartial, int* currVerb, + const SkPoint** ptsPtr, bool* isClosed, SkPathDirection* direction, + SkRect* rect); /** Returns true if SkPath is equivalent to nested SkRect pair when filled. If false, rect and dirs are unchanged. @@ -326,32 +371,13 @@ public: @param dirs storage for SkPathDirection pair; may be nullptr @return true if SkPath contains nested SkRect pair */ - static bool IsNestedFillRects(const SkPathRaw&, SkRect rect[2], + static bool IsNestedFillRects(const SkPath&, SkRect rect[2], SkPathDirection dirs[2] = nullptr); - static bool IsNestedFillRects(const SkPath& path, SkRect rect[2], - SkPathDirection dirs[2] = nullptr) { - return IsNestedFillRects(Raw(path), rect, dirs); - } - - static bool IsInverseFillType(SkPathFillType fill) { return (static_cast<int>(fill) & 2) != 0; } - /* - * We are effectively empty if we have zero or one verbs. - * Zero obviously means we're empty. - * One means we only have a MoveTo -- but no segments, so this is effectively - * empty (e.g. when adding another contour, this moveTo will be overwritten). - */ - static bool IsEffectivelyEmpty(const SkPath& path) { - return path.countVerbs() <= 1; - } - static bool IsEffectivelyEmpty(const SkPathBuilder& builder) { - return builder.verbs().size() <= 1; - } - /** Returns equivalent SkPath::FillType representing SkPath fill inside its bounds. . @@ -405,75 +431,15 @@ public: builder->privateReverseAddPath(reverseMe); } - static void ReversePathTo(SkPathBuilder* builder, const SkPath& reverseMe) { - builder->privateReversePathTo(reverseMe); - } - - static SkPath ReversePath(const SkPath& reverseMe) { - SkPathBuilder bu; - bu.privateReverseAddPath(reverseMe); - return bu.detach(); - } - - static std::optional<SkPoint> GetPoint(const SkPathBuilder& builder, int index) { - if ((unsigned)index < (unsigned)builder.fPts.size()) { - return builder.fPts.at(index); - } - return std::nullopt; - } - - static SkSpan<const SkPathVerb> GetVerbs(const SkPathBuilder& builder) { - return builder.fVerbs; - } - - static int CountVerbs(const SkPathBuilder& builder) { - return builder.fVerbs.size(); - } - static SkPath MakePath(const SkPathVerbAnalysis& analysis, const SkPoint points[], - SkSpan<const SkPathVerb> verbs, + const uint8_t verbs[], + int verbCount, const SkScalar conics[], SkPathFillType fillType, bool isVolatile) { - return SkPath::MakeInternal(analysis, points, verbs, conics, fillType, isVolatile); - } - - static SkPathRaw Raw(const SkPath& path) { - const SkPathRef* ref = path.fPathRef.get(); - SkASSERT(ref); - const SkRect bounds = ref->isFinite() - ? ref->getBounds() - : SkRect{SK_FloatNaN, SK_FloatNaN, SK_FloatNaN, SK_FloatNaN}; - return { - ref->pointSpan(), - ref->verbs(), - ref->conicSpan(), - bounds, - path.getFillType(), - path.isConvex(), - SkTo<uint8_t>(ref->getSegmentMasks()), - }; - } - - static SkPathRaw Raw(const SkPathBuilder& builder) { - const SkRect bounds = builder.isFinite() - ? builder.computeBounds() - : SkRect{SK_FloatNaN, SK_FloatNaN, SK_FloatNaN, SK_FloatNaN}; - SkPathConvexity convexity = builder.fConvexity; - if (convexity == SkPathConvexity::kUnknown) { - convexity = SkPathPriv::ComputeConvexity( - builder.fPts, builder.fVerbs, builder.fConicWeights); - } - return { - builder.points(), - builder.verbs(), - builder.conicWeights(), - bounds, - builder.fillType(), - SkPathConvexity_IsConvex(convexity), - SkTo<uint8_t>(builder.fSegmentMask), - }; + return SkPath::MakeInternal(analysis, points, verbs, verbCount, conics, fillType, + isVolatile); } }; @@ -483,8 +449,8 @@ public: // Roughly the same as SkPath::Iter(path, true), but does not return moves or closes // class SkPathEdgeIter { - const SkPathVerb* fVerbs; - const SkPathVerb* fVerbsStop; + const uint8_t* fVerbs; + const uint8_t* fVerbsStop; const SkPoint* fPts; const SkPoint* fMoveToPtr; const SkScalar* fConicWeights; @@ -493,9 +459,12 @@ class SkPathEdgeIter { bool fNextIsNewContour; SkDEBUGCODE(bool fIsConic;) + enum { + kIllegalEdgeValue = 99 + }; + public: SkPathEdgeIter(const SkPath& path); - SkPathEdgeIter(const SkPathRaw&); SkScalar conicWeight() const { SkASSERT(fIsConic); @@ -503,18 +472,16 @@ public: } enum class Edge { - kLine = (int)SkPathVerb::kLine, - kQuad = (int)SkPathVerb::kQuad, - kConic = (int)SkPathVerb::kConic, - kCubic = (int)SkPathVerb::kCubic, - kInvalid = 99, + kLine = SkPath::kLine_Verb, + kQuad = SkPath::kQuad_Verb, + kConic = SkPath::kConic_Verb, + kCubic = SkPath::kCubic_Verb, }; - static SkPathVerb EdgeToVerb(Edge e) { - return SkPathVerb(e); + static SkPath::Verb EdgeToVerb(Edge e) { + return SkPath::Verb(e); } - // todo: return as optional? fPts become span? struct Result { const SkPoint* fPts; // points for the segment, or null if done Edge fEdge; @@ -536,14 +503,16 @@ public: for (;;) { SkASSERT(fVerbs <= fVerbsStop); if (fVerbs == fVerbsStop) { - return fNeedsCloseLine ? closeline() : Result{nullptr, Edge::kInvalid, false}; + return fNeedsCloseLine + ? closeline() + : Result{ nullptr, Edge(kIllegalEdgeValue), false }; } SkDEBUGCODE(fIsConic = false;) - const auto verb = *fVerbs++; - switch (verb) { - case SkPathVerb::kMove: { + const auto v = *fVerbs++; + switch (v) { + case SkPath::kMove_Verb: { if (fNeedsCloseLine) { auto res = closeline(); fMoveToPtr = fPts++; @@ -552,11 +521,10 @@ public: fMoveToPtr = fPts++; fNextIsNewContour = true; } break; - case SkPathVerb::kClose: + case SkPath::kClose_Verb: if (fNeedsCloseLine) return closeline(); break; default: { - unsigned v = static_cast<unsigned>(verb); // Actual edge. const int pts_count = (v+2) / 2, cws_count = (v & (v-1)) / 2; @@ -566,7 +534,7 @@ public: fPts += pts_count; fConicWeights += cws_count; - SkDEBUGCODE(fIsConic = (verb == SkPathVerb::kConic);) + SkDEBUGCODE(fIsConic = (v == SkPath::kConic_Verb);) SkASSERT(fIsConic == (cws_count > 0)); bool isNewContour = fNextIsNewContour; diff --git a/gfx/skia/skia/src/core/SkPathRaw.cpp b/gfx/skia/skia/src/core/SkPathRaw.cpp @@ -1,37 +0,0 @@ -/* - * 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 "include/core/SkPathTypes.h" -#include "include/private/base/SkAssert.h" -#include "src/core/SkPathPriv.h" -#include "src/core/SkPathRaw.h" - -const uint8_t gVerbToSegmentMask[] = { - 0, // move - kLine_SkPathSegmentMask, - kQuad_SkPathSegmentMask, - kConic_SkPathSegmentMask, - kCubic_SkPathSegmentMask, - 0, // close -}; - -uint8_t SkPathPriv::ComputeSegmentMask(SkSpan<const SkPathVerb> verbs) { - unsigned mask = 0; - for (auto v : verbs) { - unsigned i = static_cast<unsigned>(v); - SkASSERT(i < std::size(gVerbToSegmentMask)); - mask |= gVerbToSegmentMask[i]; - } - return SkTo<uint8_t>(mask); -} - -std::optional<SkRect> SkPathRaw::isRect() const { - if (auto rc = SkPathPriv::IsRectContour(fPoints, fVerbs, false)) { - return rc->fRect; - } - return {}; -} diff --git a/gfx/skia/skia/src/core/SkPathRaw.h b/gfx/skia/skia/src/core/SkPathRaw.h @@ -1,55 +0,0 @@ -/* - * 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 SkPathRaw_DEFINED -#define SkPathRaw_DEFINED - -#include "include/core/SkPathIter.h" -#include "include/core/SkPathTypes.h" -#include "include/core/SkPoint.h" -#include "include/core/SkRect.h" -#include "include/core/SkSpan.h" - -#include <array> -#include <cstddef> -#include <optional> - -/** - * SkPathRaw is a non-owning, immutable view of the path geometry. - * - * It allows us to have stack-allocated paths, see SkPathRawShapes.h - * - * It is the responsibility of the creator to ensure that the spans in SkPathRaw point to valid - * data that outlives the SkPathRaw instance. - */ -struct SkPathRaw { - SkSpan<const SkPoint> fPoints; - SkSpan<const SkPathVerb> fVerbs; - SkSpan<const float> fConics; - SkRect fBounds; - SkPathFillType fFillType; - bool fIsConvex; - // See SkPath::SegmentMask - uint8_t fSegmentMask; - - SkSpan<const SkPoint> points() const { return fPoints; } - SkSpan<const SkPathVerb> verbs() const { return fVerbs; } - SkSpan<const float> conics() const { return fConics; } - SkRect bounds() const { return fBounds; } - SkPathFillType fillType() const { return fFillType; } - bool isConvex() const { return fIsConvex; } - unsigned segmentMasks() const { return fSegmentMask; } - - bool empty() const { return fVerbs.empty(); } - bool isInverseFillType() const { return SkPathFillType_IsInverse(fFillType); } - - std::optional<SkRect> isRect() const; - - SkPathIter iter() const { return {fPoints, fVerbs, fConics}; } -}; - -#endif diff --git a/gfx/skia/skia/src/core/SkPathRawShapes.cpp b/gfx/skia/skia/src/core/SkPathRawShapes.cpp @@ -1,202 +0,0 @@ -/* - * 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/SkPathRawShapes.h" - -#include "include/core/SkPathTypes.h" -#include "include/core/SkRRect.h" -#include "include/private/base/SkAssert.h" -#include "src/core/SkPathMakers.h" - -const SkPathFillType kDefFillType = SkPathFillType::kWinding; - -const SkPathVerb gRectVerbs[] = { - SkPathVerb::kMove, - SkPathVerb::kLine, - SkPathVerb::kLine, - SkPathVerb::kLine, - SkPathVerb::kClose -}; - -const uint8_t gRectSegMask = kLine_SkPathSegmentMask; - -static void set_as_rect(SkPathRaw* raw, SkSpan<SkPoint> storage, - const SkRect& r, SkPathDirection dir, unsigned index) { - SkASSERT(storage.size() >= 4); - - raw->fPoints = { storage.data(), 4 }; - raw->fVerbs = gRectVerbs; - raw->fConics = {}; - raw->fBounds = r; - raw->fFillType = kDefFillType; - raw->fIsConvex = true; - raw->fSegmentMask = gRectSegMask; - - SkPath_RectPointIterator iter(r, dir, index); - - storage[0] = iter.current(); - storage[1] = iter.next(); - storage[2] = iter.next(); - storage[3] = iter.next(); -} - -////////////////// - -const SkPathVerb gOvalVerbs[] = { - SkPathVerb::kMove, - SkPathVerb::kConic, - SkPathVerb::kConic, - SkPathVerb::kConic, - SkPathVerb::kConic, - SkPathVerb::kClose -}; - -const uint8_t gOvalSegMask = kConic_SkPathSegmentMask; - -const float gFourQuarterCircleConics[] = { - SK_ScalarRoot2Over2, - SK_ScalarRoot2Over2, - SK_ScalarRoot2Over2, - SK_ScalarRoot2Over2, -}; - -static void set_as_oval(SkPathRaw* raw, SkSpan<SkPoint> storage, - const SkRect& r, SkPathDirection dir, unsigned index) { - SkASSERT(storage.size() >= 9); - - raw->fPoints = { storage.data(), 9 }; - raw->fVerbs = gOvalVerbs; - raw->fConics = gFourQuarterCircleConics; - raw->fBounds = r; - raw->fFillType = kDefFillType; - raw->fIsConvex = true; - raw->fSegmentMask = gOvalSegMask; - - SkPath_OvalPointIterator ovalIter(r, dir, index); - SkPath_RectPointIterator rectIter(r, dir, index + (dir == SkPathDirection::kCW ? 0 : 1)); - - storage[0] = ovalIter.current(); - for (unsigned i = 0; i < 4; ++i) { - storage[i*2 + 1] = rectIter.next(); - storage[i*2 + 2] = ovalIter.next(); - } -} - -///////////////////////////// - -const SkPathVerb gRRectVerbs_LineStart[] = { - SkPathVerb::kMove, - SkPathVerb::kLine, SkPathVerb::kConic, - SkPathVerb::kLine, SkPathVerb::kConic, - SkPathVerb::kLine, SkPathVerb::kConic, - SkPathVerb::kLine, SkPathVerb::kConic, - SkPathVerb::kClose -}; - -const SkPathVerb gRRectVerbs_ConicStart[] = { - SkPathVerb::kMove, - SkPathVerb::kConic, SkPathVerb::kLine, - SkPathVerb::kConic, SkPathVerb::kLine, - SkPathVerb::kConic, SkPathVerb::kLine, - SkPathVerb::kConic, // we can skip the last line - SkPathVerb::kClose -}; - -const uint8_t gRRectSegMask = kLine_SkPathSegmentMask | kConic_SkPathSegmentMask; - -static void set_as_rrect(SkPathRaw* raw, SkSpan<SkPoint> storage, - const SkRRect& rrect, SkPathDirection dir, unsigned index) { - // we start with a conic on odd indices when moving CW vs. even indices when moving CCW - const bool startsWithConic = ((index & 1) == (dir == SkPathDirection::kCW)); - // if we start with a conic, we end with a line, which we can skip (relying on close()) - const size_t npoints = 13 - startsWithConic; - const SkRect& bounds = rrect.getBounds(); - - SkASSERT(storage.size() >= npoints); - - raw->fPoints = { storage.data(), npoints }; - if (startsWithConic) { - raw->fVerbs = gRRectVerbs_ConicStart; - } else { - raw->fVerbs = gRRectVerbs_LineStart; - } - raw->fConics = gFourQuarterCircleConics; - raw->fBounds = bounds; - raw->fFillType = kDefFillType; - raw->fIsConvex = true; - raw->fSegmentMask = gRRectSegMask; - - SkPath_RRectPointIterator rrectIter(rrect, dir, index); - // Corner iterator indices follow the collapsed radii model, - // adjusted such that the start pt is "behind" the radii start pt. - const unsigned rectStartIndex = index / 2 + (dir == SkPathDirection::kCW ? 0 : 1); - SkPath_RectPointIterator rectIter(bounds, dir, rectStartIndex); - - storage[0] = rrectIter.current(); - if (startsWithConic) { - for (unsigned i = 0; i < 3; ++i) { - // conic points - storage[i*3 + 1] = rectIter.next(); - storage[i*3 + 2] = rrectIter.next(); - // line point - storage[i*3 + 3] = rrectIter.next(); - } - // last conic points - storage[10] = rectIter.next(); - storage[11] = rrectIter.next(); - // the final line is accomplished by close() - } else { - for (unsigned i = 0; i < 4; ++i) { - // line point - storage[i*3 + 1] = rrectIter.next(); - // conic points - storage[i*3 + 2] = rectIter.next(); - storage[i*3 + 3] = rrectIter.next(); - } - } - // close -} - -///////////////////////////// - -SkPathRawShapes::Rect::Rect(const SkRect& r, SkPathDirection dir, unsigned index) { - set_as_rect(this, fStorage, r, dir, index); -} - -SkPathRawShapes::Oval::Oval(const SkRect& r, SkPathDirection dir, unsigned index) { - set_as_oval(this, fStorage, r, dir, index); -} - -SkPathRawShapes::RRect::RRect(const SkRRect& rrect, SkPathDirection dir, unsigned index) { - const SkRect& bounds = rrect.getBounds(); - - if (rrect.isRect() || rrect.isEmpty()) { - // degenerate(rect) => radii points are collapsing - set_as_rect(this, fStorage, bounds, dir, (index + 1) / 2); - } else if (rrect.isOval()) { - // degenerate(oval) => line points are collapsing - set_as_oval(this, fStorage, bounds, dir, index / 2); - } else { - set_as_rrect(this, fStorage, rrect, dir, index); - } -} - -///////////////////////////// - -const SkPathVerb gTriangle_Verbs[] = { - SkPathVerb::kMove, - SkPathVerb::kLine, - SkPathVerb::kLine, - SkPathVerb::kClose -}; - -SkPathRawShapes::Triangle::Triangle(SkSpan<const SkPoint> threePoints, const SkRect& bounds) - : SkPathRaw{threePoints, gTriangle_Verbs, {}, bounds, - SkPathFillType::kDefault, true, kLine_SkPathSegmentMask} -{ - SkASSERT(threePoints.size() == 3); - SkASSERT(SkRect::Bounds(threePoints).value() == bounds); -} diff --git a/gfx/skia/skia/src/core/SkPathRawShapes.h b/gfx/skia/skia/src/core/SkPathRawShapes.h @@ -1,58 +0,0 @@ -/* - * 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 SkPathRawShapes_DEFINED -#define SkPathRawShapes_DEFINED - -#include "include/core/SkPathTypes.h" -#include "include/core/SkPoint.h" -#include "src/core/SkPathRaw.h" - -class SkRRect; -struct SkRect; - -/** - * These classes provide their own stack-based storage for path data, making them efficient - * alternatives to SkPath for known geometries, avoiding heap allocations. - * - * The defaults were chosen to match those in SkPathBuilder.h - */ -namespace SkPathRawShapes { - -struct Rect : public SkPathRaw { - SkPoint fStorage[4]; // move + 3 lines (+ close) - - Rect(const SkRect&, - SkPathDirection = SkPathDirection::kCW, - unsigned index = 0); -}; - -struct Oval : public SkPathRaw { - SkPoint fStorage[9]; // move + 4 conics (+ close) - - Oval(const SkRect&, - SkPathDirection = SkPathDirection::kCW, - unsigned index = 1); -}; - -struct RRect : public SkPathRaw { - SkPoint fStorage[13]; // worse case: move + 4 conics + 4 lines (+ close) - - RRect(const SkRRect&, SkPathDirection dir, unsigned index); - RRect(const SkRRect& rr, SkPathDirection dir) - : RRect(rr, dir, (dir == SkPathDirection::kCW ? 6 : 7)) {} - RRect(const SkRRect& rr) - : RRect(rr, SkPathDirection::kCW, 6) {} -}; - -struct Triangle : public SkPathRaw { - Triangle(SkSpan<const SkPoint> threePoints, const SkRect& bounds); -}; - -} // namespace SkPathRawShapes - -#endif diff --git a/gfx/skia/skia/src/core/SkPathRef.cpp b/gfx/skia/skia/src/core/SkPathRef.cpp @@ -19,7 +19,7 @@ #include <utility> #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK - static constexpr int kPathRefGenIDBitCnt = 30; // leave room for the fill type (skbug.com/40032862) + static constexpr int kPathRefGenIDBitCnt = 30; // leave room for the fill type (skbug.com/1762) #else static constexpr int kPathRefGenIDBitCnt = 32; #endif @@ -84,12 +84,9 @@ SkPathRef* SkPathRef::CreateEmpty() { return SkRef(gEmpty); } -std::pair<SkPathDirection, unsigned> -SkPathPriv::TransformDirAndStart(const SkMatrix& matrix, bool isRRect, SkPathDirection dir, - unsigned start) { - unsigned inStart = start; - bool isCCW = (dir == SkPathDirection::kCCW); - +static void transform_dir_and_start(const SkMatrix& matrix, bool isRRect, bool* isCCW, + unsigned* start) { + int inStart = *start; int rm = 0; if (isRRect) { // Degenerate rrect indices to oval indices and remember the remainder. @@ -125,26 +122,21 @@ SkPathPriv::TransformDirAndStart(const SkMatrix& matrix, bool isRRect, SkPathDir if (sameSign != antiDiag) { // This is a rotation (and maybe scale). The direction is unchanged. // Trust me on the start computation (or draw yourself some pictures) - start = (inStart + 4 - (topNeg | antiDiag)) % 4; - SkASSERT(start < 4); + *start = (inStart + 4 - (topNeg | antiDiag)) % 4; + SkASSERT(*start < 4); if (isRRect) { - start = 2 * start + rm; + *start = 2 * *start + rm; } } else { // This is a mirror (and maybe scale). The direction is reversed. - isCCW = !isCCW; + *isCCW = !*isCCW; // Trust me on the start computation (or draw yourself some pictures) - start = (6 + (topNeg | antiDiag) - inStart) % 4; - SkASSERT(start < 4); + *start = (6 + (topNeg | antiDiag) - inStart) % 4; + SkASSERT(*start < 4); if (isRRect) { - start = 2 * start + (rm ? 0 : 1); + *start = 2 * *start + (rm ? 0 : 1); } } - - return { - isCCW ? SkPathDirection::kCCW : SkPathDirection::kCW, - start - }; } void SkPathRef::CreateTransformedCopy(sk_sp<SkPathRef>* dst, @@ -179,7 +171,7 @@ void SkPathRef::CreateTransformedCopy(sk_sp<SkPathRef>* dst, // don't copy, just allocate the points (*dst)->fPoints.resize(src.fPoints.size()); } - matrix.mapPoints((*dst)->fPoints, src.fPoints); + matrix.mapPoints((*dst)->fPoints.begin(), src.fPoints.begin(), src.fPoints.size()); // Need to check this here in case (&src == dst) bool canXformBounds = !src.fBoundsIsDirty && matrix.rectStaysRect() && src.countPoints() > 1; @@ -222,16 +214,19 @@ void SkPathRef::CreateTransformedCopy(sk_sp<SkPathRef>* dst, (*dst)->fSegmentMask = src.fSegmentMask; - // It's an oval/rrect only if rect stays rect. - const SkPathIsAType newType = matrix.rectStaysRect() ? src.fType : SkPathIsAType::kGeneral; - + // It's an oval only if it stays a rect. Technically if scale is uniform, then it would stay an + // arc. For now, don't bother handling that (we'd also need to fixup the angles for negative + // scale, etc.) + bool rectStaysRect = matrix.rectStaysRect(); + const PathType newType = + (rectStaysRect && src.fType != PathType::kArc) ? src.fType : PathType::kGeneral; (*dst)->fType = newType; - if (newType == SkPathIsAType::kOval || newType == SkPathIsAType::kRRect) { - auto [dir, start] = - SkPathPriv::TransformDirAndStart(matrix, newType == SkPathIsAType::kRRect, - src.fIsA.fDirection, src.fIsA.fStartIndex); - (*dst)->fIsA.fDirection = dir; - (*dst)->fIsA.fStartIndex = start; + if (newType == PathType::kOval || newType == PathType::kRRect) { + unsigned start = src.fRRectOrOvalStartIdx; + bool isCCW = SkToBool(src.fRRectOrOvalIsCCW); + transform_dir_and_start(matrix, newType == PathType::kRRect, &isCCW, &start); + (*dst)->fRRectOrOvalIsCCW = isCCW; + (*dst)->fRRectOrOvalStartIdx = start; } if (dst->get() == &src) { @@ -252,7 +247,7 @@ void SkPathRef::Rewind(sk_sp<SkPathRef>* pathRef) { (*pathRef)->fVerbs.clear(); (*pathRef)->fConicWeights.clear(); (*pathRef)->fSegmentMask = 0; - (*pathRef)->fType = SkPathIsAType::kGeneral; + (*pathRef)->fType = PathType::kGeneral; SkDEBUGCODE((*pathRef)->validate();) } else { int oldVCnt = (*pathRef)->countVerbs(); @@ -306,7 +301,12 @@ void SkPathRef::copy(const SkPathRef& ref, } fSegmentMask = ref.fSegmentMask; fType = ref.fType; - fIsA = ref.fIsA; + fRRectOrOvalIsCCW = ref.fRRectOrOvalIsCCW; + fRRectOrOvalStartIdx = ref.fRRectOrOvalStartIdx; + fArcOval = ref.fArcOval; + fArcStartAngle = ref.fArcStartAngle; + fArcSweepAngle = ref.fArcSweepAngle; + fArcType = ref.fArcType; SkDEBUGCODE(this->validate();) } @@ -318,7 +318,7 @@ void SkPathRef::interpolate(const SkPathRef& ending, SkScalar weight, SkPathRef* outValues[index] = outValues[index] * weight + inValues[index] * (1 - weight); } out->fBoundsIsDirty = true; - out->fType = SkPathIsAType::kGeneral; + out->fType = PathType::kGeneral; } std::tuple<SkPoint*, SkScalar*> SkPathRef::growForVerbsInPath(const SkPathRef& path) { @@ -326,7 +326,7 @@ std::tuple<SkPoint*, SkScalar*> SkPathRef::growForVerbsInPath(const SkPathRef& p fSegmentMask |= path.fSegmentMask; fBoundsIsDirty = true; // this also invalidates fIsFinite - fType = SkPathIsAType::kGeneral; + fType = PathType::kGeneral; if (int numVerbs = path.countVerbs()) { memcpy(fVerbs.push_back_n(numVerbs), path.fVerbs.begin(), numVerbs * sizeof(fVerbs[0])); @@ -346,42 +346,50 @@ std::tuple<SkPoint*, SkScalar*> SkPathRef::growForVerbsInPath(const SkPathRef& p return {pts, weights}; } -SkPoint* SkPathRef::growForRepeatedVerb(SkPathVerb verb, +SkPoint* SkPathRef::growForRepeatedVerb(int /*SkPath::Verb*/ verb, int numVbs, SkScalar** weights) { SkDEBUGCODE(this->validate();) - int pCnt = 0; + int pCnt; switch (verb) { - case SkPathVerb::kMove: + case SkPath::kMove_Verb: pCnt = numVbs; break; - case SkPathVerb::kLine: + case SkPath::kLine_Verb: fSegmentMask |= SkPath::kLine_SegmentMask; pCnt = numVbs; break; - case SkPathVerb::kQuad: + case SkPath::kQuad_Verb: fSegmentMask |= SkPath::kQuad_SegmentMask; pCnt = 2 * numVbs; break; - case SkPathVerb::kConic: + case SkPath::kConic_Verb: fSegmentMask |= SkPath::kConic_SegmentMask; pCnt = 2 * numVbs; break; - case SkPathVerb::kCubic: + case SkPath::kCubic_Verb: fSegmentMask |= SkPath::kCubic_SegmentMask; pCnt = 3 * numVbs; break; - case SkPathVerb::kClose: - SkDEBUGFAIL("growForRepeatedVerb called for kClose"); + case SkPath::kClose_Verb: + SkDEBUGFAIL("growForRepeatedVerb called for kClose_Verb"); + pCnt = 0; + break; + case SkPath::kDone_Verb: + SkDEBUGFAIL("growForRepeatedVerb called for kDone"); + pCnt = 0; + break; + default: + SkDEBUGFAIL("default should not be reached"); pCnt = 0; break; } fBoundsIsDirty = true; // this also invalidates fIsFinite - fType = SkPathIsAType::kGeneral; + fType = PathType::kGeneral; - memset(fVerbs.push_back_n(numVbs), (uint8_t)verb, numVbs); - if (SkPathVerb::kConic == verb) { + memset(fVerbs.push_back_n(numVbs), verb, numVbs); + if (SkPath::kConic_Verb == verb) { SkASSERT(weights); *weights = fConicWeights.push_back_n(numVbs); } @@ -391,41 +399,49 @@ SkPoint* SkPathRef::growForRepeatedVerb(SkPathVerb verb, return pts; } -SkPoint* SkPathRef::growForVerb(SkPathVerb verb, SkScalar weight) { +SkPoint* SkPathRef::growForVerb(int /* SkPath::Verb*/ verb, SkScalar weight) { SkDEBUGCODE(this->validate();) - int pCnt = 0; + int pCnt; unsigned mask = 0; switch (verb) { - case SkPathVerb::kMove: + case SkPath::kMove_Verb: pCnt = 1; break; - case SkPathVerb::kLine: + case SkPath::kLine_Verb: mask = SkPath::kLine_SegmentMask; pCnt = 1; break; - case SkPathVerb::kQuad: + case SkPath::kQuad_Verb: mask = SkPath::kQuad_SegmentMask; pCnt = 2; break; - case SkPathVerb::kConic: + case SkPath::kConic_Verb: mask = SkPath::kConic_SegmentMask; pCnt = 2; break; - case SkPathVerb::kCubic: + case SkPath::kCubic_Verb: mask = SkPath::kCubic_SegmentMask; pCnt = 3; break; - case SkPathVerb::kClose: + case SkPath::kClose_Verb: + pCnt = 0; + break; + case SkPath::kDone_Verb: + SkDEBUGFAIL("growForVerb called for kDone"); + pCnt = 0; + break; + default: + SkDEBUGFAIL("default is not reached"); pCnt = 0; break; } fSegmentMask |= mask; fBoundsIsDirty = true; // this also invalidates fIsFinite - fType = SkPathIsAType::kGeneral; + fType = PathType::kGeneral; fVerbs.push_back(verb); - if (SkPathVerb::kConic == verb) { + if (SkPath::kConic_Verb == verb) { fConicWeights.push_back(weight); } SkPoint* pts = fPoints.push_back_n(pCnt); @@ -469,54 +485,40 @@ void SkPathRef::callGenIDChangeListeners() { fGenIDChangeListeners.changed(); } -SkRRect SkPathPriv::DeduceRRectFromContour(const SkRect& bounds, SkSpan<const SkPoint> pts, - SkSpan<const SkPathVerb> vbs) { - SkASSERT(!vbs.empty()); - SkASSERT(vbs.front() == SkPathVerb::kMove); - +SkRRect SkPathRef::getRRect() const { + const SkRect& bounds = this->getBounds(); SkVector radii[4] = {{0, 0}, {0, 0}, {0, 0}, {0, 0}}; - - size_t ptIndex = 0; - for (const SkPathVerb verb : vbs) { - switch (verb) { - case SkPathVerb::kMove: - SkASSERT(ptIndex == 0); // we only expect 1 move - ptIndex += 1; - break; - case SkPathVerb::kLine: { - // we only expect horizontal or vertical lines - SkDEBUGCODE(const SkVector delta = pts[ptIndex] - pts[ptIndex-1];) - SkASSERT(delta.fX == 0 || delta.fY == 0); - ptIndex += 1; - } break; - case SkPathVerb::kQuad: SkASSERT(false); break; - case SkPathVerb::kCubic: SkASSERT(false); break; - case SkPathVerb::kConic: { - SkVector v1_0 = pts[ptIndex] - pts[ptIndex - 1]; - SkVector v2_1 = pts[ptIndex + 1] - pts[ptIndex]; - SkVector dxdy; - if (v1_0.fX) { - SkASSERT(!v2_1.fX && !v1_0.fY); - dxdy.set(SkScalarAbs(v1_0.fX), SkScalarAbs(v2_1.fY)); - } else if (!v1_0.fY) { - SkASSERT(!v2_1.fX || !v2_1.fY); - dxdy.set(SkScalarAbs(v2_1.fX), SkScalarAbs(v2_1.fY)); - } else { - SkASSERT(!v2_1.fY); - dxdy.set(SkScalarAbs(v2_1.fX), SkScalarAbs(v1_0.fY)); - } - SkRRect::Corner corner = - pts[ptIndex].fX == bounds.fLeft ? - pts[ptIndex].fY == bounds.fTop ? + Iter iter(*this); + SkPoint pts[4]; + uint8_t verb = iter.next(pts); + SkASSERT(SkPath::kMove_Verb == verb); + while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { + if (SkPath::kConic_Verb == verb) { + SkVector v1_0 = pts[1] - pts[0]; + SkVector v2_1 = pts[2] - pts[1]; + SkVector dxdy; + if (v1_0.fX) { + SkASSERT(!v2_1.fX && !v1_0.fY); + dxdy.set(SkScalarAbs(v1_0.fX), SkScalarAbs(v2_1.fY)); + } else if (!v1_0.fY) { + SkASSERT(!v2_1.fX || !v2_1.fY); + dxdy.set(SkScalarAbs(v2_1.fX), SkScalarAbs(v2_1.fY)); + } else { + SkASSERT(!v2_1.fY); + dxdy.set(SkScalarAbs(v2_1.fX), SkScalarAbs(v1_0.fY)); + } + SkRRect::Corner corner = + pts[1].fX == bounds.fLeft ? + pts[1].fY == bounds.fTop ? SkRRect::kUpperLeft_Corner : SkRRect::kLowerLeft_Corner : - pts[ptIndex].fY == bounds.fTop ? + pts[1].fY == bounds.fTop ? SkRRect::kUpperRight_Corner : SkRRect::kLowerRight_Corner; - SkASSERT(!radii[corner].fX && !radii[corner].fY); - radii[corner] = dxdy; - ptIndex += 2; - } break; - case SkPathVerb::kClose: - break; + SkASSERT(!radii[corner].fX && !radii[corner].fY); + radii[corner] = dxdy; + } else { + SkASSERT((verb == SkPath::kLine_Verb + && (!(pts[1].fX - pts[0].fX) || !(pts[1].fY - pts[0].fY))) + || verb == SkPath::kClose_Verb); } } SkRRect rrect; @@ -524,30 +526,124 @@ SkRRect SkPathPriv::DeduceRRectFromContour(const SkRect& bounds, SkSpan<const Sk return rrect; } -std::optional<SkPathRRectInfo> SkPathRef::isRRect() const { - if (fType == SkPathIsAType::kRRect) { - return {{ - SkPathPriv::DeduceRRectFromContour(this->getBounds(), this->pointSpan(), this->verbs()), - fIsA.fDirection, - fIsA.fStartIndex, - }}; +bool SkPathRef::isRRect(SkRRect* rrect, bool* isCCW, unsigned* start) const { + if (fType == PathType::kRRect) { + if (rrect) { + *rrect = this->getRRect(); + } + if (isCCW) { + *isCCW = SkToBool(fRRectOrOvalIsCCW); + } + if (start) { + *start = fRRectOrOvalStartIdx; + } } - return {}; + return fType == PathType::kRRect; } /////////////////////////////////////////////////////////////////////////////// +SkPathRef::Iter::Iter() { +#ifdef SK_DEBUG + fPts = nullptr; + fConicWeights = nullptr; +#endif + // need to init enough to make next() harmlessly return kDone_Verb + fVerbs = nullptr; + fVerbStop = nullptr; +} + +SkPathRef::Iter::Iter(const SkPathRef& path) { + this->setPathRef(path); +} + +void SkPathRef::Iter::setPathRef(const SkPathRef& path) { + fPts = path.points(); + fVerbs = path.verbsBegin(); + fVerbStop = path.verbsEnd(); + fConicWeights = path.conicWeights(); + if (fConicWeights) { + fConicWeights -= 1; // begin one behind + } + + // Don't allow iteration through non-finite points. + if (!path.isFinite()) { + fVerbStop = fVerbs; + } +} + +uint8_t SkPathRef::Iter::next(SkPoint pts[4]) { + SkASSERT(pts); + + SkDEBUGCODE(unsigned peekResult = this->peek();) + + if (fVerbs == fVerbStop) { + SkASSERT(peekResult == SkPath::kDone_Verb); + return (uint8_t) SkPath::kDone_Verb; + } + + // fVerbs points one beyond next verb so decrement first. + unsigned verb = *fVerbs++; + const SkPoint* srcPts = fPts; + + switch (verb) { + case SkPath::kMove_Verb: + pts[0] = srcPts[0]; + srcPts += 1; + break; + case SkPath::kLine_Verb: + pts[0] = srcPts[-1]; + pts[1] = srcPts[0]; + srcPts += 1; + break; + case SkPath::kConic_Verb: + fConicWeights += 1; + [[fallthrough]]; + case SkPath::kQuad_Verb: + pts[0] = srcPts[-1]; + pts[1] = srcPts[0]; + pts[2] = srcPts[1]; + srcPts += 2; + break; + case SkPath::kCubic_Verb: + pts[0] = srcPts[-1]; + pts[1] = srcPts[0]; + pts[2] = srcPts[1]; + pts[3] = srcPts[2]; + srcPts += 3; + break; + case SkPath::kClose_Verb: + break; + case SkPath::kDone_Verb: + SkASSERT(fVerbs == fVerbStop); + break; + } + fPts = srcPts; + SkASSERT(peekResult == verb); + return (uint8_t) verb; +} + +uint8_t SkPathRef::Iter::peek() const { + return fVerbs < fVerbStop ? *fVerbs : (uint8_t) SkPath::kDone_Verb; +} + + bool SkPathRef::isValid() const { switch (fType) { - case SkPathIsAType::kGeneral: + case PathType::kGeneral: + break; + case PathType::kOval: + if (fRRectOrOvalStartIdx >= 4) { + return false; + } break; - case SkPathIsAType::kOval: - if (fIsA.fStartIndex >= 4) { + case PathType::kRRect: + if (fRRectOrOvalStartIdx >= 8) { return false; } break; - case SkPathIsAType::kRRect: - if (fIsA.fStartIndex >= 8) { + case PathType::kArc: + if (!(fArcOval.isFinite() && SkIsFinite(fArcStartAngle, fArcSweepAngle))) { return false; } break; @@ -595,9 +691,9 @@ void SkPathRef::reset() { } bool SkPathRef::dataMatchesVerbs() const { - const auto info = SkPathPriv::AnalyzeVerbs(fVerbs); + const auto info = SkPathPriv::AnalyzeVerbs(fVerbs.begin(), fVerbs.size()); return info.valid && info.segmentMask == fSegmentMask && - info.points == (size_t)fPoints.size() && - info.weights == (size_t)fConicWeights.size(); + info.points == fPoints.size() && + info.weights == fConicWeights.size(); } diff --git a/gfx/skia/skia/src/core/SkPathUtils.cpp b/gfx/skia/skia/src/core/SkPathUtils.cpp @@ -10,15 +10,12 @@ #include "include/core/SkMatrix.h" #include "include/core/SkPaint.h" #include "include/core/SkPath.h" -#include "include/core/SkPathBuilder.h" #include "include/core/SkPathEffect.h" -#include "include/core/SkScalar.h" #include "include/core/SkStrokeRec.h" #include "src/core/SkMatrixPriv.h" namespace skpathutils { -#ifdef SK_SUPPORT_MUTABLE_PATHEFFECT bool FillPathWithPaint(const SkPath& src, const SkPaint& paint, SkPath* dst) { return skpathutils::FillPathWithPaint(src, paint, dst, nullptr, 1); } @@ -31,17 +28,8 @@ bool FillPathWithPaint(const SkPath& src, const SkPaint& paint, SkPath* dst, bool FillPathWithPaint(const SkPath& src, const SkPaint& paint, SkPath* dst, const SkRect* cullRect, const SkMatrix& ctm) { - SkPathBuilder builder; - bool isFilled = FillPathWithPaint(src, paint, &builder, cullRect, ctm); - *dst = builder.detach(); - return isFilled; -} -#endif - -bool FillPathWithPaint(const SkPath& origSrc, const SkPaint& paint, SkPathBuilder* builder, - const SkRect* cullRect, const SkMatrix& ctm) { - builder->reset(); - if (!origSrc.isFinite()) { + if (!src.isFinite()) { + dst->reset(); return false; } @@ -55,35 +43,51 @@ bool FillPathWithPaint(const SkPath& origSrc, const SkPaint& paint, SkPathBuilde } #endif - const SkPath* srcPtr = &origSrc; - SkPath pathStorage; + const SkPath* srcPtr = &src; + SkPath tmpPath; + SkPathEffect* pe = paint.getPathEffect(); - if (pe && pe->filterPath(builder, origSrc, &rec, cullRect, ctm)) { - pathStorage = builder->detach(); - srcPtr = &pathStorage; + if (pe && pe->filterPath(&tmpPath, src, &rec, cullRect, ctm)) { + srcPtr = &tmpPath; } - if (!rec.applyToPath(builder, *srcPtr)) { - *builder = *srcPtr; + + if (!rec.applyToPath(dst, *srcPtr)) { + if (srcPtr == &tmpPath) { + // If path's were copy-on-write, this trick would not be needed. + // As it is, we want to save making a deep-copy from tmpPath -> dst + // since we know we're just going to delete tmpPath when we return, + // so the swap saves that copy. + dst->swap(tmpPath); + } else { + *dst = *srcPtr; + } } - if (!builder->isFinite()) { - builder->reset(); + if (!dst->isFinite()) { + dst->reset(); + return false; } return !rec.isHairlineStyle(); } -bool FillPathWithPaint(const SkPath& src, const SkPaint& paint, SkPathBuilder* dst) { - return FillPathWithPaint(src, paint, dst, nullptr, SkMatrix::I()); -} +} // namespace skpathutils -SkPath FillPathWithPaint(const SkPath& src, const SkPaint& paint, bool* isFillPtr) { - SkPathBuilder builder; - bool isFill = FillPathWithPaint(src, paint, &builder); - if (isFillPtr) { - *isFillPtr = isFill; - } - return builder.detach(); +bool FillPathWithPaint(const SkPath& src, + const SkPaint& paint, + SkPath* dst, + const SkRect* cullRect, + SkScalar resScale) { + return skpathutils::FillPathWithPaint(src, paint, dst, cullRect, resScale); } +bool FillPathWithPaint(const SkPath& src, + const SkPaint& paint, + SkPath* dst, + const SkRect* cullRect, + const SkMatrix& ctm) { + return skpathutils::FillPathWithPaint(src, paint, dst, cullRect, ctm); +} -} // namespace skpathutils +bool FillPathWithPaint(const SkPath& src, const SkPaint& paint, SkPath* dst) { + return skpathutils::FillPathWithPaint(src, paint, dst); +} diff --git a/gfx/skia/skia/src/core/SkPath_serial.cpp b/gfx/skia/skia/src/core/SkPath_serial.cpp @@ -27,7 +27,6 @@ #include <cstddef> #include <cstdint> -#include <optional> enum SerializationOffsets { kType_SerializationShift = 28, // requires 4 bits @@ -68,20 +67,15 @@ static SerializationType extract_serializationtype(uint32_t packed) { /////////////////////////////////////////////////////////////////////////////////////////////////// size_t SkPath::writeToMemoryAsRRect(void* storage) const { + SkRect oval; SkRRect rrect; - SkPathDirection firstDir; + bool isCCW; unsigned start; - - if (auto oinfo = fPathRef->isOval()) { - rrect.setOval(oinfo->fBounds); - firstDir = oinfo->fDirection; + if (fPathRef->isOval(&oval, &isCCW, &start)) { + rrect.setOval(oval); // Convert to rrect start indices. - start = oinfo->fStartIndex * 2; - } else if (auto rinfo = fPathRef->isRRect()) { - rrect = rinfo->fRRect; - firstDir = rinfo->fDirection; - start = rinfo->fStartIndex; - } else { + start *= 2; + } else if (!fPathRef->isRRect(&rrect, &isCCW, &start)) { return 0; } @@ -91,8 +85,9 @@ size_t SkPath::writeToMemoryAsRRect(void* storage) const { return sizeNeeded; } + int firstDir = isCCW ? (int)SkPathFirstDirection::kCCW : (int)SkPathFirstDirection::kCW; int32_t packed = (fFillType << kFillType_SerializationShift) | - ((int)firstDir << kDirection_SerializationShift) | + (firstDir << kDirection_SerializationShift) | (SerializationType::kRRect << kType_SerializationShift) | kCurrent_Version; @@ -195,93 +190,70 @@ size_t SkPath::readAsRRect(const void* storage, size_t length) { return buffer.pos(); } -#ifndef SK_HIDE_PATH_EDIT_METHODS size_t SkPath::readFromMemory(const void* storage, size_t length) { - size_t bytesRead = 0; - if (auto path = SkPath::ReadFromMemory(storage, length, &bytesRead)) { - *this = path.value(); - } - return bytesRead; -} -#endif - -#define RETURN_PATH_AND_BYTES(p, b) \ - do { if (bytesRead) { *bytesRead = b; }; return p; } while (0) - -std::optional<SkPath> SkPath::ReadFromMemory(const void* storage, size_t length, size_t* bytesRead) { SkRBuffer buffer(storage, length); uint32_t packed; if (!buffer.readU32(&packed)) { - RETURN_PATH_AND_BYTES(std::nullopt, 0); + return 0; } unsigned version = extract_version(packed); const bool verbsAreForward = (version == kVerbsAreStoredForward_Version); if (!verbsAreForward && version != kJustPublicData_Version) SK_UNLIKELY { // Old/unsupported version. - RETURN_PATH_AND_BYTES(std::nullopt, 0); + return 0; } - SkPath path; - size_t tmp; switch (extract_serializationtype(packed)) { case SerializationType::kRRect: - tmp = path.readAsRRect(storage, length); - RETURN_PATH_AND_BYTES(path, tmp); + return this->readAsRRect(storage, length); case SerializationType::kGeneral: break; // fall out default: - RETURN_PATH_AND_BYTES(std::nullopt, 0); + return 0; } // To minimize the number of reads done a structure with the counts is used. struct { - uint32_t pts, cnx, vbs; + int32_t pts, cnx, vbs; } counts; if (!buffer.read(&counts, sizeof(counts))) { - RETURN_PATH_AND_BYTES(std::nullopt, 0); + return 0; } const SkPoint* points = buffer.skipCount<SkPoint>(counts.pts); const SkScalar* conics = buffer.skipCount<SkScalar>(counts.cnx); - const SkPathVerb* verbs = buffer.skipCount<SkPathVerb>(counts.vbs); + const uint8_t* verbs = buffer.skipCount<uint8_t>(counts.vbs); buffer.skipToAlign4(); if (!buffer.isValid()) { - RETURN_PATH_AND_BYTES(std::nullopt, 0); + return 0; } SkASSERT(buffer.pos() <= length); if (counts.vbs == 0) { if (counts.pts == 0 && counts.cnx == 0) { - path.setFillType(extract_filltype(packed)); - if (bytesRead) { - *bytesRead = buffer.pos(); - } - return path; + reset(); + setFillType(extract_filltype(packed)); + return buffer.pos(); } // No verbs but points and/or conic weights is a not a valid path. - RETURN_PATH_AND_BYTES(std::nullopt, 0); + return 0; } SkAutoMalloc reversedStorage; if (!verbsAreForward) SK_UNLIKELY { - SkPathVerb* tmpVerbs = (SkPathVerb*)reversedStorage.reset(counts.vbs); - for (unsigned i = 0; i < counts.vbs; ++i) { + uint8_t* tmpVerbs = (uint8_t*)reversedStorage.reset(counts.vbs); + for (int i = 0; i < counts.vbs; ++i) { tmpVerbs[i] = verbs[counts.vbs - i - 1]; } verbs = tmpVerbs; } - SkSpan<const SkPathVerb> verbSpan{verbs, counts.vbs}; - SkPathVerbAnalysis analysis = SkPathPriv::AnalyzeVerbs(verbSpan); - + SkPathVerbAnalysis analysis = SkPathPriv::AnalyzeVerbs(verbs, counts.vbs); if (!analysis.valid || analysis.points != counts.pts || analysis.weights != counts.cnx) { - RETURN_PATH_AND_BYTES(std::nullopt, 0); + return 0; } - path = SkPathPriv::MakePath(analysis, points, verbSpan, conics, - extract_filltype(packed), false); - - RETURN_PATH_AND_BYTES(path,buffer.pos()); + *this = SkPathPriv::MakePath(analysis, points, verbs, counts.vbs, conics, + extract_filltype(packed), false); + return buffer.pos(); } - -#undef RETURN_PATH_AND_BYTES diff --git a/gfx/skia/skia/src/core/SkPictureData.cpp b/gfx/skia/skia/src/core/SkPictureData.cpp @@ -471,9 +471,8 @@ void SkPictureData::parseBufferTag(SkReadBuffer& buffer, uint32_t tag, uint32_t return; } for (int i = 0; i < count; i++) { - if (auto path = buffer.readPath()) { - fPaths.push_back(std::move(*path)); - } else { + buffer.readPath(&fPaths.push_back()); + if (!buffer.isValid()) { return; } } diff --git a/gfx/skia/skia/src/core/SkPicturePlayback.cpp b/gfx/skia/skia/src/core/SkPicturePlayback.cpp @@ -289,11 +289,7 @@ void SkPicturePlayback::handleOp(SkReadBuffer* reader, sampling = reader->readSampling(); BREAK_ON_READ_ERROR(reader); } - canvas->drawAtlas(atlas, - {xform, count}, - {tex, count}, - {colors, colors ? count : 0}, - mode, sampling, cull, paint); + canvas->drawAtlas(atlas, xform, tex, colors, count, mode, sampling, cull, paint); } break; case DRAW_CLEAR: { auto c = reader->readInt(); @@ -589,7 +585,7 @@ void SkPicturePlayback::handleOp(SkReadBuffer* reader, const SkPoint* pts = (const SkPoint*)reader->skip(count, sizeof(SkPoint)); BREAK_ON_READ_ERROR(reader); - canvas->drawPoints(mode, {pts, count}, paint); + canvas->drawPoints(mode, count, pts, paint); } break; case DRAW_RECT: { const SkPaint& paint = fPictureData->requiredPaint(reader); diff --git a/gfx/skia/skia/src/core/SkPicturePriv.h b/gfx/skia/skia/src/core/SkPicturePriv.h @@ -124,8 +124,6 @@ public: // v105: Unclamped matrix color filter // v106: SaveLayer supports custom backdrop tile modes // v107: Combine SkColorShader and SkColorShader4 - // v108: Serialize stable keys of runtime effects - // v109: Extend SkWorkingColorSpaceShader to have alpha type + output control enum Version { kPictureShaderFilterParam_Version = 82, @@ -155,7 +153,6 @@ public: kSaveLayerBackdropTileMode = 106, kCombineColorShaders = 107, kSerializeStableKeys = 108, - kWorkingColorSpaceOutput = 109, // Only SKPs within the min/current picture version range (inclusive) can be read. // @@ -180,7 +177,7 @@ public: // // Contact the Infra Gardener if the above steps do not work for you. kMin_Version = kPictureShaderFilterParam_Version, - kCurrent_Version = kWorkingColorSpaceOutput + kCurrent_Version = kSerializeStableKeys }; }; diff --git a/gfx/skia/skia/src/core/SkPictureRecorder.cpp b/gfx/skia/skia/src/core/SkPictureRecorder.cpp @@ -14,10 +14,10 @@ #include "include/private/base/SkTemplates.h" #include "src/core/SkBigPicture.h" #include "src/core/SkRecord.h" -#include "src/core/SkRecordCanvas.h" #include "src/core/SkRecordDraw.h" #include "src/core/SkRecordOpts.h" #include "src/core/SkRecordedDrawable.h" +#include "src/core/SkRecorder.h" #include <cstddef> #include <memory> @@ -27,7 +27,7 @@ using namespace skia_private; SkPictureRecorder::SkPictureRecorder() { fActivelyRecording = false; - fRecorder = std::make_unique<SkRecordCanvas>(nullptr, SkRect::MakeEmpty()); + fRecorder = std::make_unique<SkRecorder>(nullptr, SkRect::MakeEmpty()); } SkPictureRecorder::~SkPictureRecorder() {} diff --git a/gfx/skia/skia/src/core/SkPixmapDraw.cpp b/gfx/skia/skia/src/core/SkPixmapDraw.cpp @@ -63,8 +63,7 @@ bool SkPixmap::scalePixels(const SkPixmap& actualDst, const SkSamplingOptions& s } bitmap.setImmutable(); // Don't copy when we create an image. - SkMatrix scale = SkMatrix::RectToRectOrIdentity(SkRect::Make(src.bounds()), - SkRect::Make(dst.bounds())); + SkMatrix scale = SkMatrix::RectToRect(SkRect::Make(src.bounds()), SkRect::Make(dst.bounds())); sk_sp<SkShader> shader = SkImageShader::Make(bitmap.asImage(), SkTileMode::kClamp, diff --git a/gfx/skia/skia/src/core/SkRRect.cpp b/gfx/skia/skia/src/core/SkRRect.cpp @@ -16,8 +16,6 @@ #include "include/private/base/SkDebug.h" #include "include/private/base/SkFloatingPoint.h" #include "src/base/SkBuffer.h" -#include "src/core/SkPathPriv.h" -#include "src/core/SkPathRawShapes.h" #include "src/core/SkRRectPriv.h" #include "src/core/SkRectPriv.h" #include "src/core/SkScaleToSides.h" @@ -435,45 +433,7 @@ void SkRRect::computeType() { } } -std::optional<SkRRect> SkRRect::transform(const SkMatrix& matrix) const { -// TODO(b/441005851): Resolve Android RenderEngine's shader prewarming regressions before removing. -#ifdef SK_SUPPORT_LEGACY_RRECT_TRANSFORM - SkRRect newrr; - if (this->transform(matrix, &newrr)) { - return newrr; - } - return {}; -#else - if (matrix.isIdentity()) { - return *this; - } - - if (!matrix.preservesAxisAlignment()) { - return {}; - } - - const SkRect newRect = matrix.mapRect(fRect); - if (!newRect.isFinite()) { - return {}; - } - - switch (this->getType()) { - case kEmpty_Type: return MakeEmpty(); - case kRect_Type: return MakeRect(newRect); - case kOval_Type: return MakeOval(newRect); - default: - break; - } - - SkPathRawShapes::RRect raw(*this); - matrix.mapPoints(raw.fStorage); - return SkPathPriv::DeduceRRectFromContour(newRect, raw.fPoints, raw.fVerbs); -#endif -} - bool SkRRect::transform(const SkMatrix& matrix, SkRRect* dst) const { -// TODO(b/441005851): Resolve Android RenderEngine's shader prewarming regressions before removing. -#ifdef SK_SUPPORT_LEGACY_RRECT_TRANSFORM if (nullptr == dst) { return false; } @@ -602,15 +562,6 @@ bool SkRRect::transform(const SkMatrix& matrix, SkRRect* dst) const { SkASSERT(dst->isValid()); return true; -#else - if (auto rr = this->transform(matrix)) { - if (dst) { - *dst = *rr; - } - return true; - } - return false; -#endif } /////////////////////////////////////////////////////////////////////////////// diff --git a/gfx/skia/skia/src/core/SkRSXform.cpp b/gfx/skia/skia/src/core/SkRSXform.cpp @@ -14,7 +14,7 @@ void SkRSXform::toQuad(SkScalar width, SkScalar height, SkPoint quad[4]) const { quad[2].set(width, height); quad[3].set(0, height); SkMatrix m; - m.setRSXform(*this).mapPoints({quad, 4}); + m.setRSXform(*this).mapPoints(quad, quad, 4); #else const SkScalar m00 = fSCos; const SkScalar m01 = -fSSin; diff --git a/gfx/skia/skia/src/core/SkRasterClip.cpp b/gfx/skia/skia/src/core/SkRasterClip.cpp @@ -185,14 +185,15 @@ bool SkRasterClip::op(const SkRRect& rrect, const SkMatrix& matrix, SkClipOp op, bool SkRasterClip::op(const SkPath& path, const SkMatrix& matrix, SkClipOp op, bool doAA) { AUTO_RASTERCLIP_VALIDATE(*this); - SkPath devPath = path.makeTransform(matrix); + SkPath devPath; + path.transform(matrix, &devPath); // Since op is either intersect or difference, the clip is always shrinking; that means we can // always use our current bounds as the limiting factor for region/aaclip operations. if (this->isRect() && op == SkClipOp::kIntersect) { // However, in the relatively common case of intersecting a new path with a rectangular // clip, it's faster to convert the path into a region/aa-mask in place than evaluate the - // actual intersection. See skbug.com/40043482 + // actual intersection. See skbug.com/12398 if (doAA && fIsBW) { this->convertToAA(); } diff --git a/gfx/skia/skia/src/core/SkRasterPipeline.cpp b/gfx/skia/skia/src/core/SkRasterPipeline.cpp @@ -541,7 +541,7 @@ void SkRasterPipeline::appendStore(SkColorType ct, const SkRasterPipelineContext void SkRasterPipeline::appendTransferFunction(const skcms_TransferFunction& tf) { void* ctx = const_cast<void*>(static_cast<const void*>(&tf)); switch (skcms_TransferFunction_getType(&tf)) { - default: SkASSERT(false); break; + case skcms_TFType_Invalid: SkASSERT(false); break; case skcms_TFType_sRGBish: if (tf.a == 1 && tf.b == 0 && tf.c == 0 && tf.d == 0 && tf.e == 0 && tf.f == 0) { diff --git a/gfx/skia/skia/src/core/SkRasterPipelineBlitter.cpp b/gfx/skia/skia/src/core/SkRasterPipelineBlitter.cpp @@ -28,7 +28,6 @@ #include "src/core/SkBlitter.h" #include "src/core/SkColorSpacePriv.h" #include "src/core/SkColorSpaceXformSteps.h" -#include "src/core/SkConvertPixels.h" #include "src/core/SkCoreBlitters.h" #include "src/core/SkEffectPriv.h" #include "src/core/SkMask.h" @@ -51,24 +50,6 @@ class SkColorSpace; class SkShader; -static bool can_direct_blit(const SkPaint& paint) { - if (paint.getShader() || paint.getColorFilter() || paint.isDither()) { - return false; - } - if (auto mode = paint.asBlendMode()) { - switch (mode.value()) { - case SkBlendMode::kClear: [[fallthrough]]; - case SkBlendMode::kSrc: - return true; - case SkBlendMode::kSrcOver: - return paint.getAlphaf() == 1; - default: - break; - } - } - return false; -} - class SkRasterPipelineBlitter final : public SkBlitter { public: // This is our common entrypoint for creating the blitter once we've sorted out shaders. @@ -82,14 +63,11 @@ public: const SkShader* clipShader); SkRasterPipelineBlitter(SkPixmap dst, - const SkPaint& paint, SkArenaAlloc* alloc) : fDst(std::move(dst)) , fAlloc(alloc) , fColorPipeline(alloc) , fBlendPipeline(alloc) - , fCanDirectBlit(can_direct_blit(paint)) - , fDirectBlitPaintColor(paint.getColor4f()) {} void blitH (int x, int y, int w) override; @@ -99,7 +77,6 @@ public: void blitMask (const SkMask&, const SkIRect& clip) override; void blitRect (int x, int y, int width, int height) override; void blitV (int x, int y, int height, SkAlpha alpha) override; - std::optional<DirectBlit> canDirectBlit() override; private: void appendLoadDst (SkRasterPipeline*) const; @@ -118,10 +95,6 @@ private: // set to pipeline storage (for alpha) if we have a clipShader void* fClipShaderBuffer = nullptr; // "native" : float or U16 - bool fCanDirectBlit; - const SkColor4f fDirectBlitPaintColor; - std::optional<uint64_t> fDirectBlitValue; - SkRasterPipelineContexts::MemoryCtx fDstPtr = {nullptr,0}, // Always points to the top-left of fDst. fMaskPtr = {nullptr,0}; // Updated each call to blitMask(). @@ -161,8 +134,7 @@ static bool create_pipeline_for_blitter(const SkPixmap& dst, SkRasterPipeline* shaderPipeline, SkColor4f* dstPaintColor, bool* isOpaqueOut, - bool* isConstantOut, - const SkRect& devBounds) { + bool* isConstantOut) { *dstPaintColor = paint_color_to_dst(paint, dst); auto shader = as_SB(paint.getShader()); @@ -180,8 +152,7 @@ static bool create_pipeline_for_blitter(const SkPixmap& dst, *isOpaqueOut = shader->isOpaque() && dstPaintColor->fA == 1.0f; *isConstantOut = shader->isConstant(); if (shader->appendRootStages( - SkStageRec{shaderPipeline, alloc, dstCT, dstCS, *dstPaintColor, props, devBounds}, - ctm)) { + SkStageRec{shaderPipeline, alloc, dstCT, dstCS, *dstPaintColor, props}, ctm)) { if (dstPaintColor->fA != 1.0f) { shaderPipeline->append(SkRasterPipelineOp::scale_1_float, alloc->make<float>(dstPaintColor->fA)); @@ -197,8 +168,7 @@ SkBlitter* SkCreateRasterPipelineBlitter(const SkPixmap& dst, const SkMatrix& ctm, SkArenaAlloc* alloc, sk_sp<SkShader> clipShader, - const SkSurfaceProps& props, - const SkRect& devBounds) { + const SkSurfaceProps& props) { SkRasterPipeline_<256> shaderPipeline; SkColor4f dstPaintColor; bool is_opaque, is_constant; @@ -210,8 +180,7 @@ SkBlitter* SkCreateRasterPipelineBlitter(const SkPixmap& dst, &shaderPipeline, &dstPaintColor, &is_opaque, - &is_constant, - devBounds)) { + &is_constant)) { return nullptr; } @@ -245,8 +214,7 @@ SkBlitter* CreateBlitter(const SkPixmap& dst, &shaderPipeline, &dstPaintColor, &is_opaque, - &is_constant, - SkRect::MakeEmpty())) { + &is_constant)) { return nullptr; } @@ -306,7 +274,7 @@ SkBlitter* SkRasterPipelineBlitter::Create(const SkPixmap& dst, bool is_opaque, bool is_constant, const SkShader* clipShader) { - auto blitter = alloc->make<SkRasterPipelineBlitter>(dst, paint, alloc); + auto blitter = alloc->make<SkRasterPipelineBlitter>(dst, alloc); // Our job in this factory is to fill out the blitter's color and blend pipelines. // The color pipeline is the common front of the full blit pipeline. The blend pipeline is just @@ -324,8 +292,7 @@ SkBlitter* SkRasterPipelineBlitter::Create(const SkPixmap& dst, SkColorType clipCT = kRGBA_8888_SkColorType; SkColorSpace* clipCS = nullptr; SkSurfaceProps props{}; // default OK; clipShader doesn't render text - SkStageRec rec = { - clipP, alloc, clipCT, clipCS, SkColors::kBlack, props, SkRect::MakeEmpty()}; + SkStageRec rec = {clipP, alloc, clipCT, clipCS, SkColors::kBlack, props}; if (as_SB(clipShader)->appendRootStages(rec, SkMatrix::I())) { struct Storage { // large enough for highp (float) or lowp(U16) @@ -346,13 +313,8 @@ SkBlitter* SkRasterPipelineBlitter::Create(const SkPixmap& dst, // If there's a color filter it comes next. if (auto colorFilter = paint.getColorFilter()) { SkSurfaceProps props{}; // default OK; colorFilter doesn't render text - SkStageRec rec = {colorPipeline, - alloc, - dst.colorType(), - dst.colorSpace(), - dstPaintColor, - props, - SkRect::MakeEmpty()}; + SkStageRec rec = { + colorPipeline, alloc, dst.colorType(), dst.colorSpace(), dstPaintColor, props}; if (!as_CFB(colorFilter)->appendStages(rec, is_opaque)) { return nullptr; } @@ -475,13 +437,8 @@ SkBlitter* SkRasterPipelineBlitter::Create(const SkPixmap& dst, { SkSurfaceProps props{}; // default OK; blender doesn't render text - SkStageRec rec = {blendPipeline, - alloc, - dst.colorType(), - dst.colorSpace(), - dstPaintColor, - props, - SkRect::MakeEmpty()}; + SkStageRec rec = { + blendPipeline, alloc, dst.colorType(), dst.colorSpace(), dstPaintColor, props}; if (!as_BB(blender)->appendStages(rec)) { return nullptr; } @@ -732,34 +689,3 @@ void SkRasterPipelineBlitter::blitMask(const SkMask& mask, const SkIRect& clip) SkASSERT(blitter); (*blitter)(clip.left(),clip.top(), clip.width(),clip.height()); } - -std::optional<SkBlitter::DirectBlit> SkRasterPipelineBlitter::canDirectBlit() { - if (fCanDirectBlit) { - if (!fDirectBlitValue.has_value()) { - // want maximum alignment (8) but legal punning for smaller int types - union { - uint64_t u8[1]; - uint32_t u4[2]; - uint16_t u2[4]; - uint8_t u1[8]; - } dstBuffer; - auto dst = SkImageInfo::Make(1, 1, fDst.info().colorType(), fDst.info().alphaType()); - auto src = SkImageInfo::Make(1, 1, kRGBA_F32_SkColorType, kUnpremul_SkAlphaType); - if (!SkConvertPixels(dst, &dstBuffer, sizeof(dstBuffer), - src, &fDirectBlitPaintColor, sizeof(fDirectBlitPaintColor))) { - goto FAIL; - } - switch (dst.bytesPerPixel()) { - case 1: fDirectBlitValue = dstBuffer.u1[0]; break; - case 2: fDirectBlitValue = dstBuffer.u2[0]; break; - case 4: fDirectBlitValue = dstBuffer.u4[0]; break; - case 8: fDirectBlitValue = dstBuffer.u8[0]; break; - default: goto FAIL; - } - } - return {{fDst, fDirectBlitValue.value()}}; - } -FAIL: - fCanDirectBlit = false; - return {}; -} diff --git a/gfx/skia/skia/src/core/SkRasterPipelineOpContexts.h b/gfx/skia/skia/src/core/SkRasterPipelineOpContexts.h @@ -61,10 +61,10 @@ struct MemoryCtxInfo { }; struct MemoryCtxPatch { - std::byte scratch[kMaxScratchPerPatch]; - MemoryCtxInfo info; + void* backup; // Remembers context->pixels so we can restore it + std::byte scratch[kMaxScratchPerPatch]; }; struct GatherCtx { diff --git a/gfx/skia/skia/src/core/SkRasterPipelineOpList.h b/gfx/skia/skia/src/core/SkRasterPipelineOpList.h @@ -182,7 +182,7 @@ M(colorburn) M(colordodge) M(softlight) \ M(hue) M(saturation) M(color) M(luminosity) \ M(matrix_3x3) M(matrix_3x4) M(matrix_4x5) M(matrix_4x3) \ - M(parametric) M(gamma_) M(PQish) M(HLGish) M(HLGinvish) M(ootf) \ + M(parametric) M(gamma_) M(PQish) M(HLGish) M(HLGinvish) \ M(rgb_to_hsl) M(hsl_to_rgb) \ M(css_lab_to_xyz) M(css_oklab_to_linear_srgb) \ M(css_oklab_gamut_map_to_linear_srgb) \ @@ -192,7 +192,6 @@ M(mirror_x) M(repeat_x) \ M(mirror_y) M(repeat_y) \ M(negate_x) \ - M(bilerp_clamp_8888_force_highp) \ M(bicubic_clamp_8888) \ M(bilinear_setup) \ M(bilinear_nx) M(bilinear_px) M(bilinear_ny) M(bilinear_py) \ diff --git a/gfx/skia/skia/src/core/SkReadBuffer.cpp b/gfx/skia/skia/src/core/SkReadBuffer.cpp @@ -264,23 +264,15 @@ void SkReadBuffer::readRegion(SkRegion* region) { (void)this->skip(size); } -std::optional<SkPath> SkReadBuffer::readPath() { - if (fError) { - return {}; - } - +void SkReadBuffer::readPath(SkPath* path) { size_t size = 0; - auto path = SkPath::ReadFromMemory(fCurr, this->available(), &size); - - // todo: consider moving this 4-byte-alignment check elsewhere - // i.e. why is that a burden on SkPath? - // why don't we just skipAlign4() or something? - (void)this->validate(SkAlign4(size) == size && path.has_value()); - - // we move forward, regardless of if the path succeeded + if (!fError) { + size = path->readFromMemory(fCurr, this->available()); + if (!this->validate((SkAlign4(size) == size) && (0 != size))) { + path->reset(); + } + } (void)this->skip(size); - - return path; } bool SkReadBuffer::readArray(void* value, size_t size, size_t elementSize) { @@ -293,24 +285,24 @@ bool SkReadBuffer::readByteArray(void* value, size_t size) { return this->readArray(value, size, sizeof(uint8_t)); } -bool SkReadBuffer::readColorArray(SkSpan<SkColor> colors) { - return this->readArray(colors.data(), colors.size(), sizeof(SkColor)); +bool SkReadBuffer::readColorArray(SkColor* colors, size_t size) { + return this->readArray(colors, size, sizeof(SkColor)); } -bool SkReadBuffer::readColor4fArray(SkSpan<SkColor4f> colors) { - return this->readArray(colors.data(), colors.size(), sizeof(SkColor4f)); +bool SkReadBuffer::readColor4fArray(SkColor4f* colors, size_t size) { + return this->readArray(colors, size, sizeof(SkColor4f)); } -bool SkReadBuffer::readIntArray(SkSpan<int32_t> values) { - return this->readArray(values.data(), values.size(), sizeof(int32_t)); +bool SkReadBuffer::readIntArray(int32_t* values, size_t size) { + return this->readArray(values, size, sizeof(int32_t)); } -bool SkReadBuffer::readPointArray(SkSpan<SkPoint> points) { - return this->readArray(points.data(), points.size(), sizeof(SkPoint)); +bool SkReadBuffer::readPointArray(SkPoint* points, size_t size) { + return this->readArray(points, size, sizeof(SkPoint)); } -bool SkReadBuffer::readScalarArray(SkSpan<SkScalar> values) { - return this->readArray(values.data(), values.size(), sizeof(SkScalar)); +bool SkReadBuffer::readScalarArray(SkScalar* values, size_t size) { + return this->readArray(values, size, sizeof(SkScalar)); } const void* SkReadBuffer::skipByteArray(size_t* size) { @@ -404,7 +396,7 @@ static sk_sp<SkImage> add_mipmaps(sk_sp<SkImage> img, sk_sp<SkData> data, if (!buffer.isValid()) { return img; } - sk_sp<SkImage> raster = img->makeRasterImage(nullptr); + sk_sp<SkImage> raster = img->makeRasterImage(); if (!raster) { return img; } @@ -438,7 +430,7 @@ sk_sp<SkImage> SkReadBuffer::readImage() { SkIRect subset; this->readIRect(&subset); if (image) { - image = image->makeSubset(nullptr, subset, {}); + image = image->makeSubset(nullptr, subset); } } diff --git a/gfx/skia/skia/src/core/SkReadBuffer.h b/gfx/skia/skia/src/core/SkReadBuffer.h @@ -21,7 +21,6 @@ #include "include/core/SkScalar.h" #include "include/core/SkSerialProcs.h" #include "include/core/SkShader.h" -#include "include/core/SkSpan.h" #include "include/private/base/SkAlign.h" #include "include/private/base/SkAssert.h" #include "src/core/SkBlenderBase.h" @@ -36,7 +35,6 @@ #include <cstddef> #include <cstdint> -#include <optional> class SkBlender; class SkData; @@ -124,7 +122,7 @@ public: void readRRect(SkRRect* rrect); void readRegion(SkRegion* region); - std::optional<SkPath> readPath(); + void readPath(SkPath* path); SkPaint readPaint() { return SkPaintPriv::Unflatten(*this); @@ -147,11 +145,11 @@ public: // binary data and arrays bool readByteArray(void* value, size_t size); - bool readColorArray(SkSpan<SkColor>); - bool readColor4fArray(SkSpan<SkColor4f>); - bool readIntArray(SkSpan<int32_t>); - bool readPointArray(SkSpan<SkPoint>); - bool readScalarArray(SkSpan<SkScalar>); + bool readColorArray(SkColor* colors, size_t size); + bool readColor4fArray(SkColor4f* colors, size_t size); + bool readIntArray(int32_t* values, size_t size); + bool readPointArray(SkPoint* points, size_t size); + bool readScalarArray(SkScalar* values, size_t size); const void* skipByteArray(size_t* size); diff --git a/gfx/skia/skia/src/core/SkRecord.h b/gfx/skia/skia/src/core/SkRecord.h @@ -21,9 +21,8 @@ // These future uses may include: replay, optimization, serialization, or combinations of those. // // Though an enterprising user may find calling alloc(), append(), visit(), and mutate() enough to -// work with SkRecord, you probably want to look at SkRecordCanvas which presents an SkCanvas -// interface for creating an SkRecord, and SkRecordDraw which plays an SkRecord back into another -// SkCanvas. +// work with SkRecord, you probably want to look at SkRecorder which presents an SkCanvas interface +// for creating an SkRecord, and SkRecordDraw which plays an SkRecord back into another SkCanvas. // // SkRecord often looks like it's compatible with any type T, but really it's compatible with any // type T which has a static const SkRecords::Type kType. That is to say, SkRecord is compatible diff --git a/gfx/skia/skia/src/core/SkRecordCanvas.cpp b/gfx/skia/skia/src/core/SkRecordCanvas.cpp @@ -1,446 +0,0 @@ -/* - * Copyright 2014 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "src/core/SkRecordCanvas.h" - -#include "include/core/SkCanvas.h" -#include "include/core/SkData.h" -#include "include/core/SkDrawable.h" -#include "include/core/SkImage.h" -#include "include/core/SkImageFilter.h" -#include "include/core/SkImageInfo.h" -#include "include/core/SkMatrix.h" -#include "include/core/SkPaint.h" -#include "include/core/SkPicture.h" -#include "include/core/SkPoint.h" -#include "include/core/SkRSXform.h" -#include "include/core/SkRect.h" -#include "include/core/SkShader.h" -#include "include/core/SkString.h" -#include "include/core/SkSurface.h" -#include "include/core/SkTextBlob.h" -#include "include/core/SkVertices.h" -#include "include/private/base/SkFloatingPoint.h" -#include "include/private/base/SkTemplates.h" -#include "include/private/base/SkTo.h" -#include "include/private/chromium/Slug.h" -#include "src/core/SkBigPicture.h" -#include "src/core/SkCanvasPriv.h" -#include "src/core/SkRecord.h" -#include "src/core/SkRecords.h" -#include "src/text/GlyphRun.h" -#include "src/utils/SkPatchUtils.h" - -#include <cstdint> -#include <cstring> -#include <memory> -#include <new> - -class SkBlender; -class SkMesh; -class SkPath; -class SkRRect; -class SkRegion; -class SkSurfaceProps; -enum class SkBlendMode; -enum class SkClipOp; -struct SkDrawShadowRec; - -using namespace skia_private; - -SkDrawableList::~SkDrawableList() { - for (SkDrawable* p : fArray) { - p->unref(); - } - fArray.reset(); -} - -SkBigPicture::SnapshotArray* SkDrawableList::newDrawableSnapshot() { - const int count = fArray.size(); - if (0 == count) { - return nullptr; - } - AutoTMalloc<const SkPicture*> pics(count); - for (int i = 0; i < count; ++i) { - pics[i] = fArray[i]->makePictureSnapshot().release(); - } - return new SkBigPicture::SnapshotArray(pics.release(), count); -} - -void SkDrawableList::append(SkDrawable* drawable) { *fArray.append() = SkRef(drawable); } - -/////////////////////////////////////////////////////////////////////////////////////////////// - -static SkIRect safe_picture_bounds(const SkRect& bounds) { - SkIRect picBounds = bounds.roundOut(); - // roundOut() saturates the float edges to +/-SK_MaxS32FitsInFloat (~2billion), but this is - // large enough that width/height calculations will overflow, leading to negative dimensions. - static constexpr int32_t kSafeEdge = SK_MaxS32FitsInFloat / 2 - 1; - static constexpr SkIRect kSafeBounds = {-kSafeEdge, -kSafeEdge, kSafeEdge, kSafeEdge}; - static_assert((kSafeBounds.fRight - kSafeBounds.fLeft) >= 0 && - (kSafeBounds.fBottom - kSafeBounds.fTop) >= 0); - if (!picBounds.intersect(kSafeBounds)) { - picBounds.setEmpty(); - } - return picBounds; -} - -SkRecordCanvas::SkRecordCanvas(SkRecord* record, int width, int height) - : SkCanvasVirtualEnforcer<SkNoDrawCanvas>(width, height) - , fApproxBytesUsedBySubPictures(0) - , fRecord(record) { - SkASSERT(this->imageInfo().width() >= 0 && this->imageInfo().height() >= 0); -} - -SkRecordCanvas::SkRecordCanvas(SkRecord* record, const SkRect& bounds) - : SkCanvasVirtualEnforcer<SkNoDrawCanvas>(safe_picture_bounds(bounds)) - , fApproxBytesUsedBySubPictures(0) - , fRecord(record) { - SkASSERT(this->imageInfo().width() >= 0 && this->imageInfo().height() >= 0); -} - -void SkRecordCanvas::reset(SkRecord* record, const SkRect& bounds) { - this->forgetRecord(); - fRecord = record; - this->resetCanvas(safe_picture_bounds(bounds)); - SkASSERT(this->imageInfo().width() >= 0 && this->imageInfo().height() >= 0); -} - -void SkRecordCanvas::forgetRecord() { - fDrawableList.reset(nullptr); - fApproxBytesUsedBySubPictures = 0; - fRecord = nullptr; -} - -// To make appending to fRecord a little less verbose. -template <typename T, typename... Args> void SkRecordCanvas::append(Args&&... args) { - new (fRecord->append<T>()) T{std::forward<Args>(args)...}; -} - -// For methods which must call back into SkNoDrawCanvas. -#define INHERITED(method, ...) this->SkNoDrawCanvas::method(__VA_ARGS__) - -// Use copy() only for optional arguments, to be copied if present or skipped if not. -// (For most types we just pass by value and let copy constructors do their thing.) -template <typename T> T* SkRecordCanvas::copy(const T* src) { - if (nullptr == src) { - return nullptr; - } - return new (fRecord->alloc<T>()) T(*src); -} - -// This copy() is for arrays. -// It will work with POD or non-POD, though currently we only use it for POD. -template <typename T> T* SkRecordCanvas::copy(const T src[], size_t count) { - if (nullptr == src) { - return nullptr; - } - T* dst = fRecord->alloc<T>(count); - for (size_t i = 0; i < count; i++) { - new (dst + i) T(src[i]); - } - return dst; -} - -// Specialization for copying strings, using memcpy. -// This measured around 2x faster for copying code points, -// but I found no corresponding speedup for other arrays. -template <> char* SkRecordCanvas::copy(const char src[], size_t count) { - if (nullptr == src) { - return nullptr; - } - char* dst = fRecord->alloc<char>(count); - memcpy(dst, src, count); - return dst; -} - -// As above, assuming and copying a terminating \0. -template <> char* SkRecordCanvas::copy(const char* src) { return this->copy(src, strlen(src) + 1); } - -void SkRecordCanvas::onDrawPaint(const SkPaint& paint) { - this->append<SkRecords::DrawPaint>(paint); -} - -void SkRecordCanvas::onDrawBehind(const SkPaint& paint) { - this->append<SkRecords::DrawBehind>(paint); -} - -void SkRecordCanvas::onDrawPoints(PointMode mode, - size_t count, - const SkPoint pts[], - const SkPaint& paint) { - this->append<SkRecords::DrawPoints>(paint, mode, SkToUInt(count), this->copy(pts, count)); -} - -void SkRecordCanvas::onDrawRect(const SkRect& rect, const SkPaint& paint) { - this->append<SkRecords::DrawRect>(paint, rect); -} - -void SkRecordCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) { - this->append<SkRecords::DrawRegion>(paint, region); -} - -void SkRecordCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) { - this->append<SkRecords::DrawOval>(paint, oval); -} - -void SkRecordCanvas::onDrawArc(const SkRect& oval, - SkScalar startAngle, - SkScalar sweepAngle, - bool useCenter, - const SkPaint& paint) { - this->append<SkRecords::DrawArc>(paint, oval, startAngle, sweepAngle, useCenter); -} - -void SkRecordCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) { - this->append<SkRecords::DrawRRect>(paint, rrect); -} - -void SkRecordCanvas::onDrawDRRect(const SkRRect& outer, - const SkRRect& inner, - const SkPaint& paint) { - this->append<SkRecords::DrawDRRect>(paint, outer, inner); -} - -void SkRecordCanvas::onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) { - if (!fDrawableList) { - fDrawableList = std::make_unique<SkDrawableList>(); - } - fDrawableList->append(drawable); - this->append<SkRecords::DrawDrawable>( - this->copy(matrix), drawable->getBounds(), fDrawableList->count() - 1); -} - -void SkRecordCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) { - this->append<SkRecords::DrawPath>(paint, path); -} - -void SkRecordCanvas::onDrawImage2(const SkImage* image, - SkScalar x, - SkScalar y, - const SkSamplingOptions& sampling, - const SkPaint* paint) { - this->append<SkRecords::DrawImage>(this->copy(paint), sk_ref_sp(image), x, y, sampling); -} - -void SkRecordCanvas::onDrawImageRect2(const SkImage* image, - const SkRect& src, - const SkRect& dst, - const SkSamplingOptions& sampling, - const SkPaint* paint, - SrcRectConstraint constraint) { - this->append<SkRecords::DrawImageRect>( - this->copy(paint), sk_ref_sp(image), src, dst, sampling, constraint); -} - -void SkRecordCanvas::onDrawImageLattice2(const SkImage* image, - const Lattice& lattice, - const SkRect& dst, - SkFilterMode filter, - const SkPaint* paint) { - int flagCount = lattice.fRectTypes ? (lattice.fXCount + 1) * (lattice.fYCount + 1) : 0; - SkASSERT(lattice.fBounds); - this->append<SkRecords::DrawImageLattice>(this->copy(paint), - sk_ref_sp(image), - lattice.fXCount, - this->copy(lattice.fXDivs, lattice.fXCount), - lattice.fYCount, - this->copy(lattice.fYDivs, lattice.fYCount), - flagCount, - this->copy(lattice.fRectTypes, flagCount), - this->copy(lattice.fColors, flagCount), - *lattice.fBounds, - dst, - filter); -} - -void SkRecordCanvas::onDrawTextBlob(const SkTextBlob* blob, - SkScalar x, - SkScalar y, - const SkPaint& paint) { - this->append<SkRecords::DrawTextBlob>(paint, sk_ref_sp(blob), x, y); -} - -void SkRecordCanvas::onDrawSlug(const sktext::gpu::Slug* slug, const SkPaint& paint) { - this->append<SkRecords::DrawSlug>(paint, sk_ref_sp(slug)); -} - -void SkRecordCanvas::onDrawGlyphRunList(const sktext::GlyphRunList& glyphRunList, - const SkPaint& paint) { - sk_sp<SkTextBlob> blob = sk_ref_sp(glyphRunList.blob()); - if (glyphRunList.blob() == nullptr) { - blob = glyphRunList.makeBlob(); - } - - this->onDrawTextBlob(blob.get(), glyphRunList.origin().x(), glyphRunList.origin().y(), paint); -} - -void SkRecordCanvas::onDrawPicture(const SkPicture* pic, - const SkMatrix* matrix, - const SkPaint* paint) { - fApproxBytesUsedBySubPictures += pic->approximateBytesUsed(); - this->append<SkRecords::DrawPicture>( - this->copy(paint), sk_ref_sp(pic), matrix ? *matrix : SkMatrix::I()); -} - -void SkRecordCanvas::onDrawVerticesObject(const SkVertices* vertices, - SkBlendMode bmode, - const SkPaint& paint) { - this->append<SkRecords::DrawVertices>( - paint, sk_ref_sp(const_cast<SkVertices*>(vertices)), bmode); -} - -void SkRecordCanvas::onDrawMesh(const SkMesh& mesh, - sk_sp<SkBlender> blender, - const SkPaint& paint) { - this->append<SkRecords::DrawMesh>(paint, mesh, std::move(blender)); -} - -void SkRecordCanvas::onDrawPatch(const SkPoint cubics[12], - const SkColor colors[4], - const SkPoint texCoords[4], - SkBlendMode bmode, - const SkPaint& paint) { - this->append<SkRecords::DrawPatch>( - paint, - cubics ? this->copy(cubics, SkPatchUtils::kNumCtrlPts) : nullptr, - colors ? this->copy(colors, SkPatchUtils::kNumCorners) : nullptr, - texCoords ? this->copy(texCoords, SkPatchUtils::kNumCorners) : nullptr, - bmode); -} - -void SkRecordCanvas::onDrawAtlas2(const SkImage* atlas, - const SkRSXform xform[], - const SkRect tex[], - const SkColor colors[], - int count, - SkBlendMode mode, - const SkSamplingOptions& sampling, - const SkRect* cull, - const SkPaint* paint) { - this->append<SkRecords::DrawAtlas>(this->copy(paint), - sk_ref_sp(atlas), - this->copy(xform, count), - this->copy(tex, count), - this->copy(colors, count), - count, - mode, - sampling, - this->copy(cull)); -} - -void SkRecordCanvas::onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) { - this->append<SkRecords::DrawShadowRec>(path, rec); -} - -void SkRecordCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) { - this->append<SkRecords::DrawAnnotation>(rect, SkString(key), sk_ref_sp(value)); -} - -void SkRecordCanvas::onDrawEdgeAAQuad(const SkRect& rect, - const SkPoint clip[4], - QuadAAFlags aa, - const SkColor4f& color, - SkBlendMode mode) { - this->append<SkRecords::DrawEdgeAAQuad>(rect, this->copy(clip, 4), aa, color, mode); -} - -void SkRecordCanvas::onDrawEdgeAAImageSet2(const ImageSetEntry set[], - int count, - const SkPoint dstClips[], - const SkMatrix preViewMatrices[], - const SkSamplingOptions& sampling, - const SkPaint* paint, - SrcRectConstraint constraint) { - int totalDstClipCount, totalMatrixCount; - SkCanvasPriv::GetDstClipAndMatrixCounts(set, count, &totalDstClipCount, &totalMatrixCount); - - AutoTArray<ImageSetEntry> setCopy(count); - for (int i = 0; i < count; ++i) { - setCopy[i] = set[i]; - } - - this->append<SkRecords::DrawEdgeAAImageSet>(this->copy(paint), - std::move(setCopy), - count, - this->copy(dstClips, totalDstClipCount), - this->copy(preViewMatrices, totalMatrixCount), - sampling, - constraint); -} - -void SkRecordCanvas::willSave() { this->append<SkRecords::Save>(); } - -SkCanvas::SaveLayerStrategy SkRecordCanvas::getSaveLayerStrategy(const SaveLayerRec& rec) { - AutoTArray<sk_sp<SkImageFilter>> filters(rec.fFilters.size()); - for (size_t i = 0; i < rec.fFilters.size(); ++i) { - filters[i] = rec.fFilters[i]; - } - - this->append<SkRecords::SaveLayer>(this->copy(rec.fBounds), - this->copy(rec.fPaint), - sk_ref_sp(rec.fBackdrop), - rec.fSaveLayerFlags, - SkCanvasPriv::GetBackdropScaleFactor(rec), - rec.fBackdropTileMode, - std::move(filters)); - return SkCanvas::kNoLayer_SaveLayerStrategy; -} - -bool SkRecordCanvas::onDoSaveBehind(const SkRect* subset) { - this->append<SkRecords::SaveBehind>(this->copy(subset)); - return false; -} - -void SkRecordCanvas::didRestore() { this->append<SkRecords::Restore>(this->getTotalMatrix()); } - -void SkRecordCanvas::didConcat44(const SkM44& m) { this->append<SkRecords::Concat44>(m); } - -void SkRecordCanvas::didSetM44(const SkM44& m) { this->append<SkRecords::SetM44>(m); } - -void SkRecordCanvas::didScale(SkScalar sx, SkScalar sy) { this->append<SkRecords::Scale>(sx, sy); } - -void SkRecordCanvas::didTranslate(SkScalar dx, SkScalar dy) { - this->append<SkRecords::Translate>(dx, dy); -} - -void SkRecordCanvas::onClipRect(const SkRect& rect, SkClipOp op, ClipEdgeStyle edgeStyle) { - INHERITED(onClipRect, rect, op, edgeStyle); - SkRecords::ClipOpAndAA opAA(op, kSoft_ClipEdgeStyle == edgeStyle); - this->append<SkRecords::ClipRect>(rect, opAA); -} - -void SkRecordCanvas::onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle edgeStyle) { - INHERITED(onClipRRect, rrect, op, edgeStyle); - SkRecords::ClipOpAndAA opAA(op, kSoft_ClipEdgeStyle == edgeStyle); - this->append<SkRecords::ClipRRect>(rrect, opAA); -} - -void SkRecordCanvas::onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle edgeStyle) { - INHERITED(onClipPath, path, op, edgeStyle); - SkRecords::ClipOpAndAA opAA(op, kSoft_ClipEdgeStyle == edgeStyle); - this->append<SkRecords::ClipPath>(path, opAA); -} - -void SkRecordCanvas::onClipShader(sk_sp<SkShader> cs, SkClipOp op) { - INHERITED(onClipShader, cs, op); - this->append<SkRecords::ClipShader>(std::move(cs), op); -} - -void SkRecordCanvas::onClipRegion(const SkRegion& deviceRgn, SkClipOp op) { - INHERITED(onClipRegion, deviceRgn, op); - this->append<SkRecords::ClipRegion>(deviceRgn, op); -} - -void SkRecordCanvas::onResetClip() { - INHERITED(onResetClip); - this->append<SkRecords::ResetClip>(); -} - -sk_sp<SkSurface> SkRecordCanvas::onNewSurface(const SkImageInfo&, const SkSurfaceProps&) { - return nullptr; -} diff --git a/gfx/skia/skia/src/core/SkRecordCanvas.h b/gfx/skia/skia/src/core/SkRecordCanvas.h @@ -1,199 +0,0 @@ -/* - * Copyright 2014 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkRecordCanvas_DEFINED -#define SkRecordCanvas_DEFINED - -#include "include/core/SkCPURecorder.h" -#include "include/core/SkCanvasVirtualEnforcer.h" -#include "include/core/SkColor.h" -#include "include/core/SkM44.h" -#include "include/core/SkRefCnt.h" -#include "include/core/SkSamplingOptions.h" -#include "include/core/SkScalar.h" -#include "include/core/SkTypes.h" -#include "include/private/base/SkNoncopyable.h" -#include "include/private/base/SkTDArray.h" -#include "include/utils/SkNoDrawCanvas.h" -#include "src/core/SkBigPicture.h" - -#include <cstddef> -#include <memory> -#include <utility> - -class SkBlender; -class SkData; -class SkDrawable; -class SkImage; -class SkMatrix; -class SkMesh; -class SkPaint; -class SkPath; -class SkPicture; -class SkRRect; -class SkRecord; -class SkRecorder; -class SkRegion; -class SkShader; -class SkSurface; -class SkSurfaceProps; -class SkTextBlob; -class SkVertices; -enum class SkBlendMode; -enum class SkClipOp; -struct SkDrawShadowRec; -struct SkImageInfo; -struct SkPoint; -struct SkRSXform; -struct SkRect; - -namespace sktext { -class GlyphRunList; -namespace gpu { -class Slug; -} -} // namespace sktext - -class SkDrawableList : SkNoncopyable { -public: - SkDrawableList() {} - ~SkDrawableList(); - - int count() const { return fArray.size(); } - SkDrawable* const* begin() const { return fArray.begin(); } - SkDrawable* const* end() const { return fArray.end(); } - - void append(SkDrawable* drawable); - - // Return a new or ref'd array of pictures that were snapped from our drawables. - SkBigPicture::SnapshotArray* newDrawableSnapshot(); - -private: - SkTDArray<SkDrawable*> fArray; -}; - -// SkRecordCanvas provides an SkCanvas interface for recording into an SkRecord. - -class SkRecordCanvas final : public SkCanvasVirtualEnforcer<SkNoDrawCanvas> { -public: - // Does not take ownership of the SkRecord. - SkRecordCanvas(SkRecord*, int width, int height); // TODO: remove - SkRecordCanvas(SkRecord*, const SkRect& bounds); - - void reset(SkRecord*, const SkRect& bounds); - - size_t approxBytesUsedBySubPictures() const { return fApproxBytesUsedBySubPictures; } - - SkDrawableList* getDrawableList() const { return fDrawableList.get(); } - std::unique_ptr<SkDrawableList> detachDrawableList() { return std::move(fDrawableList); } - - // Make SkRecordCanvas forget entirely about its SkRecord*; all calls to SkRecordCanvas will - // fail. - void forgetRecord(); - - void willSave() override; - SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec&) override; - bool onDoSaveBehind(const SkRect*) override; - void willRestore() override {} - void didRestore() override; - SkRecorder* baseRecorder() const override { - // TODO(kjlubick) this class should implement SkRecorder (or maybe Record should). - return skcpu::Recorder::TODO(); - } - - void didConcat44(const SkM44&) override; - void didSetM44(const SkM44&) override; - void didScale(SkScalar, SkScalar) override; - void didTranslate(SkScalar, SkScalar) override; - - void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) override; - void onDrawDrawable(SkDrawable*, const SkMatrix*) override; - void onDrawTextBlob(const SkTextBlob* blob, - SkScalar x, - SkScalar y, - const SkPaint& paint) override; - void onDrawSlug(const sktext::gpu::Slug* slug, const SkPaint& paint) override; - void onDrawGlyphRunList(const sktext::GlyphRunList& glyphRunList, - const SkPaint& paint) override; - void onDrawPatch(const SkPoint cubics[12], - const SkColor colors[4], - const SkPoint texCoords[4], - SkBlendMode, - const SkPaint& paint) override; - - void onDrawPaint(const SkPaint&) override; - void onDrawBehind(const SkPaint&) override; - void onDrawPoints(PointMode, size_t count, const SkPoint pts[], const SkPaint&) override; - void onDrawRect(const SkRect&, const SkPaint&) override; - void onDrawRegion(const SkRegion&, const SkPaint&) override; - void onDrawOval(const SkRect&, const SkPaint&) override; - void onDrawArc(const SkRect&, SkScalar, SkScalar, bool, const SkPaint&) override; - void onDrawRRect(const SkRRect&, const SkPaint&) override; - void onDrawPath(const SkPath&, const SkPaint&) override; - - void onDrawImage2( - const SkImage*, SkScalar, SkScalar, const SkSamplingOptions&, const SkPaint*) override; - void onDrawImageRect2(const SkImage*, - const SkRect&, - const SkRect&, - const SkSamplingOptions&, - const SkPaint*, - SrcRectConstraint) override; - void onDrawImageLattice2( - const SkImage*, const Lattice&, const SkRect&, SkFilterMode, const SkPaint*) override; - void onDrawAtlas2(const SkImage*, - const SkRSXform[], - const SkRect[], - const SkColor[], - int, - SkBlendMode, - const SkSamplingOptions&, - const SkRect*, - const SkPaint*) override; - - void onDrawVerticesObject(const SkVertices*, SkBlendMode, const SkPaint&) override; - - void onDrawMesh(const SkMesh&, sk_sp<SkBlender>, const SkPaint&) override; - - void onDrawShadowRec(const SkPath&, const SkDrawShadowRec&) override; - - void onClipRect(const SkRect& rect, SkClipOp, ClipEdgeStyle) override; - void onClipRRect(const SkRRect& rrect, SkClipOp, ClipEdgeStyle) override; - void onClipPath(const SkPath& path, SkClipOp, ClipEdgeStyle) override; - void onClipShader(sk_sp<SkShader>, SkClipOp) override; - void onClipRegion(const SkRegion& deviceRgn, SkClipOp) override; - void onResetClip() override; - - void onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*) override; - - void onDrawAnnotation(const SkRect&, const char[], SkData*) override; - - void onDrawEdgeAAQuad( - const SkRect&, const SkPoint[4], QuadAAFlags, const SkColor4f&, SkBlendMode) override; - void onDrawEdgeAAImageSet2(const ImageSetEntry[], - int count, - const SkPoint[], - const SkMatrix[], - const SkSamplingOptions&, - const SkPaint*, - SrcRectConstraint) override; - - sk_sp<SkSurface> onNewSurface(const SkImageInfo&, const SkSurfaceProps&) override; - -private: - template <typename T> T* copy(const T*); - - template <typename T> T* copy(const T[], size_t count); - - template <typename T, typename... Args> void append(Args&&...); - - size_t fApproxBytesUsedBySubPictures; - SkRecord* fRecord; - std::unique_ptr<SkDrawableList> fDrawableList; -}; - -#endif // SkRecordCanvas_DEFINED diff --git a/gfx/skia/skia/src/core/SkRecordDraw.cpp b/gfx/skia/skia/src/core/SkRecordDraw.cpp @@ -10,13 +10,11 @@ #include "include/core/SkBBHFactory.h" #include "include/core/SkBlendMode.h" #include "include/core/SkBlender.h" -#include "include/core/SkColor.h" #include "include/core/SkImage.h" #include "include/core/SkMatrix.h" #include "include/core/SkMesh.h" #include "include/core/SkPaint.h" #include "include/core/SkRRect.h" -#include "include/core/SkRSXform.h" #include "include/core/SkRect.h" #include "include/core/SkRefCnt.h" #include "include/core/SkRegion.h" @@ -39,7 +37,6 @@ #include "src/utils/SkPatchUtils.h" #include <algorithm> -#include <cstddef> #include <optional> #include <vector> @@ -156,17 +153,14 @@ DRAW(DrawPaint, drawPaint(r.paint)) DRAW(DrawPath, drawPath(r.path, r.paint)) DRAW(DrawPatch, drawPatch(r.cubics, r.colors, r.texCoords, r.bmode, r.paint)) DRAW(DrawPicture, drawPicture(r.picture.get(), &r.matrix, r.paint)) -DRAW(DrawPoints, drawPoints(r.mode, {r.pts, r.count}, r.paint)) +DRAW(DrawPoints, drawPoints(r.mode, r.count, r.pts, r.paint)) DRAW(DrawRRect, drawRRect(r.rrect, r.paint)) DRAW(DrawRect, drawRect(r.rect, r.paint)) DRAW(DrawRegion, drawRegion(r.region, r.paint)) DRAW(DrawTextBlob, drawTextBlob(r.blob.get(), r.x, r.y, r.paint)) DRAW(DrawSlug, drawSlug(r.slug.get(), r.paint)) -DRAW(DrawAtlas, drawAtlas(r.atlas.get(), - {r.xforms, r.count}, - {r.texs, r.count}, - {r.colors, r.colors ? r.count : 0}, - r.mode, r.sampling, r.cull, r.paint)) +DRAW(DrawAtlas, drawAtlas(r.atlas.get(), r.xforms, r.texs, r.colors, r.count, r.mode, r.sampling, + r.cull, r.paint)) DRAW(DrawVertices, drawVertices(r.vertices, r.bmode, r.paint)) DRAW(DrawMesh, drawMesh(r.mesh, r.blender, r.paint)) DRAW(DrawShadowRec, private_draw_shadow_rec(r.path, r.rec)) @@ -455,7 +449,8 @@ private: : this->adjustAndMap(op.path.getBounds(), &op.paint); } Bounds bounds(const DrawPoints& op) const { - SkRect dst = SkRect::BoundsOrEmpty({op.pts, op.count}); + SkRect dst; + dst.setBounds(op.pts, op.count); // Pad the bounding box a little to make sure hairline points' bounds aren't empty. SkScalar stroke = std::max(op.paint.getStrokeWidth(), 0.01f); @@ -464,7 +459,8 @@ private: return this->adjustAndMap(dst, &op.paint); } Bounds bounds(const DrawPatch& op) const { - const auto dst = SkRect::BoundsOrEmpty({op.cubics, (size_t)SkPatchUtils::kNumCtrlPts}); + SkRect dst; + dst.setBounds(op.cubics, SkPatchUtils::kNumCtrlPts); return this->adjustAndMap(dst, &op.paint); } Bounds bounds(const DrawVertices& op) const { @@ -514,7 +510,10 @@ private: return this->adjustAndMap(op.rect, nullptr); } Bounds bounds(const DrawEdgeAAQuad& op) const { - const auto bounds = op.clip ? SkRect::BoundsOrEmpty({op.clip, 4}) : op.rect; + SkRect bounds = op.rect; + if (op.clip) { + bounds.setBounds(op.clip, 4); + } return this->adjustAndMap(bounds, nullptr); } Bounds bounds(const DrawEdgeAAImageSet& op) const { @@ -523,7 +522,7 @@ private: for (int i = 0; i < op.count; ++i) { SkRect entryBounds = op.set[i].fDstRect; if (op.set[i].fHasClip) { - entryBounds = SkRect::BoundsOrEmpty({op.dstClips + clipIndex, 4}); + entryBounds.setBounds(op.dstClips + clipIndex, 4); clipIndex += 4; } if (op.set[i].fMatrixIndex >= 0) { @@ -549,11 +548,11 @@ private: bool adjustForSaveLayerPaints(SkRect* rect, int savesToIgnore = 0) const { for (int i = fSaveStack.size() - 1 - savesToIgnore; i >= 0; i--) { - auto inverse = fSaveStack[i].ctm.invert(); - if (!inverse) { + SkMatrix inverse; + if (!fSaveStack[i].ctm.invert(&inverse)) { return false; } - inverse->mapRect(rect); + inverse.mapRect(rect); if (!AdjustForPaint(fSaveStack[i].paint, rect)) { return false; } diff --git a/gfx/skia/skia/src/core/SkRecordOpts.cpp b/gfx/skia/skia/src/core/SkRecordOpts.cpp @@ -156,7 +156,7 @@ static bool effectively_srcover(const SkPaint* paint) { struct SaveLayerDrawRestoreNooper { // Note that we use IsSingleDraw here, to avoid matching drawAtlas, drawVertices, etc... // Those operations (can) draw multiple, overlapping primitives that blend with each other. - // Applying this operation to them changes their behavior. (skbug.com/40045501) + // Applying this operation to them changes their behavior. (skbug.com/14554) typedef Pattern<Is<SaveLayer>, IsSingleDraw, Is<Restore>> Match; bool onMatch(SkRecord* record, Match* match, int begin, int end) { diff --git a/gfx/skia/skia/src/core/SkRecordedDrawable.h b/gfx/skia/skia/src/core/SkRecordedDrawable.h @@ -13,7 +13,7 @@ #include "include/core/SkRect.h" #include "include/core/SkRefCnt.h" #include "src/core/SkRecord.h" -#include "src/core/SkRecordCanvas.h" +#include "src/core/SkRecorder.h" #include <cstddef> #include <memory> diff --git a/gfx/skia/skia/src/core/SkRecorder.cpp b/gfx/skia/skia/src/core/SkRecorder.cpp @@ -0,0 +1,419 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "src/core/SkRecorder.h" + +#include "include/core/SkCanvas.h" +#include "include/core/SkData.h" +#include "include/core/SkDrawable.h" +#include "include/core/SkImage.h" +#include "include/core/SkImageFilter.h" +#include "include/core/SkImageInfo.h" +#include "include/core/SkMatrix.h" +#include "include/core/SkPaint.h" +#include "include/core/SkPicture.h" +#include "include/core/SkPoint.h" +#include "include/core/SkRSXform.h" +#include "include/core/SkRect.h" +#include "include/core/SkShader.h" +#include "include/core/SkString.h" +#include "include/core/SkSurface.h" +#include "include/core/SkTextBlob.h" +#include "include/core/SkVertices.h" +#include "include/private/base/SkFloatingPoint.h" +#include "include/private/base/SkTemplates.h" +#include "include/private/base/SkTo.h" +#include "include/private/chromium/Slug.h" +#include "src/core/SkBigPicture.h" +#include "src/core/SkCanvasPriv.h" +#include "src/core/SkRecord.h" +#include "src/core/SkRecords.h" +#include "src/text/GlyphRun.h" +#include "src/utils/SkPatchUtils.h" + +#include <cstdint> +#include <cstring> +#include <memory> +#include <new> + +class SkBlender; +class SkMesh; +class SkPath; +class SkRRect; +class SkRegion; +class SkSurfaceProps; +enum class SkBlendMode; +enum class SkClipOp; +struct SkDrawShadowRec; + +using namespace skia_private; + +SkDrawableList::~SkDrawableList() { + for(SkDrawable* p : fArray) { + p->unref(); + } + fArray.reset(); +} + +SkBigPicture::SnapshotArray* SkDrawableList::newDrawableSnapshot() { + const int count = fArray.size(); + if (0 == count) { + return nullptr; + } + AutoTMalloc<const SkPicture*> pics(count); + for (int i = 0; i < count; ++i) { + pics[i] = fArray[i]->makePictureSnapshot().release(); + } + return new SkBigPicture::SnapshotArray(pics.release(), count); +} + +void SkDrawableList::append(SkDrawable* drawable) { + *fArray.append() = SkRef(drawable); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +static SkIRect safe_picture_bounds(const SkRect& bounds) { + SkIRect picBounds = bounds.roundOut(); + // roundOut() saturates the float edges to +/-SK_MaxS32FitsInFloat (~2billion), but this is + // large enough that width/height calculations will overflow, leading to negative dimensions. + static constexpr int32_t kSafeEdge = SK_MaxS32FitsInFloat / 2 - 1; + static constexpr SkIRect kSafeBounds = {-kSafeEdge, -kSafeEdge, kSafeEdge, kSafeEdge}; + static_assert((kSafeBounds.fRight - kSafeBounds.fLeft) >= 0 && + (kSafeBounds.fBottom - kSafeBounds.fTop) >= 0); + if (!picBounds.intersect(kSafeBounds)) { + picBounds.setEmpty(); + } + return picBounds; +} + +SkRecorder::SkRecorder(SkRecord* record, int width, int height) + : SkCanvasVirtualEnforcer<SkNoDrawCanvas>(width, height) + , fApproxBytesUsedBySubPictures(0) + , fRecord(record) { + SkASSERT(this->imageInfo().width() >= 0 && this->imageInfo().height() >= 0); +} + +SkRecorder::SkRecorder(SkRecord* record, const SkRect& bounds) + : SkCanvasVirtualEnforcer<SkNoDrawCanvas>(safe_picture_bounds(bounds)) + , fApproxBytesUsedBySubPictures(0) + , fRecord(record) { + SkASSERT(this->imageInfo().width() >= 0 && this->imageInfo().height() >= 0); +} + +void SkRecorder::reset(SkRecord* record, const SkRect& bounds) { + this->forgetRecord(); + fRecord = record; + this->resetCanvas(safe_picture_bounds(bounds)); + SkASSERT(this->imageInfo().width() >= 0 && this->imageInfo().height() >= 0); +} + +void SkRecorder::forgetRecord() { + fDrawableList.reset(nullptr); + fApproxBytesUsedBySubPictures = 0; + fRecord = nullptr; +} + +// To make appending to fRecord a little less verbose. +template<typename T, typename... Args> +void SkRecorder::append(Args&&... args) { + new (fRecord->append<T>()) T{std::forward<Args>(args)...}; +} + +// For methods which must call back into SkNoDrawCanvas. +#define INHERITED(method, ...) this->SkNoDrawCanvas::method(__VA_ARGS__) + +// Use copy() only for optional arguments, to be copied if present or skipped if not. +// (For most types we just pass by value and let copy constructors do their thing.) +template <typename T> +T* SkRecorder::copy(const T* src) { + if (nullptr == src) { + return nullptr; + } + return new (fRecord->alloc<T>()) T(*src); +} + +// This copy() is for arrays. +// It will work with POD or non-POD, though currently we only use it for POD. +template <typename T> +T* SkRecorder::copy(const T src[], size_t count) { + if (nullptr == src) { + return nullptr; + } + T* dst = fRecord->alloc<T>(count); + for (size_t i = 0; i < count; i++) { + new (dst + i) T(src[i]); + } + return dst; +} + +// Specialization for copying strings, using memcpy. +// This measured around 2x faster for copying code points, +// but I found no corresponding speedup for other arrays. +template <> +char* SkRecorder::copy(const char src[], size_t count) { + if (nullptr == src) { + return nullptr; + } + char* dst = fRecord->alloc<char>(count); + memcpy(dst, src, count); + return dst; +} + +// As above, assuming and copying a terminating \0. +template <> +char* SkRecorder::copy(const char* src) { + return this->copy(src, strlen(src)+1); +} + +void SkRecorder::onDrawPaint(const SkPaint& paint) { + this->append<SkRecords::DrawPaint>(paint); +} + +void SkRecorder::onDrawBehind(const SkPaint& paint) { + this->append<SkRecords::DrawBehind>(paint); +} + +void SkRecorder::onDrawPoints(PointMode mode, + size_t count, + const SkPoint pts[], + const SkPaint& paint) { + this->append<SkRecords::DrawPoints>(paint, mode, SkToUInt(count), this->copy(pts, count)); +} + +void SkRecorder::onDrawRect(const SkRect& rect, const SkPaint& paint) { + this->append<SkRecords::DrawRect>(paint, rect); +} + +void SkRecorder::onDrawRegion(const SkRegion& region, const SkPaint& paint) { + this->append<SkRecords::DrawRegion>(paint, region); +} + +void SkRecorder::onDrawOval(const SkRect& oval, const SkPaint& paint) { + this->append<SkRecords::DrawOval>(paint, oval); +} + +void SkRecorder::onDrawArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, + bool useCenter, const SkPaint& paint) { + this->append<SkRecords::DrawArc>(paint, oval, startAngle, sweepAngle, useCenter); +} + +void SkRecorder::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) { + this->append<SkRecords::DrawRRect>(paint, rrect); +} + +void SkRecorder::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) { + this->append<SkRecords::DrawDRRect>(paint, outer, inner); +} + +void SkRecorder::onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) { + if (!fDrawableList) { + fDrawableList = std::make_unique<SkDrawableList>(); + } + fDrawableList->append(drawable); + this->append<SkRecords::DrawDrawable>(this->copy(matrix), drawable->getBounds(), fDrawableList->count() - 1); +} + +void SkRecorder::onDrawPath(const SkPath& path, const SkPaint& paint) { + this->append<SkRecords::DrawPath>(paint, path); +} + +void SkRecorder::onDrawImage2(const SkImage* image, SkScalar x, SkScalar y, + const SkSamplingOptions& sampling, const SkPaint* paint) { + this->append<SkRecords::DrawImage>(this->copy(paint), sk_ref_sp(image), x, y, sampling); +} + +void SkRecorder::onDrawImageRect2(const SkImage* image, const SkRect& src, const SkRect& dst, + const SkSamplingOptions& sampling, const SkPaint* paint, + SrcRectConstraint constraint) { + this->append<SkRecords::DrawImageRect>(this->copy(paint), sk_ref_sp(image), src, dst, + sampling, constraint); +} + +void SkRecorder::onDrawImageLattice2(const SkImage* image, const Lattice& lattice, const SkRect& dst, + SkFilterMode filter, const SkPaint* paint) { + int flagCount = lattice.fRectTypes ? (lattice.fXCount + 1) * (lattice.fYCount + 1) : 0; + SkASSERT(lattice.fBounds); + this->append<SkRecords::DrawImageLattice>(this->copy(paint), sk_ref_sp(image), + lattice.fXCount, this->copy(lattice.fXDivs, lattice.fXCount), + lattice.fYCount, this->copy(lattice.fYDivs, lattice.fYCount), + flagCount, this->copy(lattice.fRectTypes, flagCount), + this->copy(lattice.fColors, flagCount), *lattice.fBounds, dst, filter); +} + +void SkRecorder::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, + const SkPaint& paint) { + this->append<SkRecords::DrawTextBlob>(paint, sk_ref_sp(blob), x, y); +} + +void SkRecorder::onDrawSlug(const sktext::gpu::Slug* slug, const SkPaint& paint) { + this->append<SkRecords::DrawSlug>(paint, sk_ref_sp(slug)); +} + +void SkRecorder::onDrawGlyphRunList( + const sktext::GlyphRunList& glyphRunList, const SkPaint& paint) { + sk_sp<SkTextBlob> blob = sk_ref_sp(glyphRunList.blob()); + if (glyphRunList.blob() == nullptr) { + blob = glyphRunList.makeBlob(); + } + + this->onDrawTextBlob(blob.get(), glyphRunList.origin().x(), glyphRunList.origin().y(), paint); +} + +void SkRecorder::onDrawPicture(const SkPicture* pic, const SkMatrix* matrix, const SkPaint* paint) { + fApproxBytesUsedBySubPictures += pic->approximateBytesUsed(); + this->append<SkRecords::DrawPicture>(this->copy(paint), sk_ref_sp(pic), matrix ? *matrix : SkMatrix::I()); +} + +void SkRecorder::onDrawVerticesObject(const SkVertices* vertices, SkBlendMode bmode, + const SkPaint& paint) { + this->append<SkRecords::DrawVertices>(paint, + sk_ref_sp(const_cast<SkVertices*>(vertices)), + bmode); +} + +void SkRecorder::onDrawMesh(const SkMesh& mesh, sk_sp<SkBlender> blender, const SkPaint& paint) { + this->append<SkRecords::DrawMesh>(paint, mesh, std::move(blender)); +} + +void SkRecorder::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4], + const SkPoint texCoords[4], SkBlendMode bmode, + const SkPaint& paint) { + this->append<SkRecords::DrawPatch>(paint, + cubics ? this->copy(cubics, SkPatchUtils::kNumCtrlPts) : nullptr, + colors ? this->copy(colors, SkPatchUtils::kNumCorners) : nullptr, + texCoords ? this->copy(texCoords, SkPatchUtils::kNumCorners) : nullptr, + bmode); +} + +void SkRecorder::onDrawAtlas2(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[], + const SkColor colors[], int count, SkBlendMode mode, + const SkSamplingOptions& sampling, const SkRect* cull, + const SkPaint* paint) { + this->append<SkRecords::DrawAtlas>(this->copy(paint), + sk_ref_sp(atlas), + this->copy(xform, count), + this->copy(tex, count), + this->copy(colors, count), + count, + mode, + sampling, + this->copy(cull)); +} + +void SkRecorder::onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) { + this->append<SkRecords::DrawShadowRec>(path, rec); +} + +void SkRecorder::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) { + this->append<SkRecords::DrawAnnotation>(rect, SkString(key), sk_ref_sp(value)); +} + +void SkRecorder::onDrawEdgeAAQuad(const SkRect& rect, const SkPoint clip[4], + QuadAAFlags aa, const SkColor4f& color, SkBlendMode mode) { + this->append<SkRecords::DrawEdgeAAQuad>( + rect, this->copy(clip, 4), aa, color, mode); +} + +void SkRecorder::onDrawEdgeAAImageSet2(const ImageSetEntry set[], int count, + const SkPoint dstClips[], const SkMatrix preViewMatrices[], + const SkSamplingOptions& sampling, const SkPaint* paint, + SrcRectConstraint constraint) { + int totalDstClipCount, totalMatrixCount; + SkCanvasPriv::GetDstClipAndMatrixCounts(set, count, &totalDstClipCount, &totalMatrixCount); + + AutoTArray<ImageSetEntry> setCopy(count); + for (int i = 0; i < count; ++i) { + setCopy[i] = set[i]; + } + + this->append<SkRecords::DrawEdgeAAImageSet>(this->copy(paint), std::move(setCopy), count, + this->copy(dstClips, totalDstClipCount), + this->copy(preViewMatrices, totalMatrixCount), sampling, constraint); +} + +void SkRecorder::willSave() { + this->append<SkRecords::Save>(); +} + +SkCanvas::SaveLayerStrategy SkRecorder::getSaveLayerStrategy(const SaveLayerRec& rec) { + AutoTArray<sk_sp<SkImageFilter>> filters(rec.fFilters.size()); + for (size_t i = 0; i < rec.fFilters.size(); ++i) { + filters[i] = rec.fFilters[i]; + } + + this->append<SkRecords::SaveLayer>(this->copy(rec.fBounds), + this->copy(rec.fPaint), + sk_ref_sp(rec.fBackdrop), + rec.fSaveLayerFlags, + SkCanvasPriv::GetBackdropScaleFactor(rec), + rec.fBackdropTileMode, + std::move(filters)); + return SkCanvas::kNoLayer_SaveLayerStrategy; +} + +bool SkRecorder::onDoSaveBehind(const SkRect* subset) { + this->append<SkRecords::SaveBehind>(this->copy(subset)); + return false; +} + +void SkRecorder::didRestore() { + this->append<SkRecords::Restore>(this->getTotalMatrix()); +} + +void SkRecorder::didConcat44(const SkM44& m) { + this->append<SkRecords::Concat44>(m); +} + +void SkRecorder::didSetM44(const SkM44& m) { + this->append<SkRecords::SetM44>(m); +} + +void SkRecorder::didScale(SkScalar sx, SkScalar sy) { + this->append<SkRecords::Scale>(sx, sy); +} + +void SkRecorder::didTranslate(SkScalar dx, SkScalar dy) { + this->append<SkRecords::Translate>(dx, dy); +} + +void SkRecorder::onClipRect(const SkRect& rect, SkClipOp op, ClipEdgeStyle edgeStyle) { + INHERITED(onClipRect, rect, op, edgeStyle); + SkRecords::ClipOpAndAA opAA(op, kSoft_ClipEdgeStyle == edgeStyle); + this->append<SkRecords::ClipRect>(rect, opAA); +} + +void SkRecorder::onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle edgeStyle) { + INHERITED(onClipRRect, rrect, op, edgeStyle); + SkRecords::ClipOpAndAA opAA(op, kSoft_ClipEdgeStyle == edgeStyle); + this->append<SkRecords::ClipRRect>(rrect, opAA); +} + +void SkRecorder::onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle edgeStyle) { + INHERITED(onClipPath, path, op, edgeStyle); + SkRecords::ClipOpAndAA opAA(op, kSoft_ClipEdgeStyle == edgeStyle); + this->append<SkRecords::ClipPath>(path, opAA); +} + +void SkRecorder::onClipShader(sk_sp<SkShader> cs, SkClipOp op) { + INHERITED(onClipShader, cs, op); + this->append<SkRecords::ClipShader>(std::move(cs), op); +} + +void SkRecorder::onClipRegion(const SkRegion& deviceRgn, SkClipOp op) { + INHERITED(onClipRegion, deviceRgn, op); + this->append<SkRecords::ClipRegion>(deviceRgn, op); +} + +void SkRecorder::onResetClip() { + INHERITED(onResetClip); + this->append<SkRecords::ResetClip>(); +} + +sk_sp<SkSurface> SkRecorder::onNewSurface(const SkImageInfo&, const SkSurfaceProps&) { + return nullptr; +} diff --git a/gfx/skia/skia/src/core/SkRecorder.h b/gfx/skia/skia/src/core/SkRecorder.h @@ -0,0 +1,176 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkRecorder_DEFINED +#define SkRecorder_DEFINED + +#include "include/core/SkCanvasVirtualEnforcer.h" +#include "include/core/SkColor.h" +#include "include/core/SkM44.h" +#include "include/core/SkRefCnt.h" +#include "include/core/SkSamplingOptions.h" +#include "include/core/SkScalar.h" +#include "include/core/SkTypes.h" +#include "include/private/base/SkNoncopyable.h" +#include "include/private/base/SkTDArray.h" +#include "include/utils/SkNoDrawCanvas.h" +#include "src/core/SkBigPicture.h" + +#include <cstddef> +#include <memory> +#include <utility> + +class SkBlender; +class SkData; +class SkDrawable; +class SkImage; +class SkMatrix; +class SkMesh; +class SkPaint; +class SkPath; +class SkPicture; +class SkRRect; +class SkRecord; +class SkRegion; +class SkShader; +class SkSurface; +class SkSurfaceProps; +class SkTextBlob; +class SkVertices; +enum class SkBlendMode; +enum class SkClipOp; +struct SkDrawShadowRec; +struct SkImageInfo; +struct SkPoint; +struct SkRSXform; +struct SkRect; + +namespace sktext { + class GlyphRunList; + namespace gpu { class Slug; } +} + +class SkDrawableList : SkNoncopyable { +public: + SkDrawableList() {} + ~SkDrawableList(); + + int count() const { return fArray.size(); } + SkDrawable* const* begin() const { return fArray.begin(); } + SkDrawable* const* end() const { return fArray.end(); } + + void append(SkDrawable* drawable); + + // Return a new or ref'd array of pictures that were snapped from our drawables. + SkBigPicture::SnapshotArray* newDrawableSnapshot(); + +private: + SkTDArray<SkDrawable*> fArray; +}; + +// SkRecorder provides an SkCanvas interface for recording into an SkRecord. + +class SkRecorder final : public SkCanvasVirtualEnforcer<SkNoDrawCanvas> { +public: + // Does not take ownership of the SkRecord. + SkRecorder(SkRecord*, int width, int height); // TODO: remove + SkRecorder(SkRecord*, const SkRect& bounds); + + void reset(SkRecord*, const SkRect& bounds); + + size_t approxBytesUsedBySubPictures() const { return fApproxBytesUsedBySubPictures; } + + SkDrawableList* getDrawableList() const { return fDrawableList.get(); } + std::unique_ptr<SkDrawableList> detachDrawableList() { return std::move(fDrawableList); } + + // Make SkRecorder forget entirely about its SkRecord*; all calls to SkRecorder will fail. + void forgetRecord(); + + void willSave() override; + SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec&) override; + bool onDoSaveBehind(const SkRect*) override; + void willRestore() override {} + void didRestore() override; + + void didConcat44(const SkM44&) override; + void didSetM44(const SkM44&) override; + void didScale(SkScalar, SkScalar) override; + void didTranslate(SkScalar, SkScalar) override; + + void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) override; + void onDrawDrawable(SkDrawable*, const SkMatrix*) override; + void onDrawTextBlob(const SkTextBlob* blob, + SkScalar x, + SkScalar y, + const SkPaint& paint) override; + void onDrawSlug(const sktext::gpu::Slug* slug, const SkPaint& paint) override; + void onDrawGlyphRunList( + const sktext::GlyphRunList& glyphRunList, const SkPaint& paint) override; + void onDrawPatch(const SkPoint cubics[12], const SkColor colors[4], + const SkPoint texCoords[4], SkBlendMode, + const SkPaint& paint) override; + + void onDrawPaint(const SkPaint&) override; + void onDrawBehind(const SkPaint&) override; + void onDrawPoints(PointMode, size_t count, const SkPoint pts[], const SkPaint&) override; + void onDrawRect(const SkRect&, const SkPaint&) override; + void onDrawRegion(const SkRegion&, const SkPaint&) override; + void onDrawOval(const SkRect&, const SkPaint&) override; + void onDrawArc(const SkRect&, SkScalar, SkScalar, bool, const SkPaint&) override; + void onDrawRRect(const SkRRect&, const SkPaint&) override; + void onDrawPath(const SkPath&, const SkPaint&) override; + + void onDrawImage2(const SkImage*, SkScalar, SkScalar, const SkSamplingOptions&, + const SkPaint*) override; + void onDrawImageRect2(const SkImage*, const SkRect&, const SkRect&, const SkSamplingOptions&, + const SkPaint*, SrcRectConstraint) override; + void onDrawImageLattice2(const SkImage*, const Lattice&, const SkRect&, SkFilterMode, + const SkPaint*) override; + void onDrawAtlas2(const SkImage*, const SkRSXform[], const SkRect[], const SkColor[], int, + SkBlendMode, const SkSamplingOptions&, const SkRect*, const SkPaint*) override; + + void onDrawVerticesObject(const SkVertices*, SkBlendMode, const SkPaint&) override; + + void onDrawMesh(const SkMesh&, sk_sp<SkBlender>, const SkPaint&) override; + + void onDrawShadowRec(const SkPath&, const SkDrawShadowRec&) override; + + void onClipRect(const SkRect& rect, SkClipOp, ClipEdgeStyle) override; + void onClipRRect(const SkRRect& rrect, SkClipOp, ClipEdgeStyle) override; + void onClipPath(const SkPath& path, SkClipOp, ClipEdgeStyle) override; + void onClipShader(sk_sp<SkShader>, SkClipOp) override; + void onClipRegion(const SkRegion& deviceRgn, SkClipOp) override; + void onResetClip() override; + + void onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*) override; + + void onDrawAnnotation(const SkRect&, const char[], SkData*) override; + + void onDrawEdgeAAQuad(const SkRect&, const SkPoint[4], QuadAAFlags, const SkColor4f&, + SkBlendMode) override; + void onDrawEdgeAAImageSet2(const ImageSetEntry[], int count, const SkPoint[], const SkMatrix[], + const SkSamplingOptions&, const SkPaint*, + SrcRectConstraint) override; + + sk_sp<SkSurface> onNewSurface(const SkImageInfo&, const SkSurfaceProps&) override; + +private: + template <typename T> + T* copy(const T*); + + template <typename T> + T* copy(const T[], size_t count); + + template<typename T, typename... Args> + void append(Args&&...); + + size_t fApproxBytesUsedBySubPictures; + SkRecord* fRecord; + std::unique_ptr<SkDrawableList> fDrawableList; +}; + +#endif//SkRecorder_DEFINED diff --git a/gfx/skia/skia/src/core/SkRecords.h b/gfx/skia/skia/src/core/SkRecords.h @@ -349,7 +349,7 @@ RECORD(DrawMesh, kDraw_Tag|kHasPaint_Tag|kMultiDraw_Tag, RECORD(DrawShadowRec, kDraw_Tag, PreCachedPath path; SkDrawShadowRec rec) -RECORD(DrawAnnotation, 0, // TODO: kDraw_Tag, skbug.com/40036727 +RECORD(DrawAnnotation, 0, // TODO: kDraw_Tag, skia:5548 SkRect rect; SkString key; sk_sp<SkData> value) diff --git a/gfx/skia/skia/src/core/SkRect.cpp b/gfx/skia/skia/src/core/SkRect.cpp @@ -47,91 +47,58 @@ void SkIRect::join(const SkIRect& r) { ///////////////////////////////////////////////////////////////////////////// -std::optional<SkRect> SkRect::Bounds(SkSpan<const SkPoint> points) { - if (points.empty()) { - return SkRect::MakeEmpty(); +void SkRect::toQuad(SkPoint quad[4]) const { + SkASSERT(quad); + + quad[0].set(fLeft, fTop); + quad[1].set(fRight, fTop); + quad[2].set(fRight, fBottom); + quad[3].set(fLeft, fBottom); +} + +#include "src/base/SkVx.h" + +bool SkRect::setBoundsCheck(const SkPoint pts[], int count) { + SkASSERT((pts && count > 0) || count == 0); + + if (count <= 0) { + this->setEmpty(); + return true; } - /* - * Both of these variants compute the same numerics. - * - * But, the "simple" one (no explicit skvx) runs faster (most of the time) on 64bit - * machines, and the tricky skvx version runs faster (most of the time) on 32bit machines. - * - * Hence the if/else - */ - - if constexpr (sizeof(void*) == 8) { - float L = points[0].fX, T = points[0].fY, R = points[0].fX, B = points[0].fY; - float nx = 0, ny = 0; - for (auto p : points) { - L = std::fminf(p.fX, L); - T = std::fminf(p.fY, T); - R = std::fmaxf(p.fX, R); - B = std::fmaxf(p.fY, B); - - // we do this to look for infinities or nans - nx *= p.fX; - ny *= p.fY; - } - - // if this is true, all our values were finite - if (nx == 0 && ny == 0) { - return {{L, T, R, B}}; - } + skvx::float4 min, max; + if (count & 1) { + min = max = skvx::float2::Load(pts).xyxy(); + pts += 1; + count -= 1; } else { - auto count = points.size(); - auto pts = points.data(); - - skvx::float4 min, max; - if (count & 1) { - min = max = skvx::float2::Load(pts).xyxy(); - pts += 1; - count -= 1; - } else { - min = max = skvx::float4::Load(pts); - pts += 2; - count -= 2; - } - - skvx::float4 accum = min * 0; - while (count) { - skvx::float4 xy = skvx::float4::Load(pts); - accum = accum * xy; - min = skvx::min(min, xy); - max = skvx::max(max, xy); - pts += 2; - count -= 2; - } - - const bool all_finite = all(accum * 0 == 0); - if (all_finite) { - return MakeLTRB(std::min(min[0], min[2]), std::min(min[1], min[3]), - std::max(max[0], max[2]), std::max(max[1], max[3])); - } + min = max = skvx::float4::Load(pts); + pts += 2; + count -= 2; } - /* - * If we got here, we were not empty, and at least one of the span values was - * either an Infinity or NaN -- so we return failure (no finite bounds) - */ - return {}; -} + skvx::float4 accum = min * 0; + while (count) { + skvx::float4 xy = skvx::float4::Load(pts); + accum = accum * xy; + min = skvx::min(min, xy); + max = skvx::max(max, xy); + pts += 2; + count -= 2; + } -bool SkRect::setBoundsCheck(SkSpan<const SkPoint> pts) { - if (auto bounds = Bounds(pts)) { - *this = bounds.value(); - return true; + const bool all_finite = all(accum * 0 == 0); + if (all_finite) { + this->setLTRB(std::min(min[0], min[2]), std::min(min[1], min[3]), + std::max(max[0], max[2]), std::max(max[1], max[3])); } else { - *this = MakeEmpty(); - return false; + this->setEmpty(); } + return all_finite; } -void SkRect::setBoundsNoCheck(SkSpan<const SkPoint> pts) { - if (auto bounds = Bounds(pts)) { - *this = bounds.value(); - } else { +void SkRect::setBoundsNoCheck(const SkPoint pts[], int count) { + if (!this->setBoundsCheck(pts, count)) { this->setLTRB(SK_FloatNaN, SK_FloatNaN, SK_FloatNaN, SK_FloatNaN); } } diff --git a/gfx/skia/skia/src/core/SkRegion_path.cpp b/gfx/skia/skia/src/core/SkRegion_path.cpp @@ -8,7 +8,6 @@ #include "include/core/SkColor.h" #include "include/core/SkMatrix.h" #include "include/core/SkPath.h" -#include "include/core/SkPathBuilder.h" #include "include/core/SkRect.h" #include "include/core/SkRegion.h" #include "include/core/SkScalar.h" @@ -23,7 +22,6 @@ #include "src/base/SkSafeMath.h" #include "src/base/SkTSort.h" #include "src/core/SkBlitter.h" -#include "src/core/SkPathPriv.h" #include "src/core/SkRegionPriv.h" #include "src/core/SkScan.h" @@ -261,7 +259,7 @@ void SkRgnBuilder::copyToRgn(SkRegion::RunType runs[]) const { *runs = SkRegion_kRunTypeSentinel; } -static unsigned verb_to_initial_last_index(SkPathVerb verb) { +static unsigned verb_to_initial_last_index(unsigned verb) { static const uint8_t gPathVerbToInitialLastIndex[] = { 0, // kMove_Verb 1, // kLine_Verb @@ -269,13 +267,13 @@ static unsigned verb_to_initial_last_index(SkPathVerb verb) { 2, // kConic_Verb 3, // kCubic_Verb 0, // kClose_Verb + 0 // kDone_Verb }; - const unsigned index = static_cast<unsigned>(verb); - SkASSERT(index < std::size(gPathVerbToInitialLastIndex)); - return gPathVerbToInitialLastIndex[index]; + SkASSERT((unsigned)verb < std::size(gPathVerbToInitialLastIndex)); + return gPathVerbToInitialLastIndex[verb]; } -static unsigned verb_to_max_edges(SkPathVerb verb) { +static unsigned verb_to_max_edges(unsigned verb) { static const uint8_t gPathVerbToMaxEdges[] = { 0, // kMove_Verb 1, // kLine_Verb @@ -283,23 +281,23 @@ static unsigned verb_to_max_edges(SkPathVerb verb) { 2, // kConic_VerbB 3, // kCubic_Verb 0, // kClose_Verb + 0 // kDone_Verb }; - const unsigned index = static_cast<unsigned>(verb); - SkASSERT(index < std::size(gPathVerbToMaxEdges)); - return gPathVerbToMaxEdges[index]; + SkASSERT((unsigned)verb < std::size(gPathVerbToMaxEdges)); + return gPathVerbToMaxEdges[verb]; } // If returns 0, ignore itop and ibot static int count_path_runtype_values(const SkPath& path, int* itop, int* ibot) { + SkPath::Iter iter(path, true); + SkPoint pts[4]; + SkPath::Verb verb; + int maxEdges = 0; SkScalar top = SkIntToScalar(SK_MaxS16); SkScalar bot = SkIntToScalar(SK_MinS16); - SkPath::Iter iter(path, true); - while (auto rec = iter.next()) { - const SkPathVerb verb = rec->fVerb; - const SkSpan<const SkPoint> pts = rec->fPoints; - + while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { maxEdges += verb_to_max_edges(verb); int lastIndex = verb_to_initial_last_index(verb); @@ -311,7 +309,7 @@ static int count_path_runtype_values(const SkPath& path, int* itop, int* ibot) { bot = pts[i].fY; } } - } else if (SkPathVerb::kMove == verb) { + } else if (SkPath::kMove_Verb == verb) { if (top > pts[0].fY) { top = pts[0].fY; } else if (bot < pts[0].fY) { @@ -420,7 +418,7 @@ bool SkRegion::setPath(const SkPath& path, const SkRegion& clip) { return this->setEmpty(); } - SkScan::FillPath(SkPathPriv::Raw(path), clip, &builder); + SkScan::FillPath(path, clip, &builder); builder.done(); int count = builder.computeRunCount(); @@ -514,7 +512,7 @@ static void find_link(Edge* base, Edge* stop) { base->fFlags = Edge::kCompleteLink; } -static int extract_path(Edge* edge, Edge* stop, SkPathBuilder* builder) { +static int extract_path(Edge* edge, Edge* stop, SkPath* path) { while (0 == edge->fFlags) { edge++; // skip over "used" edges } @@ -527,20 +525,20 @@ static int extract_path(Edge* edge, Edge* stop, SkPathBuilder* builder) { SkASSERT(edge != base); int count = 1; - builder->moveTo(SkIntToScalar(prev->fX), SkIntToScalar(prev->fY0)); + path->moveTo(SkIntToScalar(prev->fX), SkIntToScalar(prev->fY0)); prev->fFlags = 0; do { if (prev->fX != edge->fX || prev->fY1 != edge->fY0) { // skip collinear - builder->lineTo(SkIntToScalar(prev->fX), SkIntToScalar(prev->fY1)); // V - builder->lineTo(SkIntToScalar(edge->fX), SkIntToScalar(edge->fY0)); // H + path->lineTo(SkIntToScalar(prev->fX), SkIntToScalar(prev->fY1)); // V + path->lineTo(SkIntToScalar(edge->fX), SkIntToScalar(edge->fY0)); // H } prev = edge; edge = edge->fNext; count += 1; prev->fFlags = 0; } while (edge != base); - builder->lineTo(SkIntToScalar(prev->fX), SkIntToScalar(prev->fY1)); // V - builder->close(); + path->lineTo(SkIntToScalar(prev->fX), SkIntToScalar(prev->fY1)); // V + path->close(); return count; } @@ -550,10 +548,10 @@ struct EdgeLT { } }; -bool SkRegion::addBoundaryPath(SkPathBuilder* builder) const { +bool SkRegion::getBoundaryPath(SkPath* path) const { // path could safely be nullptr if we're empty, but the caller shouldn't // *know* that - SkASSERT(builder); + SkASSERT(path); if (this->isEmpty()) { return false; @@ -564,7 +562,7 @@ bool SkRegion::addBoundaryPath(SkPathBuilder* builder) const { if (this->isRect()) { SkRect r; r.set(bounds); // this converts the ints to scalars - builder->addRect(r); + path->addRect(r); return true; } @@ -594,27 +592,11 @@ bool SkRegion::addBoundaryPath(SkPathBuilder* builder) const { } #endif - builder->incReserve(count << 1); + path->incReserve(count << 1); do { SkASSERT(count > 1); - count -= extract_path(start, stop, builder); + count -= extract_path(start, stop, path); } while (count > 0); return true; } - -SkPath SkRegion::getBoundaryPath() const { - SkPathBuilder builder; - (void)this->addBoundaryPath(&builder); - return builder.detach(); -} - -#ifndef SK_HIDE_PATH_EDIT_METHODS -bool SkRegion::getBoundaryPath(SkPath* path) const { - if (this->isEmpty()) { - return false; - } - path->addPath(this->getBoundaryPath()); - return true; -} -#endif diff --git a/gfx/skia/skia/src/core/SkResourceCache.cpp b/gfx/skia/skia/src/core/SkResourceCache.cpp @@ -7,6 +7,7 @@ #include "src/core/SkResourceCache.h" +#include "include/core/SkGraphics.h" #include "include/core/SkString.h" #include "include/core/SkTraceMemoryDump.h" #include "include/core/SkTypes.h" @@ -14,12 +15,13 @@ #include "include/private/base/SkDebug.h" #include "include/private/base/SkMalloc.h" #include "include/private/base/SkMath.h" +#include "include/private/base/SkMutex.h" #include "include/private/base/SkTArray.h" #include "include/private/base/SkTo.h" #include "src/core/SkCachedData.h" #include "src/core/SkChecksum.h" +#include "src/core/SkImageFilter_Base.h" #include "src/core/SkMessageBus.h" -#include "src/core/SkSynchronizedResourceCache.h" #include "src/core/SkTHash.h" #if defined(SK_USE_DISCARDABLE_SCALEDIMAGECACHE) @@ -461,72 +463,93 @@ void SkResourceCache::checkMessages() { /////////////////////////////////////////////////////////////////////////////// +static SkMutex& resource_cache_mutex() { + static SkMutex& mutex = *(new SkMutex); + return mutex; +} + /** Must hold resource_cache_mutex() when calling. */ -static SkSynchronizedResourceCache* get_cache() { - static SkSynchronizedResourceCache* gResourceCache = nullptr; +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) { #if defined(SK_USE_DISCARDABLE_SCALEDIMAGECACHE) - gResourceCache = new SkSynchronizedResourceCache(SkDiscardableMemory::Create); + gResourceCache = new SkResourceCache(SkDiscardableMemory::Create); #else - gResourceCache = new SkSynchronizedResourceCache(SK_DEFAULT_IMAGE_CACHE_LIMIT); + gResourceCache = new SkResourceCache(SK_DEFAULT_IMAGE_CACHE_LIMIT); #endif } return gResourceCache; } size_t SkResourceCache::GetTotalBytesUsed() { + SkAutoMutexExclusive am(resource_cache_mutex()); return get_cache()->getTotalBytesUsed(); } size_t SkResourceCache::GetTotalByteLimit() { + SkAutoMutexExclusive am(resource_cache_mutex()); return get_cache()->getTotalByteLimit(); } size_t SkResourceCache::SetTotalByteLimit(size_t newLimit) { + SkAutoMutexExclusive am(resource_cache_mutex()); return get_cache()->setTotalByteLimit(newLimit); } SkResourceCache::DiscardableFactory SkResourceCache::GetDiscardableFactory() { + SkAutoMutexExclusive am(resource_cache_mutex()); return get_cache()->discardableFactory(); } SkCachedData* SkResourceCache::NewCachedData(size_t bytes) { + SkAutoMutexExclusive am(resource_cache_mutex()); return get_cache()->newCachedData(bytes); } void SkResourceCache::Dump() { + SkAutoMutexExclusive am(resource_cache_mutex()); get_cache()->dump(); } size_t SkResourceCache::SetSingleAllocationByteLimit(size_t size) { + SkAutoMutexExclusive am(resource_cache_mutex()); return get_cache()->setSingleAllocationByteLimit(size); } size_t SkResourceCache::GetSingleAllocationByteLimit() { + SkAutoMutexExclusive am(resource_cache_mutex()); return get_cache()->getSingleAllocationByteLimit(); } size_t SkResourceCache::GetEffectiveSingleAllocationByteLimit() { + SkAutoMutexExclusive am(resource_cache_mutex()); return get_cache()->getEffectiveSingleAllocationByteLimit(); } void SkResourceCache::PurgeAll() { + SkAutoMutexExclusive am(resource_cache_mutex()); return get_cache()->purgeAll(); } void SkResourceCache::CheckMessages() { + SkAutoMutexExclusive am(resource_cache_mutex()); return get_cache()->checkMessages(); } bool SkResourceCache::Find(const Key& key, FindVisitor visitor, void* context) { + SkAutoMutexExclusive am(resource_cache_mutex()); return get_cache()->find(key, visitor, context); } void SkResourceCache::Add(Rec* rec, void* payload) { + SkAutoMutexExclusive am(resource_cache_mutex()); get_cache()->add(rec, payload); } void SkResourceCache::VisitAll(Visitor visitor, void* context) { + SkAutoMutexExclusive am(resource_cache_mutex()); get_cache()->visitAll(visitor, context); } @@ -538,6 +561,33 @@ void SkResourceCache::PostPurgeSharedID(uint64_t sharedID) { /////////////////////////////////////////////////////////////////////////////// +size_t SkGraphics::GetResourceCacheTotalBytesUsed() { + return SkResourceCache::GetTotalBytesUsed(); +} + +size_t SkGraphics::GetResourceCacheTotalByteLimit() { + return SkResourceCache::GetTotalByteLimit(); +} + +size_t SkGraphics::SetResourceCacheTotalByteLimit(size_t newLimit) { + return SkResourceCache::SetTotalByteLimit(newLimit); +} + +size_t SkGraphics::GetResourceCacheSingleAllocationByteLimit() { + return SkResourceCache::GetSingleAllocationByteLimit(); +} + +size_t SkGraphics::SetResourceCacheSingleAllocationByteLimit(size_t newLimit) { + return SkResourceCache::SetSingleAllocationByteLimit(newLimit); +} + +void SkGraphics::PurgeResourceCache() { + SkImageFilter_Base::PurgeCache(); + return SkResourceCache::PurgeAll(); +} + +///////////// + static void dump_visitor(const SkResourceCache::Rec& rec, void*) { SkDebugf("RC: %12s bytes %9zu discardable %p\n", rec.getCategory(), rec.bytesUsed(), rec.diagnostic_only_getDiscardable()); diff --git a/gfx/skia/skia/src/core/SkResourceCache.h b/gfx/skia/skia/src/core/SkResourceCache.h @@ -208,7 +208,7 @@ public: * changed at runtime with setTotalByteLimit. */ explicit SkResourceCache(size_t byteLimit); - virtual ~SkResourceCache(); + ~SkResourceCache(); /** * Returns true if the visitor was called on a matching Key, and the visitor returned true. @@ -219,45 +219,45 @@ public: * true : Rec is valid * false : Rec is "stale" -- the cache will purge it. */ - virtual bool find(const Key&, FindVisitor, void* context) ; - virtual void add(Rec*, void* payload = nullptr); - virtual void visitAll(Visitor, void* context); + bool find(const Key&, FindVisitor, void* context); + void add(Rec*, void* payload = nullptr); + void visitAll(Visitor, void* context); - virtual size_t getTotalBytesUsed() const { return fTotalBytesUsed; } - virtual size_t getTotalByteLimit() const { return fTotalByteLimit; } + size_t getTotalBytesUsed() const { return fTotalBytesUsed; } + size_t getTotalByteLimit() const { return fTotalByteLimit; } /** * This is respected by SkBitmapProcState::possiblyScaleImage. * 0 is no maximum at all; this is the default. * setSingleAllocationByteLimit() returns the previous value. */ - virtual size_t setSingleAllocationByteLimit(size_t maximumAllocationSize); - virtual size_t getSingleAllocationByteLimit() const; + size_t setSingleAllocationByteLimit(size_t maximumAllocationSize); + size_t getSingleAllocationByteLimit() const; // returns the logical single allocation size (pinning against the budget when the cache // is not backed by discardable memory. - virtual size_t getEffectiveSingleAllocationByteLimit() const; + size_t getEffectiveSingleAllocationByteLimit() const; /** * Set the maximum number of bytes available to this cache. If the current * cache exceeds this new value, it will be purged to try to fit within * this new limit. */ - virtual size_t setTotalByteLimit(size_t newLimit); + size_t setTotalByteLimit(size_t newLimit); - virtual void purgeSharedID(uint64_t sharedID); + void purgeSharedID(uint64_t sharedID); - virtual void purgeAll() { + void purgeAll() { this->purgeAsNeeded(true); } - virtual DiscardableFactory discardableFactory() const { return fDiscardableFactory; } + DiscardableFactory discardableFactory() const { return fDiscardableFactory; } - virtual SkCachedData* newCachedData(size_t bytes); + SkCachedData* newCachedData(size_t bytes); /** * Call SkDebugf() with diagnostic information about the state of the cache */ - virtual void dump() const; + void dump() const; private: Rec* fHead; diff --git a/gfx/skia/skia/src/core/SkRuntimeEffect.cpp b/gfx/skia/skia/src/core/SkRuntimeEffect.cpp @@ -494,7 +494,7 @@ SkRuntimeEffect::Result SkRuntimeEffect::MakeInternal(std::unique_ptr<SkSL::Prog switch (kind) { case SkSL::ProgramKind::kPrivateRuntimeColorFilter: case SkSL::ProgramKind::kRuntimeColorFilter: - // TODO(skbug.com/40042585): Figure out a way to run ES3+ color filters on the CPU. This doesn't + // TODO(skia:11209): Figure out a way to run ES3+ color filters on the CPU. This doesn't // need to be fast - it could just be direct IR evaluation. But without it, there's no // way for us to fully implement the SkColorFilter API (eg, `filterColor4f`) if (!SkRuntimeEffectPriv::CanDraw(SkCapabilities::RasterBackend().get(), @@ -589,7 +589,7 @@ SkRuntimeEffect::Result SkRuntimeEffect::MakeInternal(std::unique_ptr<SkSL::Prog // If the child is never sampled, we pretend that it's actually in PassThrough mode. // Otherwise, the GP code for collecting transforms and emitting transform code gets // very confused, leading to asserts and bad (backend) shaders. There's an implicit - // assumption that every FP is used by its parent. (skbug.com/40043510) + // assumption that every FP is used by its parent. (skbug.com/12429) sampleUsages.push_back(usage.isSampled() ? usage : SkSL::SampleUsage::PassThrough()); } diff --git a/gfx/skia/skia/src/core/SkRuntimeEffectPriv.h b/gfx/skia/skia/src/core/SkRuntimeEffectPriv.h @@ -25,7 +25,6 @@ #include <cstdint> #include <functional> #include <memory> -#include <vector> #include "include/sksl/SkSLVersion.h" @@ -161,9 +160,6 @@ public: static bool UsesColorTransform(const SkRuntimeEffect* effect) { return effect->usesColorTransform(); } - static SkSL::SampleUsage ChildSampleUsage(const SkRuntimeEffect* effect, int child) { - return effect->fSampleUsages[child]; - } }; // These internal APIs for creating runtime effects vary from the public API in two ways: @@ -220,8 +216,7 @@ public: s.fDstColorType, s.fDstCS, SkColors::kTransparent, - s.fSurfaceProps, - s.fDstBounds} + s.fSurfaceProps} , fMatrix(m) , fChildren(c) , fSampleUsages(u) {} diff --git a/gfx/skia/skia/src/core/SkScalar.cpp b/gfx/skia/skia/src/core/SkScalar.cpp @@ -8,7 +8,8 @@ #include "include/core/SkScalar.h" #include "include/private/base/SkDebug.h" -float SkFloatInterpFunc(float searchKey, const float keys[], const float values[], int length) { +SkScalar SkScalarInterpFunc(SkScalar searchKey, const SkScalar keys[], + const SkScalar values[], int length) { SkASSERT(length > 0); SkASSERT(keys != nullptr); SkASSERT(values != nullptr); @@ -30,8 +31,8 @@ float SkFloatInterpFunc(float searchKey, const float keys[], const float values[ return values[0]; } // Otherwise, interpolate between right - 1 and right. - float leftKey = keys[right-1]; - float rightKey = keys[right]; - float fract = (searchKey - leftKey) / (rightKey - leftKey); + SkScalar leftKey = keys[right-1]; + SkScalar rightKey = keys[right]; + SkScalar fract = (searchKey - leftKey) / (rightKey - leftKey); return SkScalarInterp(values[right-1], values[right], fract); } diff --git a/gfx/skia/skia/src/core/SkScalerContext.cpp b/gfx/skia/skia/src/core/SkScalerContext.cpp @@ -15,7 +15,6 @@ #include "include/core/SkMaskFilter.h" #include "include/core/SkPaint.h" #include "include/core/SkPath.h" -#include "include/core/SkPathBuilder.h" #include "include/core/SkPathEffect.h" #include "include/core/SkPixmap.h" #include "include/core/SkStrokeRec.h" @@ -32,7 +31,7 @@ #include "src/core/SkBlitter_A8.h" #include "src/core/SkColorData.h" #include "src/core/SkDescriptor.h" -#include "src/core/SkDraw.h" +#include "src/core/SkDrawBase.h" #include "src/core/SkFontPriv.h" #include "src/core/SkGlyph.h" #include "src/core/SkMaskFilterBase.h" @@ -40,7 +39,6 @@ #include "src/core/SkRasterClip.h" #include "src/core/SkTextFormatParams.h" #include "src/core/SkWriteBuffer.h" -#include "src/utils/SkFloatUtils.h" #include "src/utils/SkMatrix22.h" #include <algorithm> @@ -272,7 +270,7 @@ SkGlyph SkScalerContext::internalMakeGlyph(SkPackedGlyphID packedID, SkMask::For if (mx.computeFromPath || (fGenerateImageFromPath && !mx.neverRequestPath)) { SkDEBUGCODE(glyph.fAdvancesBoundsFormatAndInitialPathDone = true;) - this->internalGetPath(glyph, alloc, std::move(mx.generatedPath)); + this->internalGetPath(glyph, alloc); const SkPath* devPath = glyph.path(); if (devPath) { const bool doVert = SkToBool(fRec.fFlags & SkScalerContext::kLCD_Vertical_Flag); @@ -298,8 +296,11 @@ SkGlyph SkScalerContext::internalMakeGlyph(SkPackedGlyphID packedID, SkMask::For // only want the bounds from the filter SkMask src(nullptr, glyph.iRect(), glyph.rowBytes(), glyph.maskFormat()); SkMaskBuilder dst; + SkMatrix matrix; - if (as_MFB(fMaskFilter)->filterMask(&dst, src, fRec.getMatrixFrom2x2(), nullptr)) { + fRec.getMatrixFrom2x2(&matrix); + + if (as_MFB(fMaskFilter)->filterMask(&dst, src, matrix, nullptr)) { if (dst.fBounds.isEmpty()) { zeroBounds(glyph); return glyph; @@ -390,7 +391,7 @@ static void pack4xHToMask(const SkPixmap& src, SkMaskBuilder& dst, dstPDelta = dstPB; } - const uint8_t* srcP = SkTAddOffset<const uint8_t>(src.addr(), y * src.rowBytes()); + const uint8_t* srcP = src.addr8(0, y); // TODO: this fir filter implementation is straight forward, but slow. // It should be possible to make it much faster. @@ -490,20 +491,6 @@ static void packA8ToA1(SkMaskBuilder& dstMask, const uint8_t* src, size_t srcRB) } } -void SkScalerContext::generateImageFromPath(const SkGlyph& glyph, void* imageBuffer) { - SkASSERT(glyph.setPathHasBeenCalled()); - const SkPath* devPath = glyph.path(); - SkASSERT_RELEASE(devPath); - SkMaskBuilder mask(static_cast<uint8_t*>(imageBuffer), - glyph.iRect(), glyph.rowBytes(), glyph.maskFormat()); - 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); - const bool hairline = glyph.pathIsHairline(); - GenerateImageFromPath(mask, *devPath, fPreBlend, doBGR, doVert, a8LCD, hairline); -} - void SkScalerContext::GenerateImageFromPath( SkMaskBuilder& dstMask, const SkPath& path, const SkMaskGamma::PreBlend& maskPreBlend, const bool doBGR, const bool verticalLCD, const bool a8FromLCD, const bool hairline) @@ -551,10 +538,7 @@ void SkScalerContext::GenerateImageFromPath( rec.setStrokeStyle(1.0f, false); rec.setStrokeParams(SkPaint::kButt_Cap, SkPaint::kRound_Join, 0.0f); } - - SkPathBuilder builder; - if (rec.needToApply() && rec.applyToPath(&builder, path)) { - strokePath = builder.detach(); + if (rec.needToApply() && rec.applyToPath(&strokePath, path)) { pathToUse = &strokePath; paint.setStyle(SkPaint::kFill_Style); } @@ -577,7 +561,7 @@ void SkScalerContext::GenerateImageFromPath( } sk_bzero(dst.writable_addr(), dst.computeByteSize()); - skcpu::Draw draw; + SkDrawBase draw; draw.fBlitterChooser = SkA8Blitter_Choose; draw.fDst = dst; draw.fRC = &clip; @@ -659,9 +643,10 @@ void SkScalerContext::getImage(const SkGlyph& origGlyph) { SkMaskBuilder srcMask; SkAutoMaskFreeImage srcMaskOwnedImage(nullptr); + SkMatrix m; + fRec.getMatrixFrom2x2(&m); - if (as_MFB(fMaskFilter)->filterMask(&srcMask, unfilteredGlyph->mask(), - fRec.getMatrixFrom2x2(), nullptr)) { + if (as_MFB(fMaskFilter)->filterMask(&srcMask, unfilteredGlyph->mask(), m, nullptr)) { // Filter succeeded; srcMask.fImage was allocated. srcMaskOwnedImage.reset(srcMask.image()); } else if (unfilteredGlyph->fImage == tmpGlyphImageStorage.get()) { @@ -754,7 +739,7 @@ void SkScalerContext::getImage(const SkGlyph& origGlyph) { } void SkScalerContext::getPath(SkGlyph& glyph, SkArenaAlloc* alloc) { - this->internalGetPath(glyph, alloc, std::nullopt); + this->internalGetPath(glyph, alloc); } sk_sp<SkDrawable> SkScalerContext::getDrawable(SkGlyph& glyph) { @@ -772,108 +757,115 @@ void SkScalerContext::getFontMetrics(SkFontMetrics* fm) { /////////////////////////////////////////////////////////////////////////////// -void SkScalerContext::internalGetPath(SkGlyph& glyph, SkArenaAlloc* alloc, - std::optional<GeneratedPath>&& generatedPath) { +void SkScalerContext::internalGetPath(SkGlyph& glyph, SkArenaAlloc* alloc) { SkASSERT(glyph.fAdvancesBoundsFormatAndInitialPathDone); if (glyph.setPathHasBeenCalled()) { return; } - if (!generatedPath) { - generatedPath = this->generatePath(glyph); - } - if (!generatedPath) { - glyph.setPath(alloc, (SkPath*)nullptr, false, false); + SkPath path; + SkPath devPath; + bool hairline = false; + bool pathModified = false; + + SkPackedGlyphID glyphID = glyph.getPackedID(); + if (!generatePath(glyph, &path, &pathModified)) { + glyph.setPath(alloc, (SkPath*)nullptr, hairline, pathModified); return; } - SkPath path = std::move(generatedPath->path); - bool pathModified = std::move(generatedPath->modified); - if (fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) { - SkPackedGlyphID glyphID = glyph.getPackedID(); SkFixed dx = glyphID.getSubXFixed(); SkFixed dy = glyphID.getSubYFixed(); if (dx | dy) { pathModified = true; - path = path.makeOffset(SkFixedToScalar(dx), SkFixedToScalar(dy)); + path.offset(SkFixedToScalar(dx), SkFixedToScalar(dy)); } } if (fRec.fFrameWidth < 0 && fPathEffect == nullptr) { - glyph.setPath(alloc, &path, false, pathModified); - return; - } + devPath.swap(path); + } else { + pathModified = true; // It could still end up the same, but it's probably going to change. + + // need the path in user-space, with only the point-size applied + // so that our stroking and effects will operate the same way they + // would if the user had extracted the path themself, and then + // called drawPath + SkPath localPath; + SkMatrix matrix; + SkMatrix inverse; + + fRec.getMatrixFrom2x2(&matrix); + if (!matrix.invert(&inverse)) { + glyph.setPath(alloc, &devPath, hairline, pathModified); + } + path.transform(inverse, &localPath); + // now localPath is only affected by the paint settings, and not the canvas matrix - pathModified = true; // It could still end up the same, but it's probably going to change. + SkStrokeRec rec(SkStrokeRec::kFill_InitStyle); - // need the path in user-space, with only the point-size applied - // so that our stroking and effects will operate the same way they - // would if the user had extracted the path themself, and then - // called drawPath - SkMatrix matrix = fRec.getMatrixFrom2x2(); + if (fRec.fFrameWidth >= 0) { + rec.setStrokeStyle(fRec.fFrameWidth, + SkToBool(fRec.fFlags & kFrameAndFill_Flag)); + // glyphs are always closed contours, so cap type is ignored, + // so we just pass something. + rec.setStrokeParams((SkPaint::Cap)fRec.fStrokeCap, + (SkPaint::Join)fRec.fStrokeJoin, + fRec.fMiterLimit); + } - // We apply the inverse, so that localPath is only affected by the paint settings - // and not the canvas matrix. - auto inverse = matrix.invert(); - if (!inverse) { - SkPath empty; - glyph.setPath(alloc, &empty, false, pathModified); - return; - } - auto localPath = path.makeTransform(*inverse); - - SkStrokeRec rec(SkStrokeRec::kFill_InitStyle); - - if (fRec.fFrameWidth >= 0) { - rec.setStrokeStyle(fRec.fFrameWidth, - SkToBool(fRec.fFlags & kFrameAndFill_Flag)); - // glyphs are always closed contours, so cap type is ignored, - // so we just pass something. - rec.setStrokeParams((SkPaint::Cap)fRec.fStrokeCap, - (SkPaint::Join)fRec.fStrokeJoin, - fRec.fMiterLimit); - } + if (fPathEffect) { + SkPath effectPath; + if (fPathEffect->filterPath(&effectPath, localPath, &rec, nullptr, matrix)) { + localPath.swap(effectPath); + } + } - if (fPathEffect) { - SkPathBuilder builder; - if (fPathEffect->filterPath(&builder, localPath, &rec, nullptr, matrix)) { - localPath = builder.detach(); + if (rec.needToApply()) { + SkPath strokePath; + if (rec.applyToPath(&strokePath, localPath)) { + localPath.swap(strokePath); + } } - } - if (rec.needToApply()) { - SkPathBuilder builder; - if (rec.applyToPath(&builder, localPath)) { - localPath = builder.detach(); + // The path effect may have modified 'rec', so wait to here to check hairline status. + if (rec.isHairlineStyle()) { + hairline = true; } - } - auto devPath = localPath.makeTransform(matrix); - glyph.setPath(alloc, &devPath, rec.isHairlineStyle(), pathModified); + localPath.transform(matrix, &devPath); + } + glyph.setPath(alloc, &devPath, hairline, pathModified); } -SkMatrix SkScalerContextRec::getMatrixFrom2x2() const { - return SkMatrix::MakeAll(fPost2x2[0][0], fPost2x2[0][1], 0, - fPost2x2[1][0], fPost2x2[1][1], 0, - 0, 0, 1); +void SkScalerContextRec::getMatrixFrom2x2(SkMatrix* dst) const { + dst->setAll(fPost2x2[0][0], fPost2x2[0][1], 0, + fPost2x2[1][0], fPost2x2[1][1], 0, + 0, 0, 1); } -SkMatrix SkScalerContextRec::getLocalMatrix() const { - return SkFontPriv::MakeTextMatrix(fTextSize, fPreScaleX, fPreSkewX); +void SkScalerContextRec::getLocalMatrix(SkMatrix* m) const { + *m = SkFontPriv::MakeTextMatrix(fTextSize, fPreScaleX, fPreSkewX); } -SkMatrix SkScalerContextRec::getSingleMatrix() const { - return this->getLocalMatrix().postConcat(this->getMatrixFrom2x2()); +void SkScalerContextRec::getSingleMatrix(SkMatrix* m) const { + this->getLocalMatrix(m); + + // now concat the device matrix + SkMatrix deviceMatrix; + this->getMatrixFrom2x2(&deviceMatrix); + m->postConcat(deviceMatrix); } bool SkScalerContextRec::computeMatrices(PreMatrixScale preMatrixScale, SkVector* s, SkMatrix* sA, - SkMatrix* GsA, SkMatrix* G_inv, SkMatrix* A_out) const + SkMatrix* GsA, SkMatrix* G_inv, SkMatrix* A_out) { // A is the 'total' matrix. - const SkMatrix A = this->getSingleMatrix(); + SkMatrix A; + this->getSingleMatrix(&A); // The caller may find the 'total' matrix useful when dealing directly with EM sizes. if (A_out) { @@ -886,7 +878,8 @@ bool SkScalerContextRec::computeMatrices(PreMatrixScale preMatrixScale, SkVector if (skewedOrFlipped) { // QR by Givens rotations. G is Q^T and GA is R. G is rotational (no reflections). // h is where A maps the horizontal baseline. - SkPoint h = A.mapPoint({SK_Scalar1, 0}); + SkPoint h = SkPoint::Make(SK_Scalar1, 0); + A.mapPoints(&h, 1); // G is the Givens Matrix for A (rotational matrix where GA[0][1] == 0). SkMatrix G; @@ -1020,10 +1013,10 @@ void SkScalerContextRec::useStrokeForFakeBold() { } fFlags &= ~SkScalerContext::kEmbolden_Flag; - SkScalar fakeBoldScale = SkFloatInterpFunc(fTextSize, - kStdFakeBoldInterpKeys, - kStdFakeBoldInterpValues, - kStdFakeBoldInterpLength); + SkScalar fakeBoldScale = SkScalarInterpFunc(fTextSize, + kStdFakeBoldInterpKeys, + kStdFakeBoldInterpValues, + kStdFakeBoldInterpLength); SkScalar extra = fTextSize * fakeBoldScale; if (fFrameWidth >= 0) { @@ -1308,8 +1301,9 @@ std::unique_ptr<SkScalerContext> SkScalerContext::MakeEmpty( return {glyph.maskFormat()}; } void generateImage(const SkGlyph&, void*) override {} - std::optional<GeneratedPath> generatePath(const SkGlyph& glyph) override { - return {}; + bool generatePath(const SkGlyph& glyph, SkPath* path, bool* modified) override { + path->reset(); + return false; } void generateFontMetrics(SkFontMetrics* metrics) override { if (metrics) { diff --git a/gfx/skia/skia/src/core/SkScalerContext.h b/gfx/skia/skia/src/core/SkScalerContext.h @@ -12,7 +12,6 @@ #include "include/core/SkFourByteTag.h" #include "include/core/SkMatrix.h" #include "include/core/SkPaint.h" -#include "include/core/SkPath.h" #include "include/core/SkRect.h" #include "include/core/SkRefCnt.h" #include "include/core/SkScalar.h" @@ -31,7 +30,6 @@ #include <cstddef> #include <cstdint> #include <memory> -#include <optional> class SkArenaAlloc; class SkAutoDescriptor; @@ -39,6 +37,7 @@ class SkDescriptor; class SkDrawable; class SkFont; class SkMaskFilter; +class SkPath; class SkPathEffect; enum class SkFontHinting; struct SkFontMetrics; @@ -168,9 +167,9 @@ public: return msg; } - SkMatrix getMatrixFrom2x2() const; - SkMatrix getLocalMatrix() const; - SkMatrix getSingleMatrix() const; + void getMatrixFrom2x2(SkMatrix*) const; + void getLocalMatrix(SkMatrix*) const; + void getSingleMatrix(SkMatrix*) const; /** The kind of scale which will be applied by the underlying port (pre-matrix). */ enum class PreMatrixScale { @@ -215,7 +214,7 @@ public: SkVector* scale, SkMatrix* remaining, SkMatrix* remainingWithoutRotation = nullptr, SkMatrix* remainingRotation = nullptr, - SkMatrix* total = nullptr) const; + SkMatrix* total = nullptr); SkAxisAlignment computeAxisAlignmentForHText() const; @@ -382,12 +381,8 @@ public: SkScalerContextEffects* effects); protected: - const SkScalerContextRec fRec; + SkScalerContextRec fRec; - struct GeneratedPath { - SkPath path; - bool modified; - }; struct GlyphMetrics { SkVector advance; SkRect bounds; @@ -395,7 +390,7 @@ protected: uint16_t extraBits; bool neverRequestPath; bool computeFromPath; - std::optional<GeneratedPath> generatedPath; + GlyphMetrics(SkMask::Format format) : advance{0, 0} , bounds{0, 0, 0, 0} @@ -403,7 +398,6 @@ protected: , extraBits(0) , neverRequestPath(false) , computeFromPath(false) - , generatedPath{std::nullopt} {} }; @@ -428,12 +422,13 @@ protected: static void GenerateImageFromPath( SkMaskBuilder& dst, const SkPath& path, const SkMaskGamma::PreBlend& maskPreBlend, bool doBGR, bool verticalLCD, bool a8FromLCD, bool hairline); - void generateImageFromPath(const SkGlyph& glyph, void* imageBuffer); - /** Return the glyph's outline, or if the glyph cannot be converted to one, return {}. + /** Sets the passed path to the glyph outline. + * If this cannot be done the path is set to empty; * Does not apply subpixel positioning to the path. + * @return false if this glyph does not have any path. */ - [[nodiscard]] virtual std::optional<GeneratedPath> generatePath(const SkGlyph&) = 0; + [[nodiscard]] virtual bool generatePath(const SkGlyph&, SkPath*, bool* modified) = 0; /** Returns the drawable for the glyph (if any). * @@ -448,6 +443,9 @@ protected: /** Retrieves font metrics. */ virtual void generateFontMetrics(SkFontMetrics*) = 0; + void forceGenerateImageFromPath() { fGenerateImageFromPath = true; } + void forceOffGenerateImageFromPath() { fGenerateImageFromPath = false; } + private: friend class PathText; // For debug purposes friend class PathTextBench; // For debug purposes @@ -469,9 +467,9 @@ private: // if this is set, we draw the image from a path, rather than // calling generateImage. - const bool fGenerateImageFromPath; + bool fGenerateImageFromPath; - void internalGetPath(SkGlyph&, SkArenaAlloc*, std::optional<GeneratedPath>&&); + void internalGetPath(SkGlyph&, SkArenaAlloc*); SkGlyph internalMakeGlyph(SkPackedGlyphID, SkMask::Format, SkArenaAlloc*); protected: diff --git a/gfx/skia/skia/src/core/SkScan.h b/gfx/skia/skia/src/core/SkScan.h @@ -15,7 +15,6 @@ class SkBlitter; class SkPath; -struct SkPathRaw; class SkRasterClip; class SkRegion; @@ -35,6 +34,8 @@ public: typedef void (*HairRgnProc)(const SkPoint[], int count, const SkRegion*, SkBlitter*); typedef void (*HairRCProc)(const SkPoint[], int count, const SkRasterClip&, SkBlitter*); + static void FillPath(const SkPath&, const SkIRect&, SkBlitter*); + // Paths of a certain size cannot be anti-aliased unless externally tiled (handled by SkDraw). // SkBitmapDevice automatically tiles, SkAAClip does not so SkRasterClipStack converts AA clips // to BW clips if that's the case. SkRegion uses this to know when to tile and union smaller @@ -49,11 +50,8 @@ public: static void FillRect(const SkRect&, const SkRasterClip&, SkBlitter*); static void AntiFillRect(const SkRect&, const SkRasterClip&, SkBlitter*); static void AntiFillXRect(const SkXRect&, const SkRasterClip&, SkBlitter*); - - static void FillPath(const SkPathRaw&, const SkRasterClip&, SkBlitter*); - static void FillPath(const SkPathRaw&, const SkRegion& clip, SkBlitter*); - static void AntiFillPath(const SkPathRaw&, const SkRasterClip&, SkBlitter*); - + static void FillPath(const SkPath&, const SkRasterClip&, SkBlitter*); + static void AntiFillPath(const SkPath&, const SkRasterClip&, SkBlitter*); static void FrameRect(const SkRect&, const SkPoint& strokeSize, const SkRasterClip&, SkBlitter*); static void AntiFrameRect(const SkRect&, const SkPoint& strokeSize, @@ -63,13 +61,15 @@ public: static void AntiHairLine(const SkPoint[], int count, const SkRasterClip&, SkBlitter*); static void HairRect(const SkRect&, const SkRasterClip&, SkBlitter*); static void AntiHairRect(const SkRect&, const SkRasterClip&, SkBlitter*); + static void HairPath(const SkPath&, const SkRasterClip&, SkBlitter*); + static void AntiHairPath(const SkPath&, const SkRasterClip&, SkBlitter*); + static void HairSquarePath(const SkPath&, const SkRasterClip&, SkBlitter*); + static void AntiHairSquarePath(const SkPath&, const SkRasterClip&, SkBlitter*); + static void HairRoundPath(const SkPath&, const SkRasterClip&, SkBlitter*); + static void AntiHairRoundPath(const SkPath&, const SkRasterClip&, SkBlitter*); - static void HairPath(const SkPathRaw&, const SkRasterClip&, SkBlitter*); - static void AntiHairPath(const SkPathRaw&, const SkRasterClip&, SkBlitter*); - static void HairSquarePath(const SkPathRaw&, const SkRasterClip&, SkBlitter*); - static void AntiHairSquarePath(const SkPathRaw&, const SkRasterClip&, SkBlitter*); - static void HairRoundPath(const SkPathRaw&, const SkRasterClip&, SkBlitter*); - static void AntiHairRoundPath(const SkPathRaw&, const SkRasterClip&, SkBlitter*); + // Needed by SkRegion::setPath + static void FillPath(const SkPath&, const SkRegion& clip, SkBlitter*); private: friend class SkAAClip; @@ -80,14 +80,14 @@ private: static void FillRect(const SkRect&, const SkRegion* clip, SkBlitter*); static void AntiFillRect(const SkRect&, const SkRegion* clip, SkBlitter*); static void AntiFillXRect(const SkXRect&, const SkRegion*, SkBlitter*); - static void AntiFillPath(const SkPathRaw&, const SkRegion& clip, SkBlitter*, bool forceRLE); + static void AntiFillPath(const SkPath&, const SkRegion& clip, SkBlitter*, bool forceRLE); static void FillTriangle(const SkPoint pts[], const SkRegion*, SkBlitter*); static void AntiFrameRect(const SkRect&, const SkPoint& strokeSize, const SkRegion*, SkBlitter*); static void HairLineRgn(const SkPoint[], int count, const SkRegion*, SkBlitter*); static void AntiHairLineRgn(const SkPoint[], int count, const SkRegion*, SkBlitter*); - static void AAAFillPath(const SkPathRaw&, SkBlitter* blitter, const SkIRect& pathIR, + static void AAAFillPath(const SkPath& path, SkBlitter* blitter, const SkIRect& pathIR, const SkIRect& clipBounds, bool forceRLE); }; diff --git a/gfx/skia/skia/src/core/SkScanPriv.h b/gfx/skia/skia/src/core/SkScanPriv.h @@ -33,6 +33,10 @@ private: const SkIRect* fClipRect; }; +void sk_fill_path(const SkPath& path, const SkIRect& clipRect, + SkBlitter* blitter, int start_y, int stop_y, int shiftEdgesUp, + bool pathContainedInClip); + // blit the rects above and below avoid, clipped to clip void sk_blit_above(SkBlitter*, const SkIRect& avoid, const SkRegion& clip); void sk_blit_below(SkBlitter*, const SkIRect& avoid, const SkRegion& clip); diff --git a/gfx/skia/skia/src/core/SkScan_AAAPath.cpp b/gfx/skia/skia/src/core/SkScan_AAAPath.cpp @@ -20,9 +20,9 @@ #include "src/core/SkAlphaRuns.h" #include "src/core/SkAnalyticEdge.h" #include "src/core/SkBlitter.h" +#include "src/core/SkEdge.h" #include "src/core/SkEdgeBuilder.h" #include "src/core/SkMask.h" -#include "src/core/SkPathRaw.h" #include "src/core/SkScan.h" #include "src/core/SkScanPriv.h" @@ -542,7 +542,7 @@ static SkAlpha trapezoid_to_alpha(SkFixed l1, SkFixed l2) { static SkAlpha partial_triangle_to_alpha(SkFixed a, SkFixed b) { SkASSERT(a <= SK_Fixed1); #if 0 - // TODO(mtklein): skbug.com/40040159 + // TODO(mtklein): skia:8877 SkASSERT(b <= SK_Fixed1); #endif @@ -551,7 +551,7 @@ static SkAlpha partial_triangle_to_alpha(SkFixed a, SkFixed b) { SkFixed area = (a >> 11) * (a >> 11) * (b >> 11); #if 0 - // TODO(mtklein): skbug.com/40040159 + // TODO(mtklein): skia:8877 return SkTo<SkAlpha>(area >> 8); #else return SkTo<SkAlpha>((area >> 8) & 0xFF); @@ -990,18 +990,18 @@ static void validate_sort(const SkAnalyticEdge* edge) { // relatively large compared to fQDDx/QCDDx and fQDDy/fCDDy static bool is_smooth_enough(SkAnalyticEdge* thisEdge, SkAnalyticEdge* nextEdge, int stop_y) { if (thisEdge->fCurveCount < 0) { - const auto cEdge = static_cast<SkAnalyticCubicEdge*>(thisEdge); - int ddshift = cEdge->fCurveShift; - return SkAbs32(cEdge->fCDx) >> 1 >= SkAbs32(cEdge->fCDDx) >> ddshift && - SkAbs32(cEdge->fCDy) >> 1 >= SkAbs32(cEdge->fCDDy) >> ddshift && + const SkCubicEdge& cEdge = static_cast<SkAnalyticCubicEdge*>(thisEdge)->fCEdge; + int ddshift = cEdge.fCurveShift; + return SkAbs32(cEdge.fCDx) >> 1 >= SkAbs32(cEdge.fCDDx) >> ddshift && + SkAbs32(cEdge.fCDy) >> 1 >= SkAbs32(cEdge.fCDDy) >> ddshift && // current Dy is (fCDy - (fCDDy >> ddshift)) >> dshift - (cEdge->fCDy - (cEdge->fCDDy >> ddshift)) >> cEdge->fCubicDShift >= SK_Fixed1; + (cEdge.fCDy - (cEdge.fCDDy >> ddshift)) >> cEdge.fCubicDShift >= SK_Fixed1; } else if (thisEdge->fCurveCount > 0) { - const auto qEdge = static_cast<SkAnalyticQuadraticEdge*>(thisEdge); - return SkAbs32(qEdge->fQDx) >> 1 >= SkAbs32(qEdge->fQDDx) && - SkAbs32(qEdge->fQDy) >> 1 >= SkAbs32(qEdge->fQDDy) && + const SkQuadraticEdge& qEdge = static_cast<SkAnalyticQuadraticEdge*>(thisEdge)->fQEdge; + return SkAbs32(qEdge.fQDx) >> 1 >= SkAbs32(qEdge.fQDDx) && + SkAbs32(qEdge.fQDy) >> 1 >= SkAbs32(qEdge.fQDDy) && // current Dy is (fQDy - fQDDy) >> shift - (qEdge->fQDy - qEdge->fQDDy) >> qEdge->fCurveShift >= SK_Fixed1; + (qEdge.fQDy - qEdge.fQDDy) >> qEdge.fCurveShift >= SK_Fixed1; } // DDx should be small and Dy should be large return SkAbs32(Sk32_sat_sub(nextEdge->fDX, thisEdge->fDX)) <= SK_Fixed1 && @@ -1601,7 +1601,7 @@ static void aaa_walk_edges(SkAnalyticEdge* prevHead, } } -static void aaa_fill_path(const SkPathRaw& path, +static void aaa_fill_path(const SkPath& path, const SkIRect& clipRect, AdditiveBlitter* blitter, int start_y, @@ -1675,7 +1675,7 @@ static void aaa_fill_path(const SkPathRaw& path, // If we're using mask, then we have to limit the bound within the path bounds. // Otherwise, the edge drift may access an invalid address inside the mask. SkIRect ir; - path.bounds().roundOut(&ir); + path.getBounds().roundOut(&ir); leftBound = std::max(leftBound, SkIntToFixed(ir.fLeft)); rightBound = std::min(rightBound, SkIntToFixed(ir.fRight)); } @@ -1686,11 +1686,11 @@ static void aaa_fill_path(const SkPathRaw& path, } else { // We skip intersection computation if there are many points which probably already // give us enough fractional scan lines. - bool skipIntersect = path.points().size() > SkToSizeT((stop_y - start_y) * 2); + bool skipIntersect = path.countPoints() > (stop_y - start_y) * 2; aaa_walk_edges(&headEdge, &tailEdge, - path.fillType(), + path.getFillType(), blitter, start_y, stop_y, @@ -1704,24 +1704,24 @@ static void aaa_fill_path(const SkPathRaw& path, // Check if the path is a rect and fat enough after clipping; if so, blit it. static inline bool try_blit_fat_anti_rect(SkBlitter* blitter, - const SkPathRaw& raw, + const SkPath& path, const SkIRect& clip) { - std::optional<SkRect> rect = raw.isRect(); - if (!rect) { - return false; + SkRect rect; + if (!path.isRect(&rect)) { + return false; // not rect } - if (!rect->intersect(SkRect::Make(clip))) { + if (!rect.intersect(SkRect::Make(clip))) { return true; // The intersection is empty. Hence consider it done. } - SkIRect bounds = rect->roundOut(); + SkIRect bounds = rect.roundOut(); if (bounds.width() < 3) { return false; // not fat } - blitter->blitFatAntiRect(*rect); + blitter->blitFatAntiRect(rect); return true; } -void SkScan::AAAFillPath(const SkPathRaw& path, +void SkScan::AAAFillPath(const SkPath& path, SkBlitter* blitter, const SkIRect& ir, const SkIRect& clipBounds, diff --git a/gfx/skia/skia/src/core/SkScan_AntiPath.cpp b/gfx/skia/skia/src/core/SkScan_AntiPath.cpp @@ -13,7 +13,6 @@ #include "include/private/base/SkMath.h" #include "src/core/SkAAClip.h" #include "src/core/SkBlitter.h" -#include "src/core/SkPathRaw.h" #include "src/core/SkRasterClip.h" #include "src/core/SkScan.h" #include "src/core/SkScanPriv.h" @@ -56,14 +55,14 @@ static int rect_overflows_short_shift(SkIRect rect, int shift) { overflows_short_shift(rect.fBottom, shift); } -void SkScan::AntiFillPath(const SkPathRaw& path, const SkRegion& origClip, +void SkScan::AntiFillPath(const SkPath& path, const SkRegion& origClip, SkBlitter* blitter, bool forceRLE) { if (origClip.isEmpty()) { return; } const bool isInverse = path.isInverseFillType(); - SkIRect ir = safeRoundOut(path.bounds()); + SkIRect ir = safeRoundOut(path.getBounds()); if (ir.isEmpty()) { if (isInverse) { blitter->blitRegion(origClip); @@ -136,20 +135,36 @@ void SkScan::AntiFillPath(const SkPathRaw& path, const SkRegion& origClip, /////////////////////////////////////////////////////////////////////////////// -void SkScan::AntiFillPath(const SkPathRaw& raw, const SkRasterClip& clip, SkBlitter* blitter) { - SkASSERT(raw.bounds().isFinite()); - if (clip.isEmpty()) { +void SkScan::FillPath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) { + if (clip.isEmpty() || !path.isFinite()) { return; } if (clip.isBW()) { - AntiFillPath(raw, clip.bwRgn(), blitter, false); + FillPath(path, clip.bwRgn(), blitter); } else { SkRegion tmp; SkAAClipBlitter aaBlitter; tmp.setRect(clip.getBounds()); aaBlitter.init(blitter, &clip.aaRgn()); - AntiFillPath(raw, tmp, &aaBlitter, true); // SkAAClipBlitter can blitMask, why forceRLE? + SkScan::FillPath(path, tmp, &aaBlitter); + } +} + +void SkScan::AntiFillPath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) { + if (clip.isEmpty() || !path.isFinite()) { + return; + } + + if (clip.isBW()) { + AntiFillPath(path, clip.bwRgn(), blitter, false); + } else { + SkRegion tmp; + SkAAClipBlitter aaBlitter; + + tmp.setRect(clip.getBounds()); + aaBlitter.init(blitter, &clip.aaRgn()); + AntiFillPath(path, tmp, &aaBlitter, true); // SkAAClipBlitter can blitMask, why forceRLE? } } diff --git a/gfx/skia/skia/src/core/SkScan_Antihair.cpp b/gfx/skia/skia/src/core/SkScan_Antihair.cpp @@ -940,16 +940,6 @@ void SkScan::AntiFrameRect(const SkRect& r, const SkPoint& strokeSize, SkScalar rx = SkScalarHalf(strokeSize.fX); SkScalar ry = SkScalarHalf(strokeSize.fY); - // If we're empty on either axis, we remove the outset amount, to be sure - // we stroke the same way a polygon would (i.e. it would just see a "line" - // and not extend it for the miter join). - if (r.width() == 0) { - ry = 0; - } - if (r.height() == 0) { - rx = 0; - } - // outset by the radius FDot8 outerL = SkScalarToFDot8(r.fLeft - rx); FDot8 outerT = SkScalarToFDot8(r.fTop - ry); @@ -960,7 +950,6 @@ void SkScan::AntiFrameRect(const SkRect& r, const SkPoint& strokeSize, // set outer to the outer rect of the outer section outer.setLTRB(FDot8Floor(outerL), FDot8Floor(outerT), FDot8Ceil(outerR), FDot8Ceil(outerB)); - SkBlitterClipper clipper; if (clip) { if (clip->quickReject(outer)) { diff --git a/gfx/skia/skia/src/core/SkScan_Hairline.cpp b/gfx/skia/skia/src/core/SkScan_Hairline.cpp @@ -5,10 +5,8 @@ * found in the LICENSE file. */ -#include "include/core/SkImageInfo.h" #include "include/core/SkPaint.h" #include "include/core/SkPath.h" -#include "include/core/SkPixmap.h" #include "include/core/SkPoint.h" #include "include/core/SkRect.h" #include "include/core/SkRegion.h" @@ -34,64 +32,27 @@ #include <array> #include <cstdint> #include <cstring> -#include <optional> - -#define DISPATCH_LOOP() \ - const auto pm = direct->pm; \ - const auto value = direct->value; \ - switch (pm.info().bytesPerPixel()) { \ - case 1: DIRECT_BLIT_LOOP(writable_addr8); break; \ - case 2: DIRECT_BLIT_LOOP(writable_addr16); break; \ - case 4: DIRECT_BLIT_LOOP(writable_addr32); break; \ - case 8: DIRECT_BLIT_LOOP(writable_addr64); break; \ - } - -#define DIRECT_BLIT_LOOP(writable_proc) \ - do { \ - *pm.writable_proc(x, fy >> 16) = value; \ - fy += dy; \ - } while (++x < stopx) static void horiline(int x, int stopx, SkFixed fy, SkFixed dy, SkBlitter* blitter) { SkASSERT(x < stopx); - if (auto direct = blitter->canDirectBlit()) { - DISPATCH_LOOP() - } else { - do { - blitter->blitH(x, fy >> 16, 1); - fy += dy; - } while (++x < stopx); - } + do { + blitter->blitH(x, fy >> 16, 1); + fy += dy; + } while (++x < stopx); } -#undef DIRECT_BLIT_LOOP -#define DIRECT_BLIT_LOOP(writable_proc) \ - do { \ - *pm.writable_proc(fx >> 16, y) = value; \ - fx += dx; \ - } while (++y < stopy) - - static void vertline(int y, int stopy, SkFixed fx, SkFixed dx, SkBlitter* blitter) { SkASSERT(y < stopy); - if (auto direct = blitter->canDirectBlit()) { - DISPATCH_LOOP() - } else { - do { - blitter->blitH(fx >> 16, y, 1); - fx += dx; - } while (++y < stopy); - } + do { + blitter->blitH(fx >> 16, y, 1); + fx += dx; + } while (++y < stopy); } -#undef DIRECT_BLIT_LOOP - -////////////////////////////////////////////////////// - #ifdef SK_DEBUG static bool canConvertFDot6ToFixed(SkFDot6 x) { const int maxDot6 = SK_MaxS32 >> (16 - 6); @@ -504,31 +465,26 @@ static int compute_quad_level(const SkPoint pts[3]) { return level; } -template <typename T> bool optional_eq(std::optional<T> opt, T other) { - return opt.has_value() && (opt.value() == other); -} - /* Extend the points in the direction of the starting or ending tangent by 1/2 unit to account for a round or square cap. If there's no distance between the end point and the control point, use the next control point to create a tangent. If the curve is degenerate, move the cap out 1/2 unit horizontally. */ template <SkPaint::Cap capStyle> -void extend_pts(std::optional<SkPathVerb> prevVerb, std::optional<SkPathVerb> nextVerb, - SkSpan<SkPoint> pts) { +void extend_pts(SkPath::Verb prevVerb, SkPath::Verb nextVerb, SkPoint* pts, int ptCount) { SkASSERT(SkPaint::kSquare_Cap == capStyle || SkPaint::kRound_Cap == capStyle); // The area of a circle is PI*R*R. For a unit circle, R=1/2, and the cap covers half of that. const SkScalar capOutset = SkPaint::kSquare_Cap == capStyle ? 0.5f : SK_ScalarPI / 8; - if (optional_eq(prevVerb, SkPathVerb::kMove)) { - SkPoint* first = pts.data(); + if (SkPath::kMove_Verb == prevVerb) { + SkPoint* first = pts; SkPoint* ctrl = first; - size_t controls = pts.size() - 1; + int controls = ptCount - 1; SkVector tangent; do { tangent = *first - *++ctrl; } while (tangent.isZero() && --controls > 0); if (tangent.isZero()) { tangent.set(1, 0); - controls = pts.size() - 1; // If all points are equal, move all but one + controls = ptCount - 1; // If all points are equal, move all but one } else { tangent.normalize(); } @@ -536,22 +492,20 @@ void extend_pts(std::optional<SkPathVerb> prevVerb, std::optional<SkPathVerb> ne first->fX += tangent.fX * capOutset; first->fY += tangent.fY * capOutset; ++first; - } while (++controls < pts.size()); + } while (++controls < ptCount); } - if (!nextVerb.has_value() || - nextVerb.value() == SkPathVerb::kMove || - nextVerb.value() == SkPathVerb::kClose) - { - SkPoint* last = &pts.back(); + if (SkPath::kMove_Verb == nextVerb || SkPath::kDone_Verb == nextVerb + || SkPath::kClose_Verb == nextVerb) { + SkPoint* last = &pts[ptCount - 1]; SkPoint* ctrl = last; - size_t controls = pts.size() - 1; + int controls = ptCount - 1; SkVector tangent; do { tangent = *last - *--ctrl; } while (tangent.isZero() && --controls > 0); if (tangent.isZero()) { tangent.set(-1, 0); - controls = pts.size() - 1; + controls = ptCount - 1; } else { tangent.normalize(); } @@ -559,14 +513,14 @@ void extend_pts(std::optional<SkPathVerb> prevVerb, std::optional<SkPathVerb> ne last->fX += tangent.fX * capOutset; last->fY += tangent.fY * capOutset; --last; - } while (++controls < pts.size()); + } while (++controls < ptCount); } } template <SkPaint::Cap capStyle> -void hair_path(const SkPathRaw& raw, const SkRasterClip& rclip, SkBlitter* blitter, +void hair_path(const SkPath& path, const SkRasterClip& rclip, SkBlitter* blitter, SkScan::HairRgnProc lineproc) { - if (raw.empty()) { + if (path.isEmpty()) { return; } @@ -578,7 +532,7 @@ void hair_path(const SkPathRaw& raw, const SkRasterClip& rclip, SkBlitter* blitt { const int capOut = SkPaint::kButt_Cap == capStyle ? 1 : 2; - const SkIRect ibounds = raw.bounds().roundOut().makeOutset(capOut, capOut); + const SkIRect ibounds = path.getBounds().roundOut().makeOutset(capOut, capOut); if (rclip.quickReject(ibounds)) { return; } @@ -624,42 +578,45 @@ void hair_path(const SkPathRaw& raw, const SkRasterClip& rclip, SkBlitter* blitt } } + SkPathPriv::RangeIter iter = SkPathPriv::Iterate(path).begin(); + SkPathPriv::RangeIter end = SkPathPriv::Iterate(path).end(); SkPoint pts[4], firstPt, lastPt; + SkPath::Verb prevVerb; SkAutoConicToQuads converter; - std::optional<SkPathVerb> prevVerb; - for (auto iter = raw.iter(); auto rec = iter.next(); ) { - const SkPoint* srcPts = rec->fPoints.data(); - SkPathVerb verb = rec->fVerb; - auto nextVerb = iter.peekNextVerb(); + if (SkPaint::kButt_Cap != capStyle) { + prevVerb = SkPath::kDone_Verb; + } + while (iter != end) { + auto [pathVerb, pathPts, w] = *iter++; + SkPath::Verb verb = (SkPath::Verb)pathVerb; + SkPath::Verb nextVerb = (iter != end) ? (SkPath::Verb)iter.peekVerb() : SkPath::kDone_Verb; + memcpy(pts, pathPts, SkPathPriv::PtsInIter(verb) * sizeof(SkPoint)); switch (verb) { - case SkPathVerb::kMove: - firstPt = lastPt = srcPts[0]; + case SkPath::kMove_Verb: + firstPt = lastPt = pts[0]; break; - case SkPathVerb::kLine: - std::copy(srcPts, srcPts + 2, pts); + case SkPath::kLine_Verb: if (SkPaint::kButt_Cap != capStyle) { - extend_pts<capStyle>(prevVerb, nextVerb, {pts, 2}); + extend_pts<capStyle>(prevVerb, nextVerb, pts, 2); } lineproc(pts, 2, clip, blitter); lastPt = pts[1]; break; - case SkPathVerb::kQuad: - std::copy(srcPts, srcPts + 3, pts); + case SkPath::kQuad_Verb: if (SkPaint::kButt_Cap != capStyle) { - extend_pts<capStyle>(prevVerb, nextVerb, {pts, 3}); + extend_pts<capStyle>(prevVerb, nextVerb, pts, 3); } hairquad(pts, clip, insetClip, outsetClip, blitter, compute_quad_level(pts), lineproc); lastPt = pts[2]; break; - case SkPathVerb::kConic: { - std::copy(srcPts, srcPts + 3, pts); + case SkPath::kConic_Verb: { if (SkPaint::kButt_Cap != capStyle) { - extend_pts<capStyle>(prevVerb, nextVerb, {pts, 3}); + extend_pts<capStyle>(prevVerb, nextVerb, pts, 3); } // how close should the quads be to the original conic? const SkScalar tol = SK_Scalar1 / 4; - const SkPoint* quadPts = converter.computeQuads(pts, rec->conicWeight(), tol); + const SkPoint* quadPts = converter.computeQuads(pts, *w, tol); for (int i = 0; i < converter.countQuads(); ++i) { int level = compute_quad_level(quadPts); hairquad(quadPts, clip, insetClip, outsetClip, blitter, level, lineproc); @@ -668,27 +625,28 @@ void hair_path(const SkPathRaw& raw, const SkRasterClip& rclip, SkBlitter* blitt lastPt = pts[2]; break; } - case SkPathVerb::kCubic: { - std::copy(srcPts, srcPts + 4, pts); + case SkPath::kCubic_Verb: { if (SkPaint::kButt_Cap != capStyle) { - extend_pts<capStyle>(prevVerb, nextVerb, {pts, 4}); + extend_pts<capStyle>(prevVerb, nextVerb, pts, 4); } haircubic(pts, clip, insetClip, outsetClip, blitter, kMaxCubicSubdivideLevel, lineproc); lastPt = pts[3]; } break; - case SkPathVerb::kClose: + case SkPath::kClose_Verb: pts[0] = lastPt; pts[1] = firstPt; - if (SkPaint::kButt_Cap != capStyle && optional_eq(prevVerb, SkPathVerb::kMove)) { + if (SkPaint::kButt_Cap != capStyle && prevVerb == SkPath::kMove_Verb) { // cap moveTo/close to match svg expectations for degenerate segments - extend_pts<capStyle>(prevVerb, nextVerb, {pts, 2}); + extend_pts<capStyle>(prevVerb, nextVerb, pts, 2); } lineproc(pts, 2, clip, blitter); break; + case SkPath::kDone_Verb: + break; } if (SkPaint::kButt_Cap != capStyle) { - if (optional_eq(prevVerb, SkPathVerb::kMove) && - verb >= SkPathVerb::kLine && verb <= SkPathVerb::kCubic) { + if (prevVerb == SkPath::kMove_Verb && + verb >= SkPath::kLine_Verb && verb <= SkPath::kCubic_Verb) { firstPt = pts[0]; // the curve moved the initial point, so close to it instead } prevVerb = verb; @@ -696,28 +654,28 @@ void hair_path(const SkPathRaw& raw, const SkRasterClip& rclip, SkBlitter* blitt } } -void SkScan::HairPath(const SkPathRaw& raw, const SkRasterClip& clip, SkBlitter* blitter) { - hair_path<SkPaint::kButt_Cap>(raw, clip, blitter, SkScan::HairLineRgn); +void SkScan::HairPath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) { + hair_path<SkPaint::kButt_Cap>(path, clip, blitter, SkScan::HairLineRgn); } -void SkScan::AntiHairPath(const SkPathRaw& raw, const SkRasterClip& clip, SkBlitter* blitter) { - hair_path<SkPaint::kButt_Cap>(raw, clip, blitter, SkScan::AntiHairLineRgn); +void SkScan::AntiHairPath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) { + hair_path<SkPaint::kButt_Cap>(path, clip, blitter, SkScan::AntiHairLineRgn); } -void SkScan::HairSquarePath(const SkPathRaw& raw, const SkRasterClip& clip, SkBlitter* blitter) { - hair_path<SkPaint::kSquare_Cap>(raw, clip, blitter, SkScan::HairLineRgn); +void SkScan::HairSquarePath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) { + hair_path<SkPaint::kSquare_Cap>(path, clip, blitter, SkScan::HairLineRgn); } -void SkScan::AntiHairSquarePath(const SkPathRaw& raw, const SkRasterClip& clip, SkBlitter* blitter) { - hair_path<SkPaint::kSquare_Cap>(raw, clip, blitter, SkScan::AntiHairLineRgn); +void SkScan::AntiHairSquarePath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) { + hair_path<SkPaint::kSquare_Cap>(path, clip, blitter, SkScan::AntiHairLineRgn); } -void SkScan::HairRoundPath(const SkPathRaw& raw, const SkRasterClip& clip, SkBlitter* blitter) { - hair_path<SkPaint::kRound_Cap>(raw, clip, blitter, SkScan::HairLineRgn); +void SkScan::HairRoundPath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) { + hair_path<SkPaint::kRound_Cap>(path, clip, blitter, SkScan::HairLineRgn); } -void SkScan::AntiHairRoundPath(const SkPathRaw& raw, const SkRasterClip& clip, SkBlitter* blitter) { - hair_path<SkPaint::kRound_Cap>(raw, clip, blitter, SkScan::AntiHairLineRgn); +void SkScan::AntiHairRoundPath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) { + hair_path<SkPaint::kRound_Cap>(path, clip, blitter, SkScan::AntiHairLineRgn); } /////////////////////////////////////////////////////////////////////////////// @@ -739,17 +697,6 @@ void SkScan::FrameRect(const SkRect& r, const SkPoint& strokeSize, outer.setLTRB(r.fLeft - rx, r.fTop - ry, r.fRight + rx, r.fBottom + ry); if (r.width() <= dx || r.height() <= dy) { - // If we're empty on either axis, we remove the outset amount, to be sure - // we stroke the same way a polygon would (i.e. it would just see a "line" - // and not extend it for the miter join). - if (r.width() == 0) { - outer.fTop = r.fTop; - outer.fBottom = r.fBottom; - } - if (r.height() == 0) { - outer.fLeft = r.fLeft; - outer.fRight = r.fRight; - } SkScan::FillRect(outer, clip, blitter); return; } @@ -774,7 +721,9 @@ void SkScan::HairLine(const SkPoint pts[], int count, const SkRasterClip& clip, } else { const SkRegion* clipRgn = nullptr; - const auto r = SkRect::BoundsOrEmpty({pts, count}).makeOutset(SK_ScalarHalf, SK_ScalarHalf); + SkRect r; + r.setBounds(pts, count); + r.outset(SK_ScalarHalf, SK_ScalarHalf); SkAAClipBlitterWrapper wrap; if (!clip.quickContains(r.roundOut())) { @@ -793,7 +742,8 @@ void SkScan::AntiHairLine(const SkPoint pts[], int count, const SkRasterClip& cl } else { const SkRegion* clipRgn = nullptr; - const auto r = SkRect::BoundsOrEmpty({pts, count}); + SkRect r; + r.setBounds(pts, count); SkAAClipBlitterWrapper wrap; if (!clip.quickContains(r.roundOut().makeOutset(1, 1))) { diff --git a/gfx/skia/skia/src/core/SkScan_Path.cpp b/gfx/skia/skia/src/core/SkScan_Path.cpp @@ -24,8 +24,6 @@ #include "src/core/SkEdge.h" #include "src/core/SkEdgeBuilder.h" #include "src/core/SkFDot6.h" -#include "src/core/SkPathPriv.h" -#include "src/core/SkPathRawShapes.h" #include "src/core/SkRasterClip.h" #include "src/core/SkRectPriv.h" #include "src/core/SkScan.h" @@ -155,8 +153,13 @@ static void walk_edges(SkEdge* prevHead, SkPathFillType fillType, SkFixed newX; if (currE->fLastY == curr_y) { // are we done with this edge? - if (currE->hasNextSegment()) { - if (currE->nextSegment()) { + if (currE->fCurveCount > 0) { + if (((SkQuadraticEdge*)currE)->updateQuadratic()) { + newX = currE->fX; + goto NEXT_X; + } + } else if (currE->fCurveCount < 0) { + if (((SkCubicEdge*)currE)->updateCubic()) { SkASSERT(currE->fFirstY == curr_y + 1); newX = currE->fX; @@ -166,7 +169,7 @@ static void walk_edges(SkEdge* prevHead, SkPathFillType fillType, remove_edge(currE); } else { SkASSERT(currE->fLastY > curr_y); - newX = currE->fX + currE->fDxDy; + newX = currE->fX + currE->fDX; currE->fX = newX; NEXT_X: if (newX < prevX) { // ripple currE backwards until it is x-sorted @@ -202,17 +205,21 @@ static void walk_edges(SkEdge* prevHead, SkPathFillType fillType, // return true if we're NOT done with this edge static bool update_edge(SkEdge* edge, int last_y) { SkASSERT(edge->fLastY >= last_y); - if (last_y != edge->fLastY) { - return true; - } - if (!edge->hasNextSegment()) { + if (last_y == edge->fLastY) { + if (edge->fCurveCount < 0) { + if (((SkCubicEdge*)edge)->updateCubic()) { + SkASSERT(edge->fFirstY == last_y + 1); + return true; + } + } else if (edge->fCurveCount > 0) { + if (((SkQuadraticEdge*)edge)->updateQuadratic()) { + SkASSERT(edge->fFirstY == last_y + 1); + return true; + } + } return false; } - if (edge->nextSegment()) { - SkASSERT(edge->fFirstY == last_y + 1); - return true; - } - return false; + return true; } // Unexpected conditions for which we need to return @@ -246,13 +253,13 @@ static void walk_simple_edges(SkEdge* prevHead, SkBlitter* blitter, int start_y, ASSERT_RETURN(local_top <= local_bot); SkFixed left = leftE->fX; - SkFixed dLeft = leftE->fDxDy; + SkFixed dLeft = leftE->fDX; SkFixed rite = riteE->fX; - SkFixed dRite = riteE->fDxDy; + SkFixed dRite = riteE->fDX; int count = local_bot - local_top; ASSERT_RETURN(count >= 0); - if (dLeft == 0 && dRite == 0) { + if (0 == (dLeft | dRite)) { int L = SkFixedRoundToInt(left); int R = SkFixedRoundToInt(rite); if (L > R) { @@ -314,10 +321,10 @@ static void walk_simple_edges(SkEdge* prevHead, SkBlitter* blitter, int start_y, // class InverseBlitter : public SkBlitter { public: - void setBlitter(SkBlitter* blitter, const SkIRect& clip) { + void setBlitter(SkBlitter* blitter, const SkIRect& clip, int shift) { fBlitter = blitter; - fFirstX = clip.fLeft; - fLastX = clip.fRight; + fFirstX = clip.fLeft << shift; + fLastX = clip.fRight << shift; } void prepost(int y, bool isStart) { if (isStart) { @@ -389,16 +396,23 @@ static SkEdge* sort_edges(SkEdge* list[], int count, SkEdge** last) { return list[0]; } -static void sk_fill_path(const SkPathRaw& raw, const SkIRect& clipRect, SkBlitter* blitter, - int start_y, int stop_y, bool pathContainedInClip) { +// clipRect has not been shifted up +void sk_fill_path(const SkPath& path, const SkIRect& clipRect, SkBlitter* blitter, + int start_y, int stop_y, int shiftEdgesUp, bool pathContainedInClip) { SkASSERT(blitter); - SkBasicEdgeBuilder builder; - int count = builder.buildEdges(raw, pathContainedInClip ? nullptr : &clipRect); + SkIRect shiftedClip = clipRect; + shiftedClip.fLeft = SkLeftShift(shiftedClip.fLeft, shiftEdgesUp); + shiftedClip.fRight = SkLeftShift(shiftedClip.fRight, shiftEdgesUp); + shiftedClip.fTop = SkLeftShift(shiftedClip.fTop, shiftEdgesUp); + shiftedClip.fBottom = SkLeftShift(shiftedClip.fBottom, shiftEdgesUp); + + SkBasicEdgeBuilder builder(shiftEdgesUp); + int count = builder.buildEdges(path, pathContainedInClip ? nullptr : &shiftedClip); SkEdge** list = builder.edgeList(); if (0 == count) { - if (raw.isInverseFillType()) { + if (path.isInverseFillType()) { /* * Since we are in inverse-fill, our caller has already drawn above * our top (start_y) and will draw below our bottom (stop_y). Thus @@ -413,10 +427,10 @@ static void sk_fill_path(const SkPathRaw& raw, const SkIRect& clipRect, SkBlitte rect.fBottom = stop_y; } if (!rect.isEmpty()) { - blitter->blitRect(rect.fLeft, - rect.fTop, - rect.width(), - rect.height()); + blitter->blitRect(rect.fLeft << shiftEdgesUp, + rect.fTop << shiftEdgesUp, + rect.width() << shiftEdgesUp, + rect.height() << shiftEdgesUp); } } return; @@ -438,28 +452,31 @@ static void sk_fill_path(const SkPathRaw& raw, const SkIRect& clipRect, SkBlitte last->fNext = &tailEdge; // now edge is the head of the sorted linklist - if (!pathContainedInClip && start_y < clipRect.fTop) { - start_y = clipRect.fTop; + + start_y = SkLeftShift(start_y, shiftEdgesUp); + stop_y = SkLeftShift(stop_y, shiftEdgesUp); + if (!pathContainedInClip && start_y < shiftedClip.fTop) { + start_y = shiftedClip.fTop; } - if (!pathContainedInClip && stop_y > clipRect.fBottom) { - stop_y = clipRect.fBottom; + if (!pathContainedInClip && stop_y > shiftedClip.fBottom) { + stop_y = shiftedClip.fBottom; } InverseBlitter ib; PrePostProc proc = nullptr; - if (raw.isInverseFillType()) { - ib.setBlitter(blitter, clipRect); + if (path.isInverseFillType()) { + ib.setBlitter(blitter, clipRect, shiftEdgesUp); blitter = &ib; proc = PrePostInverseBlitterProc; } // count >= 2 is required as the convex walker does not handle missing right edges - if (raw.isConvex() && (nullptr == proc) && count >= 2) { + if (path.isConvex() && (nullptr == proc) && count >= 2) { walk_simple_edges(&headEdge, blitter, start_y, stop_y); } else { - walk_edges(&headEdge, raw.fillType(), blitter, start_y, stop_y, proc, - clipRect.right()); + walk_edges(&headEdge, path.getFillType(), blitter, start_y, stop_y, proc, + shiftedClip.right()); } } @@ -539,7 +556,7 @@ SkScanClipper::SkScanClipper(SkBlitter* blitter, const SkRegion* clip, static bool clip_to_limit(const SkRegion& orig, SkRegion* reduced) { // need to limit coordinates such that the width/height of our rect can be represented - // in SkFixed (16.16). See skbug.com/40039252 + // in SkFixed (16.16). See skbug.com/7998 const int32_t limit = 32767 >> 1; SkIRect limitR; @@ -601,7 +618,8 @@ static SkIRect conservative_round_to_int(const SkRect& src) { }; } -void SkScan::FillPath(const SkPathRaw& raw, const SkRegion& origClip, SkBlitter* blitter) { +void SkScan::FillPath(const SkPath& path, const SkRegion& origClip, + SkBlitter* blitter) { if (origClip.isEmpty()) { return; } @@ -619,7 +637,7 @@ void SkScan::FillPath(const SkPathRaw& raw, const SkRegion& origClip, SkBlitter* // don't reference "origClip" any more, just use clipPtr - SkRect bounds = raw.bounds(); + SkRect bounds = path.getBounds(); bool irPreClipped = false; if (!SkRectPriv::MakeLargeS32().contains(bounds)) { if (!bounds.intersect(SkRectPriv::MakeLargeS32())) { @@ -630,26 +648,26 @@ void SkScan::FillPath(const SkPathRaw& raw, const SkRegion& origClip, SkBlitter* SkIRect ir = conservative_round_to_int(bounds); if (ir.isEmpty()) { - if (raw.isInverseFillType()) { + if (path.isInverseFillType()) { blitter->blitRegion(*clipPtr); } return; } - SkScanClipper clipper(blitter, clipPtr, ir, raw.isInverseFillType(), irPreClipped); + SkScanClipper clipper(blitter, clipPtr, ir, path.isInverseFillType(), irPreClipped); blitter = clipper.getBlitter(); if (blitter) { // we have to keep our calls to blitter in sorted order, so we // must blit the above section first, then the middle, then the bottom. - if (raw.isInverseFillType()) { + if (path.isInverseFillType()) { sk_blit_above(blitter, ir, *clipPtr); } SkASSERT(clipper.getClipRect() == nullptr || *clipper.getClipRect() == clipPtr->getBounds()); - sk_fill_path(raw, clipPtr->getBounds(), blitter, ir.fTop, ir.fBottom, - clipper.getClipRect() == nullptr); - if (raw.isInverseFillType()) { + sk_fill_path(path, clipPtr->getBounds(), blitter, ir.fTop, ir.fBottom, + 0, clipper.getClipRect() == nullptr); + if (path.isInverseFillType()) { sk_blit_below(blitter, ir, *clipPtr); } } else { @@ -657,6 +675,12 @@ void SkScan::FillPath(const SkPathRaw& raw, const SkRegion& origClip, SkBlitter* } } +void SkScan::FillPath(const SkPath& path, const SkIRect& ir, + SkBlitter* blitter) { + SkRegion rgn(ir); + FillPath(path, rgn, blitter); +} + bool SkScan::PathRequiresTiling(const SkIRect& bounds) { SkRegion out; // ignored return clip_to_limit(SkRegion(bounds), &out); @@ -668,15 +692,15 @@ static int build_tri_edges(SkEdge edge[], const SkPoint pts[], const SkIRect* clipRect, SkEdge* list[]) { SkEdge** start = list; - if (edge->setLine(pts[0], pts[1], clipRect)) { + if (edge->setLine(pts[0], pts[1], clipRect, 0)) { *list++ = edge; - edge++; + edge = (SkEdge*)((char*)edge + sizeof(SkEdge)); } - if (edge->setLine(pts[1], pts[2], clipRect)) { + if (edge->setLine(pts[1], pts[2], clipRect, 0)) { *list++ = edge; - edge++; + edge = (SkEdge*)((char*)edge + sizeof(SkEdge)); } - if (edge->setLine(pts[2], pts[0], clipRect)) { + if (edge->setLine(pts[2], pts[0], clipRect, 0)) { *list++ = edge; } return (int)(list - start); @@ -729,22 +753,20 @@ void SkScan::FillTriangle(const SkPoint pts[], const SkRasterClip& clip, return; } - const auto r = SkRect::Bounds({pts, 3}); - if (!r) { - return; - } - + SkRect r; + r.setBounds(pts, 3); // If r is too large (larger than can easily fit in SkFixed) then we need perform geometric // clipping. This is a bit of work, so we just call the general FillPath() to handle it. // Use FixedMax/2 as the limit so we can subtract two edges and still store that in Fixed. const SkScalar limit = SK_MaxS16 >> 1; - if (!SkRect::MakeLTRB(-limit, -limit, limit, limit).contains(*r)) { - SkPathRawShapes::Triangle tri({pts, 3}, *r); - FillPath(tri, clip, blitter); + if (!SkRect::MakeLTRB(-limit, -limit, limit, limit).contains(r)) { + SkPath path; + path.addPoly(pts, 3, false); + FillPath(path, clip, blitter); return; } - SkIRect ir = conservative_round_to_int(*r); + SkIRect ir = conservative_round_to_int(r); if (ir.isEmpty() || !SkIRect::Intersects(ir, clip.getBounds())) { return; } @@ -765,21 +787,3 @@ void SkScan::FillTriangle(const SkPoint pts[], const SkRasterClip& clip, sk_fill_triangle(pts, clipper.getClipRect(), blitter, ir); } } - -void SkScan::FillPath(const SkPathRaw& raw, const SkRasterClip& clip, SkBlitter* blitter) { - if (clip.isEmpty()) { - return; - } - - if (clip.isBW()) { - SkScan::FillPath(raw, clip.bwRgn(), blitter); - } else { - SkRegion tmp; - SkAAClipBlitter aaBlitter; - - tmp.setRect(clip.getBounds()); - aaBlitter.init(blitter, &clip.aaRgn()); - SkScan::FillPath(raw, tmp, &aaBlitter); - } -} - diff --git a/gfx/skia/skia/src/core/SkSpecialImage.cpp b/gfx/skia/skia/src/core/SkSpecialImage.cpp @@ -9,6 +9,7 @@ #include "include/core/SkBitmap.h" #include "include/core/SkCanvas.h" +#include "include/core/SkColorType.h" #include "include/core/SkImage.h" #include "include/core/SkMatrix.h" #include "include/core/SkPoint.h" @@ -18,6 +19,13 @@ #include "src/image/SkImage_Base.h" #include "src/shaders/SkImageShader.h" +// Currently, the raster imagefilters can only handle certain imageinfos. Call this to know if +// a given info is supported. +static bool valid_for_imagefilters(const SkImageInfo& info) { + // no support for other swizzles/depths yet + return info.colorType() == kN32_SkColorType; +} + SkSpecialImage::SkSpecialImage(const SkIRect& subset, uint32_t uniqueID, const SkColorInfo& colorInfo, @@ -39,7 +47,7 @@ void SkSpecialImage::draw(SkCanvas* canvas, : SkCanvas::kFast_SrcRectConstraint); } -// TODO(skbug.com/40043877): Once bitmap images work with SkImageShader::MakeSubset(), this does not +// TODO(skbug.com/12784): Once bitmap images work with SkImageShader::MakeSubset(), this does not // need to be virtual anymore. sk_sp<SkShader> SkSpecialImage::asShader(SkTileMode tileMode, const SkSamplingOptions& sampling, @@ -94,7 +102,7 @@ public: const SkMatrix& lm, bool strict) const override { if (strict) { - // TODO(skbug.com/40043877): SkImage::makeShader() doesn't support a subset yet, but + // TODO(skbug.com/12784): SkImage::makeShader() doesn't support a subset yet, but // SkBitmap supports subset views so create the shader from the subset bitmap instead of // fBitmap. SkBitmap subsetBM; @@ -125,7 +133,19 @@ sk_sp<SkSpecialImage> MakeFromRaster(const SkIRect& subset, if (!bm.pixelRef()) { return nullptr; } - return sk_make_sp<SkSpecialImage_Raster>(subset, bm, props); + + const SkBitmap* srcBM = &bm; + SkBitmap tmp; + // ImageFilters only handle N32 at the moment, so force our src to be that + if (!valid_for_imagefilters(bm.info())) { + if (!tmp.tryAllocPixels(bm.info().makeColorType(kN32_SkColorType)) || + !bm.readPixels(tmp.info(), tmp.getPixels(), tmp.rowBytes(), 0, 0)) + { + return nullptr; + } + srcBM = &tmp; + } + return sk_make_sp<SkSpecialImage_Raster>(subset, *srcBM, props); } sk_sp<SkSpecialImage> CopyFromRaster(const SkIRect& subset, @@ -136,14 +156,20 @@ sk_sp<SkSpecialImage> CopyFromRaster(const SkIRect& subset, if (!bm.pixelRef()) { return nullptr; } + SkBitmap tmp; SkImageInfo info = bm.info().makeDimensions(subset.size()); + // As in MakeFromRaster, must force src to N32 for ImageFilters + if (!valid_for_imagefilters(bm.info())) { + info = info.makeColorType(kN32_SkColorType); + } if (!tmp.tryAllocPixels(info)) { return nullptr; } if (!bm.readPixels(tmp.info(), tmp.getPixels(), tmp.rowBytes(), subset.x(), subset.y())) { return nullptr; } + // Since we're making a copy of the raster, the resulting special image is the exact size // of the requested subset of the original and no longer needs to be offset by subset's left // and top, since those were relative to the original's buffer. diff --git a/gfx/skia/skia/src/core/SkStreamPriv.h b/gfx/skia/skia/src/core/SkStreamPriv.h @@ -44,7 +44,7 @@ private: }; /** - * Helper functions to read and write big-endian values to a stream. + * Helper functions to write big-endian values to a stream. */ inline bool SkWStreamWriteU16BE(SkWStream* s, uint16_t value) { value = SkEndian_SwapBE16(value); @@ -61,30 +61,6 @@ inline bool SkWStreamWriteS32BE(SkWStream* s, int32_t value) { return s->write(&value, sizeof(value)); } -inline bool SkStreamReadU16BE(SkStream* s, uint16_t* value) { - if (!s->readU16(value)) { - return false; - } - *value = SkEndian_SwapBE16(*value); - return true; -} - -inline bool SkStreamReadU32BE(SkStream* s, uint32_t* value) { - if (!s->readU32(value)) { - return false; - } - *value = SkEndian_SwapBE32(*value); - return true; -} - -inline bool SkStreamReadS32BE(SkStream* s, int32_t* value) { - if (!s->readS32(value)) { - return false; - } - *value = SkEndian_SwapBE32(*value); - return true; -} - // If the stream supports identifying the current position and total length, this returns // true if there are not enough bytes in the stream to fulfill a read of the given length. // Otherwise, it returns false. diff --git a/gfx/skia/skia/src/core/SkStrike.cpp b/gfx/skia/skia/src/core/SkStrike.cpp @@ -278,6 +278,9 @@ void SkStrike::dump() const { SkAutoMutexExclusive lock{fStrikeLock}; const SkTypeface* face = fScalerContext->getTypeface(); const SkScalerContextRec& rec = fScalerContext->getRec(); + SkMatrix matrix; + rec.getSingleMatrix(&matrix); + matrix.preScale(SkScalarInvert(rec.fTextSize), SkScalarInvert(rec.fTextSize)); SkString name; face->getFamilyName(&name); diff --git a/gfx/skia/skia/src/core/SkStrikeSpec.cpp b/gfx/skia/skia/src/core/SkStrikeSpec.cpp @@ -12,6 +12,7 @@ #include "include/core/SkPaint.h" #include "include/core/SkPathEffect.h" #include "include/core/SkSurfaceProps.h" +#include "src/base/SkTLazy.h" #include "src/core/SkFontPriv.h" #include "src/core/SkGlyph.h" #include "src/core/SkStrike.h" @@ -74,11 +75,10 @@ std::tuple<SkStrikeSpec, SkScalar> SkStrikeSpec::MakeCanonicalized( } const SkFont* canonicalizedFont = &font; - std::optional<SkFont> pathFont; + SkTLazy<SkFont> pathFont; SkScalar strikeToSourceScale = 1; if (ShouldDrawAsPath(canonicalizedPaint, font, SkMatrix::I())) { - pathFont = font; - canonicalizedFont = &pathFont.value(); + canonicalizedFont = pathFont.set(font); strikeToSourceScale = pathFont->setupForAsPaths(nullptr); canonicalizedPaint.reset(); } diff --git a/gfx/skia/skia/src/core/SkStroke.cpp b/gfx/skia/skia/src/core/SkStroke.cpp @@ -8,12 +8,9 @@ #include "src/core/SkStroke.h" #include "include/core/SkPath.h" -#include "include/core/SkPathBuilder.h" #include "include/core/SkPoint.h" -#include "include/core/SkRRect.h" #include "include/core/SkRect.h" #include "include/core/SkScalar.h" -#include "include/core/SkSpan.h" #include "include/private/base/SkFloatingPoint.h" #include "include/private/base/SkMacros.h" #include "include/private/base/SkTo.h" @@ -25,7 +22,6 @@ #include <algorithm> #include <array> -#include <optional> enum { kTangent_RecursiveLimit, @@ -168,21 +164,6 @@ struct SkQuadConstruct { // The state of the quad stroke under construction. } }; -static bool isZeroLengthSincePoint(SkSpan<const SkPoint> span, int startPtIndex) { - int count = SkToInt(span.size()) - startPtIndex; - if (count < 2) { - return true; - } - const SkPoint* pts = span.data() + startPtIndex; - const SkPoint& first = *pts; - for (int index = 1; index < count; ++index) { - if (first != pts[index]) { - return false; - } - } - return true; -} - class SkPathStroker { public: SkPathStroker(const SkPath& src, @@ -200,17 +181,16 @@ public: void cubicTo(const SkPoint&, const SkPoint&, const SkPoint&); void close(bool isLine) { this->finishContour(true, isLine); } - void done(SkPathBuilder* dst, bool isLine) { + void done(SkPath* dst, bool isLine) { this->finishContour(false, isLine); - *dst = fOuter; - fOuter.reset(); // is this needed? we used to "swap" it with dst + dst->swap(fOuter); } SkScalar getResScale() const { return fResScale; } bool isCurrentContourEmpty() const { - return isZeroLengthSincePoint(fInner.points(), 0) && - isZeroLengthSincePoint(fOuter.points(), fFirstOuterPtIndexInContour); + return fInner.isZeroLengthSincePoint(0) && + fOuter.isZeroLengthSincePoint(fFirstOuterPtIndexInContour); } private: @@ -231,7 +211,7 @@ private: SkStrokerPriv::CapProc fCapper; SkStrokerPriv::JoinProc fJoiner; - SkPathBuilder fInner, fOuter, fCusper; // outer is our working answer, inner is temp + SkPath fInner, fOuter, fCusper; // outer is our working answer, inner is temp enum StrokeType { kOuter_StrokeType = 1, // use sign-opposite values later to flip perpendicular axis @@ -353,6 +333,8 @@ void SkPathStroker::postJoinTo(const SkPoint& currPt, const SkVector& normal, void SkPathStroker::finishContour(bool close, bool currIsLine) { if (fSegmentCount > 0) { + SkPoint pt; + if (close) { fJoiner(&fOuter, &fInner, fPrevUnitNormal, fPrevPt, fFirstUnitNormal, fRadius, fInvMiterLimit, @@ -362,32 +344,35 @@ void SkPathStroker::finishContour(bool close, bool currIsLine) { if (fCanIgnoreCenter) { // If we can ignore the center just make sure the larger of the two paths // is preserved and don't add the smaller one. - if (fInner.computeBounds().contains(fOuter.computeBounds())) { - fOuter = fInner; + if (fInner.getBounds().contains(fOuter.getBounds())) { + fInner.swap(fOuter); } } else { // now add fInner as its own contour - if (auto pt = fInner.getLastPt()) { - fOuter.moveTo(*pt); - fOuter.privateReversePathTo(fInner.detach()); // todo: take builder or raw - fOuter.close(); - } + fInner.getLastPt(&pt); + fOuter.moveTo(pt); + fOuter.reversePathTo(fInner); + fOuter.close(); } } else { // add caps to start and end // cap the end - if (auto pt = fInner.getLastPt()) { - fCapper(&fOuter, fPrevPt, fPrevNormal, *pt, currIsLine); - fOuter.privateReversePathTo(fInner.detach()); - // cap the start - fCapper(&fOuter, fFirstPt, -fFirstNormal, fFirstOuterPt, fPrevIsLine); - fOuter.close(); - } + fInner.getLastPt(&pt); + fCapper(&fOuter, fPrevPt, fPrevNormal, pt, + currIsLine ? &fInner : nullptr); + fOuter.reversePathTo(fInner); + // cap the start + fCapper(&fOuter, fFirstPt, -fFirstNormal, fFirstOuterPt, + fPrevIsLine ? &fInner : nullptr); + fOuter.close(); } if (!fCusper.isEmpty()) { - fOuter.addPath(fCusper.detach()); + fOuter.addPath(fCusper); + fCusper.rewind(); } } - fInner.reset(); + // since we may re-use fInner, we rewind instead of reset, to save on + // reallocating its internal storage. + fInner.rewind(); fSegmentCount = -1; fFirstOuterPtIndexInContour = fOuter.countPoints(); } @@ -403,7 +388,7 @@ SkPathStroker::SkPathStroker(const SkPath& src, , fCanIgnoreCenter(canIgnoreCenter) { /* This is only used when join is miter_join, but we initialize it here - so that it is always defined, to fix sanitizer warnings. + so that it is always defined, to fis valgrind warnings. */ fInvMiterLimit = 0; @@ -453,28 +438,30 @@ void SkPathStroker::line_to(const SkPoint& currPt, const SkVector& normal) { static bool has_valid_tangent(const SkPath::Iter* iter) { SkPath::Iter copy = *iter; - while (auto rec = copy.next()) { - SkSpan<const SkPoint> pts = rec->fPoints; - switch (rec->fVerb) { - case SkPathVerb::kMove: + SkPath::Verb verb; + SkPoint pts[4]; + while ((verb = copy.next(pts))) { + switch (verb) { + case SkPath::kMove_Verb: return false; - case SkPathVerb::kLine: + case SkPath::kLine_Verb: if (pts[0] == pts[1]) { continue; } return true; - case SkPathVerb::kQuad: - case SkPathVerb::kConic: + case SkPath::kQuad_Verb: + case SkPath::kConic_Verb: if (pts[0] == pts[1] && pts[0] == pts[2]) { continue; } return true; - case SkPathVerb::kCubic: + case SkPath::kCubic_Verb: if (pts[0] == pts[1] && pts[0] == pts[2] && pts[0] == pts[3]) { continue; } return true; - case SkPathVerb::kClose: + case SkPath::kClose_Verb: + case SkPath::kDone_Verb: return false; } } @@ -1163,8 +1150,8 @@ SkPathStroker::ResultType SkPathStroker::compareQuadQuad(const SkPoint quad[3], void SkPathStroker::addDegenerateLine(const SkQuadConstruct* quadPts) { const SkPoint* quad = quadPts->fQuad; - auto sink = fStrokeType == kOuter_StrokeType ? &fOuter : &fInner; - sink->lineTo(quad[2]); + SkPath* path = fStrokeType == kOuter_StrokeType ? &fOuter : &fInner; + path->lineTo(quad[2]); } bool SkPathStroker::cubicMidOnLine(const SkPoint cubic[4], const SkQuadConstruct* quadPts) const { @@ -1192,9 +1179,9 @@ bool SkPathStroker::cubicStroke(const SkPoint cubic[4], SkQuadConstruct* quadPts if (fFoundTangents) { ResultType resultType = this->compareQuadCubic(cubic, quadPts); if (kQuad_ResultType == resultType) { - auto sink = fStrokeType == kOuter_StrokeType ? &fOuter : &fInner; + SkPath* path = fStrokeType == kOuter_StrokeType ? &fOuter : &fInner; const SkPoint* stroke = quadPts->fQuad; - sink->quadTo(stroke[1], stroke[2]); + path->quadTo(stroke[1], stroke[2]); DEBUG_CUBIC_RECURSION_TRACK_DEPTH(fRecursionDepth); return true; } @@ -1247,8 +1234,8 @@ bool SkPathStroker::conicStroke(const SkConic& conic, SkQuadConstruct* quadPts) ResultType resultType = this->compareQuadConic(conic, quadPts); if (kQuad_ResultType == resultType) { const SkPoint* stroke = quadPts->fQuad; - auto sink = fStrokeType == kOuter_StrokeType ? &fOuter : &fInner; - sink->quadTo(stroke[1], stroke[2]); + SkPath* path = fStrokeType == kOuter_StrokeType ? &fOuter : &fInner; + path->quadTo(stroke[1], stroke[2]); return true; } if (kDegenerate_ResultType == resultType) { @@ -1281,8 +1268,8 @@ bool SkPathStroker::quadStroke(const SkPoint quad[3], SkQuadConstruct* quadPts) ResultType resultType = this->compareQuadQuad(quad, quadPts); if (kQuad_ResultType == resultType) { const SkPoint* stroke = quadPts->fQuad; - auto sink = fStrokeType == kOuter_StrokeType ? &fOuter : &fInner; - sink->quadTo(stroke[1], stroke[2]); + SkPath* path = fStrokeType == kOuter_StrokeType ? &fOuter : &fInner; + path->quadTo(stroke[1], stroke[2]); return true; } if (kDegenerate_ResultType == resultType) { @@ -1427,11 +1414,39 @@ void SkStroke::setJoin(SkPaint::Join join) { /////////////////////////////////////////////////////////////////////////////// -void SkStroke::strokePath(const SkPath& src, SkPathBuilder* dst) const { +// If src==dst, then we use a tmp path to record the stroke, and then swap +// its contents with src when we're done. +class AutoTmpPath { +public: + AutoTmpPath(const SkPath& src, SkPath** dst) : fSrc(src) { + if (&src == *dst) { + *dst = &fTmpDst; + fSwapWithSrc = true; + } else { + (*dst)->reset(); + fSwapWithSrc = false; + } + } + + ~AutoTmpPath() { + if (fSwapWithSrc) { + fTmpDst.swap(*const_cast<SkPath*>(&fSrc)); + } + } + +private: + SkPath fTmpDst; + const SkPath& fSrc; + bool fSwapWithSrc; +}; + +void SkStroke::strokePath(const SkPath& src, SkPath* dst) const { SkASSERT(dst); SkScalar radius = SkScalarHalf(fWidth); + AutoTmpPath tmp(src, &dst); + if (radius <= 0) { return; } @@ -1459,32 +1474,32 @@ void SkStroke::strokePath(const SkPath& src, SkPathBuilder* dst) const { SkPathStroker stroker(src, radius, fMiterLimit, this->getCap(), this->getJoin(), fResScale, ignoreCenter); + SkPath::Iter iter(src, false); + SkPath::Verb lastSegment = SkPath::kMove_Verb; - SkPath::Iter iter(src, false); - SkPathVerb lastSegment = SkPathVerb::kMove; - while (auto rec = iter.next()) { - SkSpan<const SkPoint> pts = rec->fPoints; - switch (rec->fVerb) { - case SkPathVerb::kMove: + for (;;) { + SkPoint pts[4]; + switch (iter.next(pts)) { + case SkPath::kMove_Verb: stroker.moveTo(pts[0]); break; - case SkPathVerb::kLine: + case SkPath::kLine_Verb: stroker.lineTo(pts[1], &iter); - lastSegment = SkPathVerb::kLine; + lastSegment = SkPath::kLine_Verb; break; - case SkPathVerb::kQuad: + case SkPath::kQuad_Verb: stroker.quadTo(pts[1], pts[2]); - lastSegment = SkPathVerb::kQuad; + lastSegment = SkPath::kQuad_Verb; break; - case SkPathVerb::kConic: { - stroker.conicTo(pts[1], pts[2], rec->conicWeight()); - lastSegment = SkPathVerb::kConic; + case SkPath::kConic_Verb: { + stroker.conicTo(pts[1], pts[2], iter.conicWeight()); + lastSegment = SkPath::kConic_Verb; } break; - case SkPathVerb::kCubic: + case SkPath::kCubic_Verb: stroker.cubicTo(pts[1], pts[2], pts[3]); - lastSegment = SkPathVerb::kCubic; + lastSegment = SkPath::kCubic_Verb; break; - case SkPathVerb::kClose: + case SkPath::kClose_Verb: if (SkPaint::kButt_Cap != this->getCap()) { /* If the stroke consists of a moveTo followed by a close, treat it as if it were followed by a zero-length line. Lines without length @@ -1498,20 +1513,22 @@ void SkStroke::strokePath(const SkPath& src, SkPathBuilder* dst) const { zero-length line. Lines without length can have square & round end caps. */ if (stroker.isCurrentContourEmpty()) { ZERO_LENGTH: - lastSegment = SkPathVerb::kLine; + lastSegment = SkPath::kLine_Verb; break; } } - stroker.close(lastSegment == SkPathVerb::kLine); + stroker.close(lastSegment == SkPath::kLine_Verb); break; + case SkPath::kDone_Verb: + goto DONE; } } - stroker.done(dst, lastSegment == SkPathVerb::kLine); +DONE: + stroker.done(dst, lastSegment == SkPath::kLine_Verb); if (fDoFill && !ignoreCenter) { - auto d = SkPathPriv::ComputeFirstDirection(SkPathPriv::Raw(src)); - if (d == SkPathFirstDirection::kCCW) { - dst->privateReverseAddPath(src); + if (SkPathPriv::ComputeFirstDirection(src) == SkPathFirstDirection::kCCW) { + dst->reverseAddPath(src); } else { dst->addPath(src); } @@ -1549,8 +1566,7 @@ static SkPathDirection reverse_direction(SkPathDirection dir) { return gOpposite[(int)dir]; } -static void addBevel(SkPathBuilder* path, const SkRect& r, const SkRect& outer, - SkPathDirection dir) { +static void addBevel(SkPath* path, const SkRect& r, const SkRect& outer, SkPathDirection dir) { SkPoint pts[8]; if (SkPathDirection::kCW == dir) { @@ -1572,10 +1588,10 @@ static void addBevel(SkPathBuilder* path, const SkRect& r, const SkRect& outer, pts[1].set(outer.fLeft, r.fBottom); pts[0].set(outer.fLeft, r.fTop); } - path->addPolygon(pts, true); + path->addPoly(pts, 8, true); } -void SkStroke::strokeRect(const SkRect& origRect, SkPathBuilder* dst, +void SkStroke::strokeRect(const SkRect& origRect, SkPath* dst, SkPathDirection dir) const { SkASSERT(dst != nullptr); dst->reset(); @@ -1612,7 +1628,7 @@ void SkStroke::strokeRect(const SkRect& origRect, SkPathBuilder* dst, addBevel(dst, rect, r, dir); break; case SkPaint::kRound_Join: - dst->addRRect(SkRRect::MakeRectXY(r, radius, radius), dir); + dst->addRoundRect(r, radius, radius, dir); break; default: break; diff --git a/gfx/skia/skia/src/core/SkStroke.h b/gfx/skia/skia/src/core/SkStroke.h @@ -19,7 +19,6 @@ #include <cstdint> class SkPath; -class SkPathBuilder; struct SkRect; #ifdef SK_DEBUG @@ -69,9 +68,9 @@ public: /** * Stroke the specified rect, winding it in the specified direction.. */ - void strokeRect(const SkRect& rect, SkPathBuilder* result, + void strokeRect(const SkRect& rect, SkPath* result, SkPathDirection = SkPathDirection::kCW) const; - void strokePath(const SkPath& path, SkPathBuilder*) const; + void strokePath(const SkPath& path, SkPath*) const; //////////////////////////////////////////////////////////////// diff --git a/gfx/skia/skia/src/core/SkStrokeRec.cpp b/gfx/skia/skia/src/core/SkStrokeRec.cpp @@ -104,7 +104,7 @@ void SkStrokeRec::setStrokeStyle(SkScalar width, bool strokeAndFill) { SkScalar gDebugStrokerError; #endif -bool SkStrokeRec::applyToPath(SkPathBuilder* dst, const SkPath& src) const { +bool SkStrokeRec::applyToPath(SkPath* dst, const SkPath& src) const { if (fWidth <= 0) { // hairline or fill return false; } @@ -155,7 +155,7 @@ SkScalar SkStrokeRec::GetInflationRadius(SkPaint::Join join, SkScalar miterLimit } else if (0 == strokeWidth) { // FIXME: We need a "matrixScale" parameter here in order to properly handle hairlines. // Their with is determined in device space, unlike other strokes. - // skbug.com/40039419 + // http://skbug.com/8157 return SK_Scalar1; } diff --git a/gfx/skia/skia/src/core/SkStrokerPriv.cpp b/gfx/skia/skia/src/core/SkStrokerPriv.cpp @@ -7,41 +7,41 @@ #include "src/core/SkStrokerPriv.h" #include "include/core/SkMatrix.h" -#include "include/core/SkPathBuilder.h" +#include "include/core/SkPath.h" #include "include/private/base/SkAssert.h" #include "src/core/SkGeometry.h" #include "src/core/SkPointPriv.h" #include <utility> -static void ButtCapper(SkPathBuilder* sink, const SkPoint& pivot, const SkVector& normal, - const SkPoint& stop, bool) { - sink->lineTo(stop.fX, stop.fY); +static void ButtCapper(SkPath* path, const SkPoint& pivot, const SkVector& normal, + const SkPoint& stop, SkPath*) { + path->lineTo(stop.fX, stop.fY); } -static void RoundCapper(SkPathBuilder* sink, const SkPoint& pivot, const SkVector& normal, - const SkPoint& stop, bool) { +static void RoundCapper(SkPath* path, const SkPoint& pivot, const SkVector& normal, + const SkPoint& stop, SkPath*) { SkVector parallel; SkPointPriv::RotateCW(normal, &parallel); SkPoint projectedCenter = pivot + parallel; - sink->conicTo(projectedCenter + normal, projectedCenter, SK_ScalarRoot2Over2); - sink->conicTo(projectedCenter - normal, stop, SK_ScalarRoot2Over2); + path->conicTo(projectedCenter + normal, projectedCenter, SK_ScalarRoot2Over2); + path->conicTo(projectedCenter - normal, stop, SK_ScalarRoot2Over2); } -static void SquareCapper(SkPathBuilder* sink, const SkPoint& pivot, const SkVector& normal, - const SkPoint& stop, bool extendLastPt) { +static void SquareCapper(SkPath* path, const SkPoint& pivot, const SkVector& normal, + const SkPoint& stop, SkPath* otherPath) { SkVector parallel; SkPointPriv::RotateCW(normal, &parallel); - if (extendLastPt) { - sink->setLastPt(pivot.fX + normal.fX + parallel.fX, pivot.fY + normal.fY + parallel.fY); - sink->lineTo(pivot.fX - normal.fX + parallel.fX, pivot.fY - normal.fY + parallel.fY); + if (otherPath) { + path->setLastPt(pivot.fX + normal.fX + parallel.fX, pivot.fY + normal.fY + parallel.fY); + path->lineTo(pivot.fX - normal.fX + parallel.fX, pivot.fY - normal.fY + parallel.fY); } else { - sink->lineTo(pivot.fX + normal.fX + parallel.fX, pivot.fY + normal.fY + parallel.fY); - sink->lineTo(pivot.fX - normal.fX + parallel.fX, pivot.fY - normal.fY + parallel.fY); - sink->lineTo(stop.fX, stop.fY); + path->lineTo(pivot.fX + normal.fX + parallel.fX, pivot.fY + normal.fY + parallel.fY); + path->lineTo(pivot.fX - normal.fX + parallel.fX, pivot.fY - normal.fY + parallel.fY); + path->lineTo(stop.fX, stop.fY); } } @@ -69,7 +69,7 @@ static AngleType Dot2AngleType(SkScalar dot) { } } -static void HandleInnerJoin(SkPathBuilder* inner, const SkPoint& pivot, const SkVector& after) { +static void HandleInnerJoin(SkPath* inner, const SkPoint& pivot, const SkVector& after) { #if 1 /* In the degenerate case that the stroke radius is larger than our segments just connecting the two inner segments may "show through" as a funny @@ -83,8 +83,7 @@ static void HandleInnerJoin(SkPathBuilder* inner, const SkPoint& pivot, const Sk inner->lineTo(pivot.fX - after.fX, pivot.fY - after.fY); } -static void BluntJoiner(SkPathBuilder* outer, SkPathBuilder* inner, - const SkVector& beforeUnitNormal, +static void BluntJoiner(SkPath* outer, SkPath* inner, const SkVector& beforeUnitNormal, const SkPoint& pivot, const SkVector& afterUnitNormal, SkScalar radius, SkScalar invMiterLimit, bool, bool) { SkVector after; @@ -100,8 +99,7 @@ static void BluntJoiner(SkPathBuilder* outer, SkPathBuilder* inner, HandleInnerJoin(inner, pivot, after); } -static void RoundJoiner(SkPathBuilder* outer, SkPathBuilder* inner, - const SkVector& beforeUnitNormal, +static void RoundJoiner(SkPath* outer, SkPath* inner, const SkVector& beforeUnitNormal, const SkPoint& pivot, const SkVector& afterUnitNormal, SkScalar radius, SkScalar invMiterLimit, bool, bool) { SkScalar dotProd = SkPoint::DotProduct(beforeUnitNormal, afterUnitNormal); @@ -110,16 +108,16 @@ static void RoundJoiner(SkPathBuilder* outer, SkPathBuilder* inner, if (angleType == kNearlyLine_AngleType) return; - SkVector before = beforeUnitNormal; - SkVector after = afterUnitNormal; - SkPathDirection dir = SkPathDirection::kCW; + SkVector before = beforeUnitNormal; + SkVector after = afterUnitNormal; + SkRotationDirection dir = kCW_SkRotationDirection; if (!is_clockwise(before, after)) { using std::swap; swap(outer, inner); before.negate(); after.negate(); - dir = SkPathDirection::kCCW; + dir = kCCW_SkRotationDirection; } SkMatrix matrix; @@ -138,8 +136,7 @@ static void RoundJoiner(SkPathBuilder* outer, SkPathBuilder* inner, #define kOneOverSqrt2 (0.707106781f) -static void MiterJoiner(SkPathBuilder* outer, SkPathBuilder* inner, - const SkVector& beforeUnitNormal, +static void MiterJoiner(SkPath* outer, SkPath* inner, const SkVector& beforeUnitNormal, const SkPoint& pivot, const SkVector& afterUnitNormal, SkScalar radius, SkScalar invMiterLimit, bool prevIsLine, bool currIsLine) { diff --git a/gfx/skia/skia/src/core/SkStrokerPriv.h b/gfx/skia/skia/src/core/SkStrokerPriv.h @@ -13,7 +13,7 @@ #include "include/core/SkPoint.h" #include "include/core/SkScalar.h" -class SkPathBuilder; +class SkPath; #define CWX(x, y) (-y) #define CWY(x, y) (x) @@ -27,13 +27,13 @@ class SkPathBuilder; class SkStrokerPriv { public: - typedef void (*CapProc)(SkPathBuilder* path, + typedef void (*CapProc)(SkPath* path, const SkPoint& pivot, const SkVector& normal, const SkPoint& stop, - bool extendLastPt); + SkPath* otherPath); - typedef void (*JoinProc)(SkPathBuilder* outer, SkPathBuilder* inner, + typedef void (*JoinProc)(SkPath* outer, SkPath* inner, const SkVector& beforeUnitNormal, const SkPoint& pivot, const SkVector& afterUnitNormal, diff --git a/gfx/skia/skia/src/core/SkSynchronizedResourceCache.cpp b/gfx/skia/skia/src/core/SkSynchronizedResourceCache.cpp @@ -1,81 +0,0 @@ -/* - * 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/SkSynchronizedResourceCache.h" - -SkSynchronizedResourceCache::SkSynchronizedResourceCache(DiscardableFactory fact) - : SkResourceCache(fact) {} - -SkSynchronizedResourceCache::SkSynchronizedResourceCache(size_t byteLimit) - : SkResourceCache(byteLimit) {} - -SkSynchronizedResourceCache::~SkSynchronizedResourceCache() = default; - -size_t SkSynchronizedResourceCache::getTotalBytesUsed() const { - SkAutoMutexExclusive am(fMutex); - return SkResourceCache::getTotalBytesUsed(); -} - -size_t SkSynchronizedResourceCache::getTotalByteLimit() const { - SkAutoMutexExclusive am(fMutex); - return SkResourceCache::getTotalByteLimit(); -} - -size_t SkSynchronizedResourceCache::setTotalByteLimit(size_t newLimit) { - SkAutoMutexExclusive am(fMutex); - return SkResourceCache::setTotalByteLimit(newLimit); -} - -SkResourceCache::DiscardableFactory SkSynchronizedResourceCache::discardableFactory() const { - SkAutoMutexExclusive am(fMutex); - return SkResourceCache::discardableFactory(); -} - -SkCachedData* SkSynchronizedResourceCache::newCachedData(size_t bytes) { - SkAutoMutexExclusive am(fMutex); - return SkResourceCache::newCachedData(bytes); -} - -void SkSynchronizedResourceCache::dump() const { - SkAutoMutexExclusive am(fMutex); - SkResourceCache::dump(); -} - -size_t SkSynchronizedResourceCache::setSingleAllocationByteLimit(size_t size) { - SkAutoMutexExclusive am(fMutex); - return SkResourceCache::setSingleAllocationByteLimit(size); -} - -size_t SkSynchronizedResourceCache::getSingleAllocationByteLimit() const { - SkAutoMutexExclusive am(fMutex); - return SkResourceCache::getSingleAllocationByteLimit(); -} - -size_t SkSynchronizedResourceCache::getEffectiveSingleAllocationByteLimit() const { - SkAutoMutexExclusive am(fMutex); - return SkResourceCache::getEffectiveSingleAllocationByteLimit(); -} - -void SkSynchronizedResourceCache::purgeAll() { - SkAutoMutexExclusive am(fMutex); - return SkResourceCache::purgeAll(); -} - -bool SkSynchronizedResourceCache::find(const Key& key, FindVisitor visitor, void* context) { - SkAutoMutexExclusive am(fMutex); - return SkResourceCache::find(key, visitor, context); -} - -void SkSynchronizedResourceCache::add(Rec* rec, void* payload) { - SkAutoMutexExclusive am(fMutex); - SkResourceCache::add(rec, payload); -} - -void SkSynchronizedResourceCache::visitAll(Visitor visitor, void* context) { - SkAutoMutexExclusive am(fMutex); - SkResourceCache::visitAll(visitor, context); -} diff --git a/gfx/skia/skia/src/core/SkSynchronizedResourceCache.h b/gfx/skia/skia/src/core/SkSynchronizedResourceCache.h @@ -1,49 +0,0 @@ -/* - * 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 SkSynchronizedResourceCache_DEFINED -#define SkSynchronizedResourceCache_DEFINED - -#include "include/private/base/SkDebug.h" -#include "include/private/base/SkMutex.h" -#include "src/core/SkResourceCache.h" - -#include <cstddef> - -class SkCachedData; - -class SkSynchronizedResourceCache : public SkResourceCache { -public: - bool find(const Key& key, FindVisitor, void* context) override; - void add(Rec*, void* payload = nullptr) override; - - void visitAll(Visitor, void* context) override; - - size_t getTotalBytesUsed() const override; - size_t getTotalByteLimit() const override; - size_t setTotalByteLimit(size_t newLimit) override; - - size_t setSingleAllocationByteLimit(size_t) override; - size_t getSingleAllocationByteLimit() const override; - size_t getEffectiveSingleAllocationByteLimit() const override; - - void purgeAll() override; - - DiscardableFactory discardableFactory() const override; - - SkCachedData* newCachedData(size_t bytes) override; - - void dump() const override; - - SkSynchronizedResourceCache(DiscardableFactory); - explicit SkSynchronizedResourceCache(size_t byteLimit); - ~SkSynchronizedResourceCache() override; - -private: - mutable SkMutex fMutex; -}; -#endif diff --git a/gfx/skia/skia/src/core/SkTMultiMap.h b/gfx/skia/skia/src/core/SkTMultiMap.h @@ -18,16 +18,12 @@ template <typename T, typename HashTraits=T> class SkTMultiMap { struct ValueList { - explicit ValueList(T* value) : fValue(value), fNext(nullptr), fCount(1) {} + explicit ValueList(T* value) : fValue(value), fNext(nullptr) {} static const Key& GetKey(const ValueList& e) { return HashTraits::GetKey(*e.fValue); } static uint32_t Hash(const Key& key) { return HashTraits::Hash(key); } T* fValue; ValueList* fNext; - - // TODO(b/407062399): Debugging information holding count of elements in ValueList. - // Only maintained in the head of the linked list to view in LLDB dumps. - uint32_t fCount; }; public: SkTMultiMap() : fCount(0) {} @@ -60,7 +56,6 @@ public: // inserted value. list->fNext = newEntry; list->fValue = value; - list->fCount++; } else { fHash.add(new ValueList(value)); } @@ -69,8 +64,7 @@ public: } void remove(const Key& key, const T* value) { - ValueList* root = fHash.find(key); - ValueList* list = root; + ValueList* list = fHash.find(key); // Temporarily making this safe for remove entries not in the map because of // crbug.com/877915. #if 0 @@ -82,7 +76,7 @@ public: prev = list; list = list->fNext; } - this->internalRemove(root, prev, list, key); + this->internalRemove(prev, list, key); #else ValueList* prev = nullptr; while (list && list->fValue != value) { @@ -92,7 +86,7 @@ public: // Crash in Debug since it'd be great to detect a repro of 877915. SkASSERT(list); if (list) { - this->internalRemove(root, prev, list, key); + this->internalRemove(prev, list, key); } #endif } @@ -119,14 +113,13 @@ public: template<class FindPredicate> T* findAndRemove(const Key& key, const FindPredicate f) { - ValueList* root = fHash.find(key); + ValueList* list = fHash.find(key); - ValueList* list = root; ValueList* prev = nullptr; while (list) { if (f(list->fValue)){ T* value = list->fValue; - this->internalRemove(root, prev, list, key); + this->internalRemove(prev, list, key); return value; } prev = list; @@ -172,8 +165,7 @@ private: SkTDynamicHash<ValueList, Key> fHash; int fCount; - void internalRemove(ValueList* root, ValueList* prev, ValueList* elem, const Key& key) { - root->fCount--; + void internalRemove(ValueList* prev, ValueList* elem, const Key& key) { if (elem->fNext) { ValueList* next = elem->fNext; elem->fValue = next->fValue; diff --git a/gfx/skia/skia/src/core/SkTaskGroup.cpp b/gfx/skia/skia/src/core/SkTaskGroup.cpp @@ -14,21 +14,11 @@ SkTaskGroup::SkTaskGroup(SkExecutor& executor) : fPending(0), fExecutor(executor) {} void SkTaskGroup::add(std::function<void(void)> fn) { - this->add(std::move(fn), /* workList= */ 0); -} - -void SkTaskGroup::add(std::function<void(void)> fn, int workList) { fPending.fetch_add(+1, std::memory_order_relaxed); fExecutor.add([this, fn{std::move(fn)}] { - fn(); - fPending.fetch_add(-1, std::memory_order_release); - }, - workList); -} - -void SkTaskGroup::discardAllPendingWork() { - int numDiscarded = fExecutor.discardAllPendingWork(); - fPending.fetch_add(-numDiscarded, std::memory_order_release); + fn(); + fPending.fetch_add(-1, std::memory_order_release); + }); } void SkTaskGroup::batch(int N, std::function<void(int)> fn) { diff --git a/gfx/skia/skia/src/core/SkTaskGroup.h b/gfx/skia/skia/src/core/SkTaskGroup.h @@ -25,9 +25,6 @@ public: // Add a task to this SkTaskGroup. void add(std::function<void(void)> fn); - void add(std::function<void(void)> fn, int workList); - - void discardAllPendingWork(); // Add a batch of N tasks, all calling fn with different arguments. void batch(int N, std::function<void(int)> fn); diff --git a/gfx/skia/skia/src/core/SkTextBlob.cpp b/gfx/skia/skia/src/core/SkTextBlob.cpp @@ -14,12 +14,13 @@ #include "include/core/SkPathEffect.h" #include "include/core/SkPoint.h" #include "include/core/SkRSXform.h" -#include "include/core/SkSpan.h" #include "include/private/base/SkAlign.h" #include "include/private/base/SkFloatingPoint.h" #include "include/private/base/SkMalloc.h" +#include "include/private/base/SkSpan_impl.h" #include "include/private/base/SkTo.h" #include "src/base/SkSafeMath.h" +#include "src/base/SkTLazy.h" #include "src/core/SkFontPriv.h" #include "src/core/SkGlyph.h" #include "src/core/SkReadBuffer.h" @@ -286,7 +287,7 @@ SkRect SkTextBlobBuilder::TightRunBounds(const SkTextBlob::RunRecord& run) { } AutoSTArray<16, SkRect> glyphBounds(run.glyphCount()); - font.getBounds({run.glyphBuffer(), run.glyphCount()}, glyphBounds, nullptr); + font.getBounds(run.glyphBuffer(), run.glyphCount(), glyphBounds.get(), nullptr); if (SkTextBlob::kRSXform_Positioning == run.positioning()) { bounds.setEmpty(); @@ -355,7 +356,7 @@ SkRect SkTextBlobBuilder::ConservativeRunBounds(const SkTextBlob::RunRecord& run const SkPoint* glyphPosPts = run.pointBuffer(); SkASSERT((void*)(glyphPosPts + run.glyphCount()) <= SkTextBlob::RunRecord::Next(&run)); - bounds = SkRect::BoundsOrEmpty({glyphPosPts, run.glyphCount()}); + bounds.setBounds(glyphPosPts, run.glyphCount()); } break; case SkTextBlob::kRSXform_Positioning: { const SkRSXform* xform = run.xformBuffer(); @@ -788,56 +789,56 @@ sk_sp<SkTextBlob> SkTextBlob::MakeFromText(const void* text, size_t byteLength, SkTextEncoding encoding) { // Note: we deliberately promote this to fully positioned blobs, since we'd have to pay the // same cost down stream (i.e. computing bounds), so its cheaper to pay the cost once now. - const size_t count = font.countText(text, byteLength, encoding); - if (count == 0) { + const int count = font.countText(text, byteLength, encoding); + if (count < 1) { return nullptr; } SkTextBlobBuilder builder; auto buffer = builder.allocRunPos(font, count); - font.textToGlyphs(text, byteLength, encoding, {buffer.glyphs, count}); - font.getPos({buffer.glyphs, count}, {buffer.points(), count}, {0, 0}); + font.textToGlyphs(text, byteLength, encoding, buffer.glyphs, count); + font.getPos(buffer.glyphs, count, buffer.points(), {0, 0}); return builder.make(); } sk_sp<SkTextBlob> SkTextBlob::MakeFromPosText(const void* text, size_t byteLength, - SkSpan<const SkPoint> pos, const SkFont& font, + const SkPoint pos[], const SkFont& font, SkTextEncoding encoding) { - const size_t count = font.countText(text, byteLength, encoding); - if (count == 0 || pos.size() < count) { + const int count = font.countText(text, byteLength, encoding); + if (count < 1) { return nullptr; } SkTextBlobBuilder builder; auto buffer = builder.allocRunPos(font, count); - font.textToGlyphs(text, byteLength, encoding, {buffer.glyphs, count}); - memcpy(buffer.points(), pos.data(), count * sizeof(SkPoint)); + font.textToGlyphs(text, byteLength, encoding, buffer.glyphs, count); + memcpy(buffer.points(), pos, count * sizeof(SkPoint)); return builder.make(); } sk_sp<SkTextBlob> SkTextBlob::MakeFromPosTextH(const void* text, size_t byteLength, - SkSpan<const SkScalar> xpos, SkScalar constY, + const SkScalar xpos[], SkScalar constY, const SkFont& font, SkTextEncoding encoding) { - const size_t count = font.countText(text, byteLength, encoding); - if (count == 0 || xpos.size() < count) { + const int count = font.countText(text, byteLength, encoding); + if (count < 1) { return nullptr; } SkTextBlobBuilder builder; auto buffer = builder.allocRunPosH(font, count, constY); - font.textToGlyphs(text, byteLength, encoding, {buffer.glyphs, count}); - memcpy(buffer.pos, xpos.data(), count * sizeof(SkScalar)); + font.textToGlyphs(text, byteLength, encoding, buffer.glyphs, count); + memcpy(buffer.pos, xpos, count * sizeof(SkScalar)); return builder.make(); } sk_sp<SkTextBlob> SkTextBlob::MakeFromRSXform(const void* text, size_t byteLength, - SkSpan<const SkRSXform> xform, const SkFont& font, + const SkRSXform xform[], const SkFont& font, SkTextEncoding encoding) { - const size_t count = font.countText(text, byteLength, encoding); - if (count == 0 || xform.size() < count) { + const int count = font.countText(text, byteLength, encoding); + if (count < 1) { return nullptr; } SkTextBlobBuilder builder; auto buffer = builder.allocRunRSXform(font, count); - font.textToGlyphs(text, byteLength, encoding, {buffer.glyphs, count}); - memcpy(buffer.xforms(), xform.data(), count * sizeof(SkRSXform)); + font.textToGlyphs(text, byteLength, encoding, buffer.glyphs, count); + memcpy(buffer.xforms(), xform, count * sizeof(SkRSXform)); return builder.make(); } @@ -925,10 +926,10 @@ int get_glyph_run_intercepts(const sktext::GlyphRun& glyphRun, int SkTextBlob::getIntercepts(const SkScalar bounds[2], SkScalar intervals[], const SkPaint* paint) const { - std::optional<SkPaint> defaultPaint; + SkTLazy<SkPaint> defaultPaint; if (paint == nullptr) { - defaultPaint.emplace(); - paint = &defaultPaint.value(); + defaultPaint.init(); + paint = defaultPaint.get(); } sktext::GlyphRunBuilder builder; @@ -946,19 +947,18 @@ int SkTextBlob::getIntercepts(const SkScalar bounds[2], SkScalar intervals[], return intervalCount; } -std::vector<SkScalar> SkFont::getIntercepts(SkSpan<const SkGlyphID> glyphs, - SkSpan<const SkPoint> positions, +std::vector<SkScalar> SkFont::getIntercepts(const SkGlyphID glyphs[], int count, + const SkPoint positions[], SkScalar top, SkScalar bottom, const SkPaint* paintPtr) const { - const auto count = std::min(glyphs.size(), positions.size()); - if (count == 0) { + if (count <= 0) { return std::vector<SkScalar>(); } const SkPaint paint(paintPtr ? *paintPtr : SkPaint()); const SkScalar bounds[] = {top, bottom}; const sktext::GlyphRun run(*this, - positions, glyphs, + {positions, size_t(count)}, {glyphs, size_t(count)}, {nullptr, 0}, {nullptr, 0}, {nullptr, 0}); std::vector<SkScalar> result; diff --git a/gfx/skia/skia/src/core/SkTraceEvent.h b/gfx/skia/skia/src/core/SkTraceEvent.h @@ -12,7 +12,7 @@ #include "include/utils/SkEventTracer.h" #include "src/base/SkUtils.h" -#include "src/core/SkTraceEventCommon.h" // IWYU pragma: export +#include "src/core/SkTraceEventCommon.h" #include <atomic> #if defined(SK_ANDROID_FRAMEWORK_USE_PERFETTO) diff --git a/gfx/skia/skia/src/core/SkTraceEventCommon.h b/gfx/skia/skia/src/core/SkTraceEventCommon.h @@ -241,10 +241,6 @@ static inline void sk_noop(Args...) {} #define TRACE_EVENT_INSTANT1(cg, n, scope, a1n, a1v) TRACE_EMPTY(cg, n, scope, a1n, a1v) #define TRACE_EVENT_INSTANT2(cg, n, scope, a1n, a1v, a2n, a2v) \ TRACE_EMPTY(cg, n, scope, a1n, a1v, a2n, a2v) - #define TRACE_EVENT_INSTANT0_ALWAYS(cg, n, scope) TRACE_EMPTY(cg, n, scope) - #define TRACE_EVENT_INSTANT1_ALWAYS(cg, n, scope, a1n, a1v) TRACE_EMPTY(cg, n, scope, a1n, a1v) - #define TRACE_EVENT_INSTANT2_ALWAYS(cg, n, scope, a1n, a1v, a2n, a2v) \ - TRACE_EMPTY(cg, n, scope, a1n, a1v, a2n, a2v) #define TRACE_EVENT_OBJECT_CREATED_WITH_ID(cg, n, id) TRACE_EMPTY(cg, n, id) #define TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(cg, n, id, ss) TRACE_EMPTY(cg, n, id, ss) #define TRACE_EVENT_OBJECT_DELETED_WITH_ID(cg, n, id) TRACE_EMPTY(cg, n, id) @@ -535,27 +531,6 @@ namespace skia_private { do { TRACE_EVENT_ATRACE_OR_PERFETTO(category_group, name, arg1_name, arg1_val, \ arg2_name, arg2_val); } while(0) -// As above but appends the ".always" suffix -#define TRACE_EVENT_INSTANT0_ALWAYS(category_group, name, scope) \ - do { TRACE_EVENT_ATRACE_OR_PERFETTO_FORCEABLE( \ - /* force_always_trace = */ true, category_group ".always", name); } while(0) - -#define TRACE_EVENT_INSTANT1_ALWAYS(category_group, name, scope, arg1_name, arg1_val) \ - do { TRACE_EVENT_ATRACE_OR_PERFETTO_FORCEABLE( \ - /* force_always_trace = */ true, category_group ".always", name, arg1_name, arg1_val); \ - } while(0) - -#define TRACE_EVENT_INSTANT2_ALWAYS(category_group, name, scope, arg1_name, arg1_val, \ - arg2_name, arg2_val) \ - do { TRACE_EVENT_ATRACE_OR_PERFETTO_FORCEABLE(/* force_always_trace = */ true, \ - category_group, \ - name, \ - arg1_name, \ - arg1_val, \ - arg2_name, \ - arg2_val); \ - } while(0) - // Records the value of a counter called "name" immediately. Value // must be representable as a 32 bit integer. #define TRACE_COUNTER1(category_group, name, value) \ @@ -641,20 +616,6 @@ namespace skia_private { TRACE_EVENT_FLAG_NONE | scope, arg1_name, arg1_val, \ arg2_name, arg2_val) -#define TRACE_EVENT_INSTANT0_ALWAYS(category_group, name, scope) \ - INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, category_group, name, \ - TRACE_EVENT_FLAG_NONE | scope) - -#define TRACE_EVENT_INSTANT1_ALWAYS(category_group, name, scope, arg1_name, arg1_val) \ - INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, category_group, name, \ - TRACE_EVENT_FLAG_NONE | scope, arg1_name, arg1_val) - -#define TRACE_EVENT_INSTANT2_ALWAYS(category_group, name, scope, arg1_name, arg1_val, \ - arg2_name, arg2_val) \ - INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, category_group, name, \ - TRACE_EVENT_FLAG_NONE | scope, arg1_name, arg1_val, \ - arg2_name, arg2_val) - // Records the value of a counter called "name" immediately. Value // must be representable as a 32 bit integer. #define TRACE_COUNTER1(category_group, name, value) \ diff --git a/gfx/skia/skia/src/core/SkTypeface.cpp b/gfx/skia/skia/src/core/SkTypeface.cpp @@ -42,12 +42,11 @@ #include "src/ports/SkTypeface_win_dw.h" #endif -// TODO(skbug.com/40045343): This needs to be set by Bazel rules. +// TODO(https://crbug.com/skia/14338): This needs to be set by Bazel rules. #ifdef SK_TYPEFACE_FACTORY_FONTATIONS #include "src/ports/SkTypeface_fontations_priv.h" #endif -#include <algorithm> #include <cstddef> #include <cstring> #include <vector> @@ -99,12 +98,12 @@ protected: desc->setFactoryId(FactoryId); *serialize = false; } - void onCharsToGlyphs(SkSpan<const SkUnichar> chars, SkSpan<SkGlyphID> glyphs) const override { - sk_bzero(glyphs.data(), glyphs.size_bytes()); + void onCharsToGlyphs(const SkUnichar* chars, int count, SkGlyphID glyphs[]) const override { + sk_bzero(glyphs, count * sizeof(glyphs[0])); } int onCountGlyphs() const override { return 0; } void getPostScriptGlyphNames(SkString*) const override {} - void getGlyphToUnicodeMap(SkSpan<SkUnichar>) const override {} + void getGlyphToUnicodeMap(SkUnichar*) const override {} int onGetUPEM() const override { return 0; } bool onComputeBounds(SkRect* bounds) const override { return false; } @@ -124,16 +123,17 @@ protected: bool onGlyphMaskNeedsCurrentColor() const override { return false; } - int onGetVariationDesignPosition( - SkSpan<SkFontArguments::VariationPosition::Coordinate>) const override + int onGetVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[], + int coordinateCount) const override { return 0; } - int onGetVariationDesignParameters(SkSpan<SkFontParameters::Variation::Axis>) const override + int onGetVariationDesignParameters(SkFontParameters::Variation::Axis parameters[], + int parameterCount) const override { return 0; } - int onGetTableTags(SkSpan<SkFontTableTag>) const override { return 0; } + int onGetTableTags(SkFontTableTag tags[]) const override { return 0; } size_t onGetTableData(SkFontTableTag, size_t, size_t, void*) const override { return 0; } @@ -220,9 +220,9 @@ void SkTypeface::serialize(SkWStream* wstream, SerializeBehavior behavior) const desc.setCollectionIndex(index); } - int numAxes = this->getVariationDesignPosition({}); + int numAxes = this->getVariationDesignPosition(nullptr, 0); if (0 < numAxes) { - numAxes = this->getVariationDesignPosition({desc.setVariationCoordinates(numAxes), numAxes}); + numAxes = this->getVariationDesignPosition(desc.setVariationCoordinates(numAxes), numAxes); if (numAxes <= 0) { desc.setVariationCoordinates(0); } @@ -286,22 +286,22 @@ bool SkTypeface::glyphMaskNeedsCurrentColor() const { } int SkTypeface::getVariationDesignPosition( - SkSpan<SkFontArguments::VariationPosition::Coordinate> coordinates) const + SkFontArguments::VariationPosition::Coordinate coordinates[], int coordinateCount) const { - return this->onGetVariationDesignPosition(coordinates); + return this->onGetVariationDesignPosition(coordinates, coordinateCount); } int SkTypeface::getVariationDesignParameters( - SkSpan<SkFontParameters::Variation::Axis> parameters) const + SkFontParameters::Variation::Axis parameters[], int parameterCount) const { - return this->onGetVariationDesignParameters(parameters); + return this->onGetVariationDesignParameters(parameters, parameterCount); } int SkTypeface::countTables() const { - return this->onGetTableTags({}); + return this->onGetTableTags(nullptr); } -int SkTypeface::readTableTags(SkSpan<SkFontTableTag> tags) const { +int SkTypeface::getTableTags(SkFontTableTag tags[]) const { return this->onGetTableTags(tags); } @@ -360,15 +360,15 @@ std::unique_ptr<SkScalerContext> SkTypeface::onCreateScalerContextAsProxyTypefac SK_ABORT("Not implemented."); } -void SkTypeface::unicharsToGlyphs(SkSpan<const SkUnichar> uni, SkSpan<SkGlyphID> glyphs) const { - if (const size_t n = std::min(uni.size(), glyphs.size())) { - this->onCharsToGlyphs(uni.first(n), glyphs.first(n)); +void SkTypeface::unicharsToGlyphs(const SkUnichar uni[], int count, SkGlyphID glyphs[]) const { + if (count > 0 && glyphs && uni) { + this->onCharsToGlyphs(uni, count, glyphs); } } SkGlyphID SkTypeface::unicharToGlyph(SkUnichar uni) const { SkGlyphID glyphs[1] = { 0 }; - this->onCharsToGlyphs({&uni, 1}, glyphs); + this->onCharsToGlyphs(&uni, 1, glyphs); return glyphs[0]; } @@ -410,28 +410,28 @@ private: }; } -size_t SkTypeface::textToGlyphs(const void* text, size_t byteLength, SkTextEncoding encoding, - SkSpan<SkGlyphID> glyphs) const { +int SkTypeface::textToGlyphs(const void* text, size_t byteLength, SkTextEncoding encoding, + SkGlyphID glyphs[], int maxGlyphCount) const { if (0 == byteLength) { return 0; } SkASSERT(text); - size_t count = SkFontPriv::CountTextElements(text, byteLength, encoding); - if (count > glyphs.size()) { + int count = SkFontPriv::CountTextElements(text, byteLength, encoding); + if (!glyphs || count > maxGlyphCount) { return count; } if (encoding == SkTextEncoding::kGlyphID) { - memcpy(glyphs.data(), text, count << 1); + memcpy(glyphs, text, count << 1); return count; } SkConvertToUTF32 storage; const SkUnichar* uni = storage.convert(text, byteLength, encoding); - this->unicharsToGlyphs({uni, count}, glyphs); + this->unicharsToGlyphs(uni, count, glyphs); return count; } @@ -444,17 +444,18 @@ int SkTypeface::getUnitsPerEm() const { return this->onGetUPEM(); } -bool SkTypeface::getKerningPairAdjustments(SkSpan<const SkGlyphID> glyphs, - SkSpan<int32_t> adjustments) const { - // We need glyphs.size() == adjustments.size() + 1 - // unless either is emptyish, in which case we still call the virtual, just - // to get the boolean result. - if (glyphs.size() <= 1 || adjustments.empty()) { - return this->onGetKerningPairAdjustments({}, {}); // just return the bool +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 + // in which case they just want to know if this face can possibly support + // kerning (true) or never (false). + if (nullptr == glyphs || nullptr == adjustments) { + SkASSERT(nullptr == glyphs); + SkASSERT(0 == count); + SkASSERT(nullptr == adjustments); } - - const size_t n = std::min(glyphs.size() - 1, adjustments.size()); - return this->onGetKerningPairAdjustments(glyphs.first(n + 1), adjustments.first(n)); + return this->onGetKerningPairAdjustments(glyphs, count, adjustments); } SkTypeface::LocalizedStrings* SkTypeface::createFamilyNameIterator() const { @@ -502,8 +503,8 @@ bool SkTypeface::onGetFixedPitch() const { return fIsFixedPitch; } -void SkTypeface::getGlyphToUnicodeMap(SkSpan<SkUnichar> dst) const { - sk_bzero(dst.data(), dst.size_bytes()); +void SkTypeface::getGlyphToUnicodeMap(SkUnichar* dst) const { + sk_bzero(dst, sizeof(SkUnichar) * this->countGlyphs()); } std::unique_ptr<SkAdvancedTypefaceMetrics> SkTypeface::getAdvancedMetrics() const { @@ -530,7 +531,8 @@ std::unique_ptr<SkAdvancedTypefaceMetrics> SkTypeface::getAdvancedMetrics() cons return result; } -bool SkTypeface::onGetKerningPairAdjustments(SkSpan<const SkGlyphID>, SkSpan<int32_t> adj) const { +bool SkTypeface::onGetKerningPairAdjustments(const SkGlyphID glyphs[], int count, + int32_t adjustments[]) const { return false; } diff --git a/gfx/skia/skia/src/core/SkTypefaceCache.cpp b/gfx/skia/skia/src/core/SkTypefaceCache.cpp @@ -10,7 +10,6 @@ #include "include/core/SkFontStyle.h" #include "include/core/SkGraphics.h" #include "include/core/SkString.h" -#include "include/private/base/SkAssert.h" #include "include/private/base/SkDebug.h" #include "include/private/base/SkMutex.h" @@ -22,7 +21,6 @@ SkTypefaceCache::SkTypefaceCache() {} void SkTypefaceCache::add(sk_sp<SkTypeface> face) { #ifndef SK_DISABLE_TYPEFACE_CACHE - SkASSERT_RELEASE(face); const auto limit = SkGraphics::GetTypefaceCacheCountLimit(); if (fTypefaces.size() >= limit) { diff --git a/gfx/skia/skia/src/core/SkTypeface_remote.cpp b/gfx/skia/skia/src/core/SkTypeface_remote.cpp @@ -22,6 +22,7 @@ class SkArenaAlloc; class SkDescriptor; +class SkPath; SkScalerContextProxy::SkScalerContextProxy(SkTypeface& tf, const SkScalerContextEffects& effects, @@ -56,7 +57,7 @@ void SkScalerContextProxy::generateImage(const SkGlyph& glyph, void*) { SkStrikeClient::CacheMissType::kGlyphImage, fRec.fTextSize); } -std::optional<SkScalerContext::GeneratedPath> SkScalerContextProxy::generatePath(const SkGlyph&) { +bool SkScalerContextProxy::generatePath(const SkGlyph& glyph, SkPath* path, bool* modified) { TRACE_EVENT1("skia", "generatePath", "rec", TRACE_STR_COPY(this->getRec().dump().c_str())); if (this->getProxyTypeface()->isLogging()) { SkDebugf("GlyphCacheMiss generatePath: %s\n", this->getRec().dump().c_str()); @@ -64,7 +65,7 @@ std::optional<SkScalerContext::GeneratedPath> SkScalerContextProxy::generatePath fDiscardableManager->notifyCacheMiss( SkStrikeClient::CacheMissType::kGlyphPath, fRec.fTextSize); - return {}; + return false; } sk_sp<SkDrawable> SkScalerContextProxy::generateDrawable(const SkGlyph&) { diff --git a/gfx/skia/skia/src/core/SkTypeface_remote.h b/gfx/skia/skia/src/core/SkTypeface_remote.h @@ -12,7 +12,6 @@ #include "include/core/SkFontParameters.h" #include "include/core/SkFontStyle.h" #include "include/core/SkRefCnt.h" -#include "include/core/SkSpan.h" #include "include/core/SkString.h" #include "include/core/SkTypeface.h" #include "include/core/SkTypes.h" @@ -29,6 +28,7 @@ class SkDescriptor; class SkDrawable; class SkFontDescriptor; class SkGlyph; +class SkPath; class SkReadBuffer; class SkStreamAsset; class SkTypefaceProxy; @@ -46,7 +46,7 @@ public: protected: GlyphMetrics generateMetrics(const SkGlyph&, SkArenaAlloc*) override; void generateImage(const SkGlyph&, void*) override; - std::optional<GeneratedPath> generatePath(const SkGlyph&) override; + bool generatePath(const SkGlyph& glyph, SkPath* path, bool* modified) override; sk_sp<SkDrawable> generateDrawable(const SkGlyph&) override; void generateFontMetrics(SkFontMetrics* metrics) override; SkTypefaceProxy* getProxyTypeface() const; @@ -118,11 +118,12 @@ protected: bool onGlyphMaskNeedsCurrentColor() const override { return fGlyphMaskNeedsCurrentColor; } - int onGetVariationDesignPosition( - SkSpan<SkFontArguments::VariationPosition::Coordinate>) const override { + int onGetVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[], + int coordinateCount) const override { SK_ABORT("Should never be called."); } - int onGetVariationDesignParameters(SkSpan<SkFontParameters::Variation::Axis>) const override { + int onGetVariationDesignParameters(SkFontParameters::Variation::Axis parameters[], + int parameterCount) const override { SK_ABORT("Should never be called."); } void onGetFamilyName(SkString* familyName) const override { @@ -135,7 +136,7 @@ protected: SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override { SK_ABORT("Should never be called."); } - int onGetTableTags(SkSpan<SkFontTableTag>) const override { + int onGetTableTags(SkFontTableTag tags[]) const override { SK_ABORT("Should never be called."); } size_t onGetTableData(SkFontTableTag, size_t offset, size_t length, void* data) const override { @@ -154,7 +155,7 @@ protected: void onGetFontDescriptor(SkFontDescriptor*, bool*) const override { SK_ABORT("Should never be called."); } - void getGlyphToUnicodeMap(SkSpan<SkUnichar>) const override { + void getGlyphToUnicodeMap(SkUnichar*) const override { SK_ABORT("Should never be called."); } @@ -165,7 +166,7 @@ protected: std::unique_ptr<SkAdvancedTypefaceMetrics> onGetAdvancedMetrics() const override { SK_ABORT("Should never be called."); } - void onCharsToGlyphs(SkSpan<const SkUnichar>, SkSpan<SkGlyphID>) const override { + void onCharsToGlyphs(const SkUnichar* chars, int count, SkGlyphID glyphs[]) const override { SK_ABORT("Should never be called."); } int onCountGlyphs() const override { diff --git a/gfx/skia/skia/src/core/SkVertices.cpp b/gfx/skia/skia/src/core/SkVertices.cpp @@ -152,8 +152,7 @@ void SkVertices::Builder::init(const Desc& desc) { sk_sp<SkVertices> SkVertices::Builder::detach() { if (fVertices) { - fVertices->fBounds = SkRect::BoundsOrEmpty({fVertices->fPositions, - fVertices->fVertexCount}); + fVertices->fBounds.setBounds(fVertices->fPositions, fVertices->fVertexCount); if (fVertices->fMode == kTriangleFan_VertexMode) { if (fIntermediateFanIndices) { SkASSERT(fVertices->fIndexCount); diff --git a/gfx/skia/skia/src/core/SkWriteBuffer.cpp b/gfx/skia/skia/src/core/SkWriteBuffer.cpp @@ -64,18 +64,18 @@ void SkBinaryWriteBuffer::writeScalar(SkScalar value) { fWriter.writeScalar(value); } -void SkBinaryWriteBuffer::writeScalarArray(SkSpan<const SkScalar> values) { - fWriter.write32(SkToInt(values.size())); - fWriter.write(values.data(), values.size_bytes()); +void SkBinaryWriteBuffer::writeScalarArray(const SkScalar* value, uint32_t count) { + fWriter.write32(count); + fWriter.write(value, count * sizeof(SkScalar)); } void SkBinaryWriteBuffer::writeInt(int32_t value) { fWriter.write32(value); } -void SkBinaryWriteBuffer::writeIntArray(SkSpan<const int32_t> values) { - fWriter.write32(SkToInt(values.size())); - fWriter.write(values.data(), values.size_bytes()); +void SkBinaryWriteBuffer::writeIntArray(const int32_t* value, uint32_t count) { + fWriter.write32(count); + fWriter.write(value, count * sizeof(int32_t)); } void SkBinaryWriteBuffer::writeUInt(uint32_t value) { @@ -90,18 +90,18 @@ void SkBinaryWriteBuffer::writeColor(SkColor color) { fWriter.write32(color); } -void SkBinaryWriteBuffer::writeColorArray(SkSpan<const SkColor> values) { - fWriter.write32(SkToInt(values.size())); - fWriter.write(values.data(), values.size_bytes()); +void SkBinaryWriteBuffer::writeColorArray(const SkColor* color, uint32_t count) { + fWriter.write32(count); + fWriter.write(color, count * sizeof(SkColor)); } void SkBinaryWriteBuffer::writeColor4f(const SkColor4f& color) { fWriter.write(&color, sizeof(SkColor4f)); } -void SkBinaryWriteBuffer::writeColor4fArray(SkSpan<const SkColor4f> values) { - fWriter.write32(SkToInt(values.size())); - fWriter.write(values.data(), values.size_bytes()); +void SkBinaryWriteBuffer::writeColor4fArray(const SkColor4f* color, uint32_t count) { + fWriter.write32(count); + fWriter.write(color, count * sizeof(SkColor4f)); } void SkBinaryWriteBuffer::writePoint(const SkPoint& point) { @@ -113,9 +113,9 @@ void SkBinaryWriteBuffer::writePoint3(const SkPoint3& point) { this->writePad32(&point, sizeof(SkPoint3)); } -void SkBinaryWriteBuffer::writePointArray(SkSpan<const SkPoint> values) { - fWriter.write32(SkToInt(values.size())); - fWriter.write(values.data(), values.size_bytes()); +void SkBinaryWriteBuffer::writePointArray(const SkPoint* point, uint32_t count) { + fWriter.write32(count); + fWriter.write(point, count * sizeof(SkPoint)); } void SkBinaryWriteBuffer::write(const SkM44& matrix) { @@ -179,8 +179,9 @@ static sk_sp<SkData> serialize_image(const SkImage* image, SkSerialProcs procs) if (!ib->getROPixels(ib->directContext(), &bm)) { return nullptr; } - if (auto result = SkPngEncoder::Encode(bm.pixmap(), SkPngEncoder::Options())) { - return result; + SkDynamicMemoryWStream stream; + if (SkPngEncoder::Encode(&stream, bm.pixmap(), SkPngEncoder::Options())) { + return stream.detachAsData(); } #endif return nullptr; diff --git a/gfx/skia/skia/src/core/SkWriteBuffer.h b/gfx/skia/skia/src/core/SkWriteBuffer.h @@ -14,7 +14,6 @@ #include "include/core/SkSamplingOptions.h" #include "include/core/SkScalar.h" #include "include/core/SkSerialProcs.h" -#include "include/core/SkSpan.h" #include "src/core/SkTHash.h" #include "src/core/SkWriter32.h" @@ -57,9 +56,9 @@ public: virtual void writeBool(bool value) = 0; virtual void writeScalar(SkScalar value) = 0; - virtual void writeScalarArray(SkSpan<const SkScalar>) = 0; + virtual void writeScalarArray(const SkScalar* value, uint32_t count) = 0; virtual void writeInt(int32_t value) = 0; - virtual void writeIntArray(SkSpan<const int32_t>) = 0; + virtual void writeIntArray(const int32_t* value, uint32_t count) = 0; virtual void writeUInt(uint32_t value) = 0; void write32(int32_t value) { this->writeInt(value); @@ -68,11 +67,11 @@ public: virtual void writeFlattenable(const SkFlattenable* flattenable) = 0; virtual void writeColor(SkColor color) = 0; - virtual void writeColorArray(SkSpan<const SkColor>) = 0; + virtual void writeColorArray(const SkColor* color, uint32_t count) = 0; virtual void writeColor4f(const SkColor4f& color) = 0; - virtual void writeColor4fArray(SkSpan<const SkColor4f>) = 0; + virtual void writeColor4fArray(const SkColor4f* color, uint32_t count) = 0; virtual void writePoint(const SkPoint& point) = 0; - virtual void writePointArray(SkSpan<const SkPoint>) = 0; + virtual void writePointArray(const SkPoint* point, uint32_t count) = 0; virtual void writePoint3(const SkPoint3& point) = 0; virtual void write(const SkM44&) = 0; virtual void writeMatrix(const SkMatrix& matrix) = 0; @@ -121,19 +120,19 @@ public: void writeByteArray(const void* data, size_t size) override; void writeBool(bool value) override; void writeScalar(SkScalar value) override; - void writeScalarArray(SkSpan<const SkScalar>) override; + void writeScalarArray(const SkScalar* value, uint32_t count) override; void writeInt(int32_t value) override; - void writeIntArray(SkSpan<const int32_t>) override; + void writeIntArray(const int32_t* value, uint32_t count) override; void writeUInt(uint32_t value) override; void writeString(std::string_view value) override; void writeFlattenable(const SkFlattenable* flattenable) override; void writeColor(SkColor color) override; - void writeColorArray(SkSpan<const SkColor>) override; + void writeColorArray(const SkColor* color, uint32_t count) override; void writeColor4f(const SkColor4f& color) override; - void writeColor4fArray(SkSpan<const SkColor4f>) override; + void writeColor4fArray(const SkColor4f* color, uint32_t count) override; void writePoint(const SkPoint& point) override; - void writePointArray(SkSpan<const SkPoint>) override; + void writePointArray(const SkPoint* point, uint32_t count) override; void writePoint3(const SkPoint3& point) override; void write(const SkM44&) override; void writeMatrix(const SkMatrix& matrix) override; diff --git a/gfx/skia/skia/src/effects/Sk1DPathEffect.cpp b/gfx/skia/skia/src/effects/Sk1DPathEffect.cpp @@ -10,7 +10,6 @@ #include "include/core/SkFlattenable.h" #include "include/core/SkMatrix.h" #include "include/core/SkPath.h" -#include "include/core/SkPathBuilder.h" #include "include/core/SkPathEffect.h" #include "include/core/SkPathMeasure.h" #include "include/core/SkPoint.h" @@ -32,7 +31,7 @@ struct SkRect; class Sk1DPathEffect : public SkPathEffectBase { public: protected: - bool onFilterPath(SkPathBuilder* builder, const SkPath& src, SkStrokeRec*, const SkRect*, + bool onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec*, const SkRect*, const SkMatrix&) const override { SkPathMeasure meas(src, false); do { @@ -40,7 +39,7 @@ protected: SkScalar length = meas.getLength(); SkScalar distance = this->begin(length); while (distance < length && --governor >= 0) { - SkScalar delta = this->next(builder, distance, meas); + SkScalar delta = this->next(dst, distance, meas); if (delta <= 0) { break; } @@ -62,7 +61,7 @@ protected: Return the distance to travel for the next call. If return <= 0, then that contour is done. */ - virtual SkScalar next(SkPathBuilder* dst, SkScalar dist, SkPathMeasure&) const = 0; + virtual SkScalar next(SkPath* dst, SkScalar dist, SkPathMeasure&) const = 0; private: // For simplicity, assume fast bounds cannot be computed @@ -105,30 +104,25 @@ public: fStyle = style; } - bool onFilterPath(SkPathBuilder* builder, const SkPath& src, SkStrokeRec* rec, + bool onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec* rec, const SkRect* cullRect, const SkMatrix& ctm) const override { rec->setFillStyle(); - return this->INHERITED::onFilterPath(builder, src, rec, cullRect, ctm); + return this->INHERITED::onFilterPath(dst, src, rec, cullRect, ctm); } SkScalar begin(SkScalar contourLength) const override { return fInitialOffset; } - SkScalar next(SkPathBuilder*, SkScalar, SkPathMeasure&) const override; + SkScalar next(SkPath*, SkScalar, SkPathMeasure&) const override; static sk_sp<SkFlattenable> CreateProc(SkReadBuffer& buffer) { - sk_sp<SkFlattenable> result; - SkScalar advance = buffer.readScalar(); - if (auto path = buffer.readPath()) { - SkScalar phase = buffer.readScalar(); - SkPath1DPathEffect::Style style = buffer.read32LE(SkPath1DPathEffect::kLastEnum_Style); - if (buffer.isValid()) { - result = SkPath1DPathEffect::Make(*path, advance, phase, style); - } - } - return result; + SkPath path; + buffer.readPath(&path); + SkScalar phase = buffer.readScalar(); + SkPath1DPathEffect::Style style = buffer.read32LE(SkPath1DPathEffect::kLastEnum_Style); + return buffer.isValid() ? SkPath1DPathEffect::Make(path, advance, phase, style) : nullptr; } void flatten(SkWriteBuffer& buffer) const override { @@ -150,10 +144,9 @@ private: using INHERITED = Sk1DPathEffect; }; -static bool morphpoints(SkSpan<SkPoint> dst, SkSpan<const SkPoint> src, +static bool morphpoints(SkPoint dst[], const SkPoint src[], int count, SkPathMeasure& meas, SkScalar dist) { - SkASSERT(dst.size() >= src.size()); - for (size_t i = 0; i < src.size(); i++) { + for (int i = 0; i < count; i++) { SkPoint pos; SkVector tangent; @@ -171,7 +164,7 @@ static bool morphpoints(SkSpan<SkPoint> dst, SkSpan<const SkPoint> src, matrix.setSinCos(tangent.fY, tangent.fX, 0, 0); matrix.preTranslate(-sx, 0); matrix.postTranslate(pos.fX, pos.fY); - dst[i] = matrix.mapPoint(pt); + matrix.mapPoints(&dst[i], &pt, 1); } return true; } @@ -182,52 +175,53 @@ Need differentially more subdivisions when the follow-path is curvy. Not sure ho determine that, but we need it. I guess a cheap answer is let the caller tell us, but that seems like a cop-out. Another answer is to get Rob Johnson to figure it out. */ -static void morphpath(SkPathBuilder* dst, const SkPath& src, SkPathMeasure& meas, +static void morphpath(SkPath* dst, const SkPath& src, SkPathMeasure& meas, SkScalar dist) { SkPath::Iter iter(src, false); - SkPoint dstP[3], scratch[3]; + SkPoint srcP[4], dstP[3]; + SkPath::Verb verb; - while (auto rec = iter.next()) { - SkSpan<const SkPoint> srcP = rec->fPoints; - switch (rec->fVerb) { - case SkPathVerb::kMove: - if (morphpoints(dstP, srcP, meas, dist)) { + while ((verb = iter.next(srcP)) != SkPath::kDone_Verb) { + switch (verb) { + case SkPath::kMove_Verb: + if (morphpoints(dstP, srcP, 1, meas, dist)) { dst->moveTo(dstP[0]); } break; - case SkPathVerb::kLine: - scratch[0] = srcP[0]; - scratch[1].set(SkScalarAve(srcP[0].fX, srcP[1].fX), - SkScalarAve(srcP[0].fY, srcP[1].fY)); - scratch[2] = srcP[1]; - srcP = scratch; // now we look like a quad + case SkPath::kLine_Verb: + srcP[2] = srcP[1]; + srcP[1].set(SkScalarAve(srcP[0].fX, srcP[2].fX), + SkScalarAve(srcP[0].fY, srcP[2].fY)); [[fallthrough]]; - case SkPathVerb::kQuad: - if (morphpoints(dstP, srcP.subspan(1), meas, dist)) { + case SkPath::kQuad_Verb: + if (morphpoints(dstP, &srcP[1], 2, meas, dist)) { dst->quadTo(dstP[0], dstP[1]); } break; - case SkPathVerb::kConic: - if (morphpoints(dstP, srcP.subspan(1), meas, dist)) { - dst->conicTo(dstP[0], dstP[1], rec->conicWeight()); + case SkPath::kConic_Verb: + if (morphpoints(dstP, &srcP[1], 2, meas, dist)) { + dst->conicTo(dstP[0], dstP[1], iter.conicWeight()); } break; - case SkPathVerb::kCubic: - if (morphpoints(dstP, srcP.subspan(1), meas, dist)) { + case SkPath::kCubic_Verb: + if (morphpoints(dstP, &srcP[1], 3, meas, dist)) { dst->cubicTo(dstP[0], dstP[1], dstP[2]); } break; - case SkPathVerb::kClose: + case SkPath::kClose_Verb: dst->close(); break; + default: + SkDEBUGFAIL("unknown verb"); + break; } } } -SkScalar SkPath1DPathEffectImpl::next(SkPathBuilder* builder, SkScalar distance, +SkScalar SkPath1DPathEffectImpl::next(SkPath* dst, SkScalar distance, SkPathMeasure& meas) const { #if defined(SK_BUILD_FOR_FUZZER) - if (builder->countPoints() > 100000) { + if (dst->countPoints() > 100000) { return fAdvance; } #endif @@ -235,17 +229,17 @@ SkScalar SkPath1DPathEffectImpl::next(SkPathBuilder* builder, SkScalar distance, case SkPath1DPathEffect::kTranslate_Style: { SkPoint pos; if (meas.getPosTan(distance, &pos, nullptr)) { - builder->addPath(fPath, pos.fX, pos.fY); + dst->addPath(fPath, pos.fX, pos.fY); } } break; case SkPath1DPathEffect::kRotate_Style: { SkMatrix matrix; if (meas.getMatrix(distance, &matrix)) { - builder->addPath(fPath, matrix); + dst->addPath(fPath, matrix); } } break; case SkPath1DPathEffect::kMorph_Style: - morphpath(builder, fPath, meas, distance); + morphpath(dst, fPath, meas, distance); break; } return fAdvance; diff --git a/gfx/skia/skia/src/effects/Sk2DPathEffect.cpp b/gfx/skia/skia/src/effects/Sk2DPathEffect.cpp @@ -10,7 +10,6 @@ #include "include/core/SkFlattenable.h" #include "include/core/SkMatrix.h" #include "include/core/SkPath.h" -#include "include/core/SkPathBuilder.h" #include "include/core/SkPathEffect.h" #include "include/core/SkPoint.h" #include "include/core/SkRect.h" @@ -37,15 +36,15 @@ protected: next() will receive u and v values within these bounds, and then a call to end() will signal the end of processing. */ - virtual void begin(const SkIRect& uvBounds, SkPathBuilder* dst) const {} - virtual void next(const SkPoint& loc, int u, int v, SkPathBuilder* dst) const {} - virtual void end(SkPathBuilder* dst) const {} + virtual void begin(const SkIRect& uvBounds, SkPath* dst) const {} + virtual void next(const SkPoint& loc, int u, int v, SkPath* dst) const {} + virtual void end(SkPath* dst) const {} /** Low-level virtual called per span of locations in the u-direction. The default implementation calls next() repeatedly with each location. */ - virtual void nextSpan(int x, int y, int ucount, SkPathBuilder* builder) const { + virtual void nextSpan(int x, int y, int ucount, SkPath* path) const { if (!fMatrixIsInvertible) { return; } @@ -60,8 +59,8 @@ protected: src.set(SkIntToScalar(x) + SK_ScalarHalf, SkIntToScalar(y) + SK_ScalarHalf); do { - dst = mat.mapPoint(src); - this->next(dst, x++, y, builder); + mat.mapPoints(&dst, &src, 1); + this->next(dst, x++, y, path); src.fX += SK_Scalar1; } while (--ucount > 0); } @@ -72,14 +71,17 @@ protected: buffer.writeMatrix(fMatrix); } - bool onFilterPath(SkPathBuilder* dst, const SkPath& src, SkStrokeRec* rec, + bool onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec* rec, const SkRect* cullRect, const SkMatrix&) const override { if (!fMatrixIsInvertible) { return false; } - SkPath tmp = src.makeTransform(fInverse); - SkIRect ir = tmp.getBounds().round(); + SkPath tmp; + SkIRect ir; + + src.transform(fInverse, &tmp); + tmp.getBounds().round(&ir); if (!ir.isEmpty()) { this->begin(ir, dst); @@ -124,7 +126,7 @@ public: SkASSERT(width >= 0); } - bool onFilterPath(SkPathBuilder* dst, const SkPath& src, SkStrokeRec* rec, + bool onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec* rec, const SkRect* cullRect, const SkMatrix& ctm) const override { if (this->INHERITED::onFilterPath(dst, src, rec, cullRect, ctm)) { rec->setStrokeStyle(fWidth); @@ -133,13 +135,13 @@ public: return false; } - void nextSpan(int u, int v, int ucount, SkPathBuilder* dst) const override { + void nextSpan(int u, int v, int ucount, SkPath* dst) const override { if (ucount > 1) { SkPoint src[2], dstP[2]; src[0].set(SkIntToScalar(u) + SK_ScalarHalf, SkIntToScalar(v) + SK_ScalarHalf); src[1].set(SkIntToScalar(u+ucount) + SK_ScalarHalf, SkIntToScalar(v) + SK_ScalarHalf); - this->getMatrix().mapPoints(dstP, src); + this->getMatrix().mapPoints(dstP, src, 2); dst->moveTo(dstP[0]); dst->lineTo(dstP[1]); @@ -173,17 +175,16 @@ class SkPath2DPathEffectImpl : public Sk2DPathEffect { public: SkPath2DPathEffectImpl(const SkMatrix& m, const SkPath& p) : INHERITED(m), fPath(p) {} - void next(const SkPoint& loc, int u, int v, SkPathBuilder* dst) const override { + void next(const SkPoint& loc, int u, int v, SkPath* dst) const override { dst->addPath(fPath, loc.fX, loc.fY); } static sk_sp<SkFlattenable> CreateProc(SkReadBuffer& buffer) { SkMatrix matrix; buffer.readMatrix(&matrix); - if (auto path = buffer.readPath()) { - return SkPath2DPathEffect::Make(matrix, *path); - } - return nullptr; + SkPath path; + buffer.readPath(&path); + return SkPath2DPathEffect::Make(matrix, path); } void flatten(SkWriteBuffer& buffer) const override { diff --git a/gfx/skia/skia/src/effects/SkCornerPathEffect.cpp b/gfx/skia/skia/src/effects/SkCornerPathEffect.cpp @@ -9,7 +9,6 @@ #include "include/core/SkFlattenable.h" #include "include/core/SkPath.h" -#include "include/core/SkPathBuilder.h" #include "include/core/SkPathEffect.h" #include "include/core/SkPoint.h" #include "include/core/SkRefCnt.h" @@ -44,14 +43,15 @@ public: SkASSERT(radius > 0); } - bool onFilterPath(SkPathBuilder* dst, const SkPath& src, SkStrokeRec*, const SkRect*, + bool onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec*, const SkRect*, const SkMatrix&) const override { if (fRadius <= 0) { return false; } - // just need a value that won't match when we compare it initially - SkPathVerb prevVerb = static_cast<SkPathVerb>(0xFF); + SkPath::Iter iter(src, false); + SkPath::Verb verb, prevVerb = SkPath::kDone_Verb; + SkPoint pts[4]; bool closed; SkPoint moveTo, lastCorner; @@ -64,13 +64,11 @@ public: firstStep.set(0, 0); lastCorner.set(0, 0); - SkPath::Iter iter(src, false); - while (auto rec = iter.next()) { - SkSpan<const SkPoint> pts = rec->fPoints; - switch (rec->fVerb) { - case SkPathVerb::kMove: + for (;;) { + switch (verb = iter.next(pts)) { + case SkPath::kMove_Verb: // close out the previous (open) contour - if (SkPathVerb::kLine == prevVerb) { + if (SkPath::kLine_Verb == prevVerb) { dst->lineTo(lastCorner); } closed = iter.isClosedContour(); @@ -82,7 +80,7 @@ public: prevIsValid = true; } break; - case SkPathVerb::kLine: { + case SkPath::kLine_Verb: { bool drawSegment = ComputeStep(pts[0], pts[1], fRadius, &step); // prev corner if (!prevIsValid) { @@ -99,7 +97,7 @@ public: prevIsValid = true; break; } - case SkPathVerb::kQuad: + case SkPath::kQuad_Verb: // TBD - just replicate the curve for now if (!prevIsValid) { dst->moveTo(pts[0]); @@ -109,17 +107,17 @@ public: lastCorner = pts[2]; firstStep.set(0, 0); break; - case SkPathVerb::kConic: + case SkPath::kConic_Verb: // TBD - just replicate the curve for now if (!prevIsValid) { dst->moveTo(pts[0]); prevIsValid = true; } - dst->conicTo(pts[1], pts[2], rec->conicWeight()); + dst->conicTo(pts[1], pts[2], iter.conicWeight()); lastCorner = pts[2]; firstStep.set(0, 0); break; - case SkPathVerb::kCubic: + case SkPath::kCubic_Verb: if (!prevIsValid) { dst->moveTo(pts[0]); prevIsValid = true; @@ -129,7 +127,7 @@ public: lastCorner = pts[3]; firstStep.set(0, 0); break; - case SkPathVerb::kClose: + case SkPath::kClose_Verb: if (firstStep.fX || firstStep.fY) { dst->quadTo(lastCorner.fX, lastCorner.fY, lastCorner.fX + firstStep.fX, @@ -138,16 +136,21 @@ public: dst->close(); prevIsValid = false; break; + case SkPath::kDone_Verb: + if (prevIsValid) { + dst->lineTo(lastCorner); + } + return true; + default: + SkDEBUGFAIL("default should not be reached"); + return false; } - if (SkPathVerb::kMove == prevVerb) { + + if (SkPath::kMove_Verb == prevVerb) { firstStep = step; } - prevVerb = rec->fVerb; + prevVerb = verb; } - if (prevIsValid) { - dst->lineTo(lastCorner); - } - return true; } bool computeFastBounds(SkRect*) const override { diff --git a/gfx/skia/skia/src/effects/SkDashImpl.h b/gfx/skia/skia/src/effects/SkDashImpl.h @@ -8,25 +8,22 @@ #ifndef SkDashImpl_DEFINED #define SkDashImpl_DEFINED -#include "include/core/SkSpan.h" -#include "include/private/base/SkTemplates.h" #include "src/core/SkPathEffectBase.h" -#include <optional> - class SkDashImpl : public SkPathEffectBase { public: - SkDashImpl(SkSpan<const SkScalar> intervals, SkScalar phase); + SkDashImpl(const SkScalar intervals[], int count, SkScalar phase); protected: + ~SkDashImpl() override; void flatten(SkWriteBuffer&) const override; - bool onFilterPath(SkPathBuilder* dst, const SkPath& src, SkStrokeRec*, const SkRect*, + bool onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec*, const SkRect*, const SkMatrix&) const override; bool onAsPoints(PointData* results, const SkPath& src, const SkStrokeRec&, const SkMatrix&, const SkRect*) const override; - std::optional<DashInfo> asADash() const override; + DashType asADash(DashInfo* info) const override; private: SK_FLATTENABLE_HOOKS(SkDashImpl) @@ -37,13 +34,14 @@ private: return true; } - skia_private::AutoTArray<SkScalar> fIntervals; - SkScalar fPhase; - + SkScalar* fIntervals; + int32_t fCount; + SkScalar fPhase; // computed from phase + SkScalar fInitialDashLength; + int32_t fInitialDashIndex; SkScalar fIntervalLength; - size_t fInitialDashIndex; using INHERITED = SkPathEffectBase; }; diff --git a/gfx/skia/skia/src/effects/SkDashPathEffect.cpp b/gfx/skia/skia/src/effects/SkDashPathEffect.cpp @@ -17,7 +17,9 @@ #include "include/core/SkStrokeRec.h" #include "include/private/base/SkAlign.h" #include "include/private/base/SkFloatingPoint.h" +#include "include/private/base/SkMalloc.h" #include "include/private/base/SkTemplates.h" +#include "include/private/base/SkTo.h" #include "src/core/SkPathEffectBase.h" #include "src/core/SkReadBuffer.h" #include "src/core/SkWriteBuffer.h" @@ -27,31 +29,35 @@ #include <algorithm> #include <cstdint> #include <cstring> -#include <optional> - -class SkPathBuilder; using namespace skia_private; -SkDashImpl::SkDashImpl(SkSpan<const SkScalar> intervals, SkScalar phase) - : fIntervals(intervals.size()) - , fPhase(0) +SkDashImpl::SkDashImpl(const SkScalar intervals[], int count, SkScalar phase) + : fPhase(0) , fInitialDashLength(-1) - , fIntervalLength(0) , fInitialDashIndex(0) -{ - SkASSERT(intervals.size() > 1 && SkIsAlign2(intervals.size())); - - memcpy(fIntervals.data(), intervals.data(), intervals.size_bytes()); + , fIntervalLength(0) { + SkASSERT(intervals); + SkASSERT(count > 1 && SkIsAlign2(count)); + + fIntervals = (SkScalar*)sk_malloc_throw(sizeof(SkScalar) * count); + fCount = count; + for (int i = 0; i < count; i++) { + fIntervals[i] = intervals[i]; + } // set the internal data members - SkDashPath::CalcDashParameters(phase, fIntervals, + SkDashPath::CalcDashParameters(phase, fIntervals, fCount, &fInitialDashLength, &fInitialDashIndex, &fIntervalLength, &fPhase); } -bool SkDashImpl::onFilterPath(SkPathBuilder* builder, const SkPath& src, SkStrokeRec* rec, +SkDashImpl::~SkDashImpl() { + sk_free(fIntervals); +} + +bool SkDashImpl::onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec* rec, const SkRect* cullRect, const SkMatrix&) const { - return SkDashPath::InternalFilter(builder, src, rec, cullRect, fIntervals, + return SkDashPath::InternalFilter(dst, src, rec, cullRect, fIntervals, fCount, fInitialDashLength, fInitialDashIndex, fIntervalLength, fPhase); } @@ -186,7 +192,7 @@ bool SkDashImpl::onAsPoints(PointData* results, const SkPath& src, const SkStrok // Additionally, they do not necessarily need to be integers. // We cannot allow arbitrary intervals since we want the returned points // to be uniformly sized. - if (fIntervals.size() != 2 || + if (fCount != 2 || !SkScalarNearlyEqual(fIntervals[0], fIntervals[1]) || !SkScalarIsInt(fIntervals[0]) || !SkScalarIsInt(fIntervals[1])) { @@ -308,8 +314,8 @@ bool SkDashImpl::onAsPoints(PointData* results, const SkPath& src, const SkStrok } if (clampedInitialDashLength < fIntervals[0]) { // This one will not be like the others - results->fFirst = SkPath::Rect({x - halfWidth, y - halfHeight, - x + halfWidth, y + halfHeight}); + results->fFirst.addRect(x - halfWidth, y - halfHeight, + x + halfWidth, y + halfHeight); } else { SkASSERT(curPt < results->fNumPoints); results->fPoints[curPt].set(x, y); @@ -357,8 +363,8 @@ bool SkDashImpl::onAsPoints(PointData* results, const SkPath& src, const SkStrok halfWidth = SkScalarHalf(rec.getWidth()); halfHeight = SkScalarHalf(temp); } - results->fLast = SkPath::Rect({x - halfWidth, y - halfHeight, - x + halfWidth, y + halfHeight}); + results->fLast.addRect(x - halfWidth, y - halfHeight, + x + halfWidth, y + halfHeight); } SkASSERT(curPt == results->fNumPoints); @@ -367,13 +373,20 @@ bool SkDashImpl::onAsPoints(PointData* results, const SkPath& src, const SkStrok return true; } -std::optional<SkPathEffectBase::DashInfo> SkDashImpl::asADash() const { - return {{fIntervals, fPhase}}; +SkPathEffectBase::DashType SkDashImpl::asADash(DashInfo* info) const { + if (info) { + if (info->fCount >= fCount && info->fIntervals) { + memcpy(info->fIntervals, fIntervals, fCount * sizeof(SkScalar)); + } + info->fCount = fCount; + info->fPhase = fPhase; + } + return DashType::kDash; } void SkDashImpl::flatten(SkWriteBuffer& buffer) const { buffer.writeScalar(fPhase); - buffer.writeScalarArray(fIntervals); + buffer.writeScalarArray(fIntervals, fCount); } sk_sp<SkFlattenable> SkDashImpl::CreateProc(SkReadBuffer& buffer) { @@ -386,17 +399,17 @@ sk_sp<SkFlattenable> SkDashImpl::CreateProc(SkReadBuffer& buffer) { } AutoSTArray<32, SkScalar> intervals(count); - if (buffer.readScalarArray(intervals)) { - return SkDashPathEffect::Make(intervals, phase); + if (buffer.readScalarArray(intervals.get(), count)) { + return SkDashPathEffect::Make(intervals.get(), SkToInt(count), phase); } return nullptr; } ////////////////////////////////////////////////////////////////////////////////////////////////// -sk_sp<SkPathEffect> SkDashPathEffect::Make(SkSpan<const SkScalar> intervals, SkScalar phase) { - if (!SkDashPath::ValidDashPath(phase, intervals)) { +sk_sp<SkPathEffect> SkDashPathEffect::Make(const SkScalar intervals[], int count, SkScalar phase) { + if (!SkDashPath::ValidDashPath(phase, intervals, count)) { return nullptr; } - return sk_sp<SkPathEffect>(new SkDashImpl(intervals, phase)); + return sk_sp<SkPathEffect>(new SkDashImpl(intervals, count, phase)); } diff --git a/gfx/skia/skia/src/effects/SkDiscretePathEffect.cpp b/gfx/skia/skia/src/effects/SkDiscretePathEffect.cpp @@ -6,7 +6,7 @@ */ #include "include/core/SkFlattenable.h" -#include "include/core/SkPathBuilder.h" +#include "include/core/SkPath.h" #include "include/core/SkPathEffect.h" #include "include/core/SkPathMeasure.h" #include "include/core/SkPoint.h" @@ -27,7 +27,6 @@ #include <cstdint> class SkMatrix; -class SkPath; /** \class LCGRandom @@ -87,7 +86,7 @@ public: SkASSERT(segLength > SK_ScalarNearlyZero); } - bool onFilterPath(SkPathBuilder* dst, const SkPath& src, SkStrokeRec* rec, + bool onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec* rec, const SkRect*, const SkMatrix&) const override { bool doFill = rec->isFillStyle(); diff --git a/gfx/skia/skia/src/effects/SkEmbossMaskFilter.cpp b/gfx/skia/skia/src/effects/SkEmbossMaskFilter.cpp @@ -7,17 +7,11 @@ #include "src/effects/SkEmbossMaskFilter.h" -#include "include/core/SkBlendMode.h" #include "include/core/SkBlurTypes.h" -#include "include/core/SkColorSpace.h" -#include "include/core/SkImageFilter.h" #include "include/core/SkMatrix.h" -#include "include/core/SkPaint.h" #include "include/core/SkPoint.h" #include "include/core/SkPoint3.h" -#include "include/core/SkShader.h" #include "include/core/SkTypes.h" -#include "include/effects/SkImageFilters.h" #include "include/private/base/SkFloatingPoint.h" #include "src/core/SkBlurMask.h" #include "src/core/SkReadBuffer.h" @@ -118,8 +112,8 @@ bool SkEmbossMaskFilter::filterMask(SkMaskBuilder* dst, const SkMask& src, // run the light direction through the matrix... Light light = fLight; - matrix.mapVectors({(SkVector*)(void*)light.fDirection, 1}, - {(SkVector*)(void*)fLight.fDirection, 1}); + matrix.mapVectors((SkVector*)(void*)light.fDirection, + (SkVector*)(void*)fLight.fDirection, 1); // now restore the length of the XY component // cast to SkVector so we can call setLength (this double cast silences alias warnings) @@ -152,77 +146,3 @@ void SkEmbossMaskFilter::flatten(SkWriteBuffer& buffer) const { buffer.writeByteArray(&tmpLight, sizeof(tmpLight)); buffer.writeScalar(fBlurSigma); } - -// This image filter uses coverage masks for operations but affects shading properties -// of a draw using the paint parameter, and returning true to indicate appliesShading. -std::pair<sk_sp<SkImageFilter>, bool> SkEmbossMaskFilter::asImageFilter( - const SkMatrix& ctm, const SkPaint& paint) const { - // Here the original bitmap we are operating on (nullptr for imageFilters) should be - // our coverage mask, as a white RGBA8 image where the alpha corresponds to the coverage. - sk_sp<SkImageFilter> coverageBlurred = SkImageFilters::Blur(fBlurSigma, fBlurSigma, nullptr); - - // The paint should have the original shading properties that we want to apply. - sk_sp<SkShader> srcShader = SkShaders::Color(paint.getColor4f(), /*cs=*/nullptr); - if (paint.getShader()) { - srcShader = SkShaders::Blend(SkBlendMode::kDstIn, paint.refShader(), std::move(srcShader)); - } - srcShader = srcShader->makeWithColorFilter(paint.refColorFilter()); - sk_sp<SkImageFilter> srcColor = SkImageFilters::Shader( - std::move(srcShader), paint.isDither() ? SkImageFilters::Dither::kYes - : SkImageFilters::Dither::kNo); - - // ka = fLight.fAmbient - float ambientf = fLight.fAmbient / 255.f; - SkColor4f ambientColor = {ambientf, ambientf, ambientf, 1}; - sk_sp<SkImageFilter> ambient = SkImageFilters::Shader(SkShaders::Color(ambientColor, nullptr)); - - // L = fLight.fDirection - SkPoint3 lightDirection = SkPoint3::Make(fLight.fDirection[0], - fLight.fDirection[1], - fLight.fDirection[2]); - - - // Amount to scale the alpha by to calculate N, set this way to mimic the legacy - // emboss mask filter implementation. - // Made negative to match functionality of legacy emboss mask filter which calculates - // the normal "into" the monitor, away from the user, whereas all other documentation - // points normals towards negative directions (towards user). - const float surfaceScale = -255.f/ 32.f; - - // diffuse = kd * dot(L, N) - sk_sp<SkImageFilter> diffuseCF = SkImageFilters::DistantLitDiffuse(lightDirection, - SK_ColorWHITE, - surfaceScale, - 1, - coverageBlurred); - // mul = ka + diffuse - sk_sp<SkImageFilter> ambientdiffuse = SkImageFilters::Blend(SkBlendMode::kPlus, - diffuseCF, - ambient); - // ambientdiffuseColor = srcColor * mul - sk_sp<SkImageFilter> ambientdiffuseBlend = SkImageFilters::Blend( - SkBlendMode::kModulate, srcColor, ambientdiffuse); - - // fLight.fSpecular is in a fixed 4.4 format. - // This uses the legacy implementation for emboss which calculates the specular - // lighting differently than standard specular functions. - // - // specular = ks * pow((2 * (L * N) - L_z) * L_z), shininess) - float shininess = ((fLight.fSpecular >> 4) + 1); - - sk_sp<SkImageFilter> specular = LegacySpecular(lightDirection, - SK_ColorWHITE, - surfaceScale, - 1, - shininess, - coverageBlurred); - - // dstColor = ambientdiffuseColor + specular - // = srcColor * (ka + kd * dot(L, N)) + ks * pow((2 * (L * N) - L_z) * L_z), shininess) - sk_sp<SkImageFilter> finalFilter = SkImageFilters::Blend(SkBlendMode::kPlus, - ambientdiffuseBlend, - specular); - // Mask by original coverage mask, it remains unchanged. - // Return true to indicate applies shading. - return {SkImageFilters::Blend(SkBlendMode::kDstIn, finalFilter, nullptr), true}; -} diff --git a/gfx/skia/skia/src/effects/SkEmbossMaskFilter.h b/gfx/skia/skia/src/effects/SkEmbossMaskFilter.h @@ -8,7 +8,6 @@ #ifndef SkEmbossMaskFilter_DEFINED #define SkEmbossMaskFilter_DEFINED -#include "include/core/SkColor.h" #include "include/core/SkFlattenable.h" #include "include/core/SkMaskFilter.h" #include "include/core/SkRefCnt.h" @@ -17,15 +16,11 @@ #include "src/core/SkMaskFilterBase.h" #include <cstdint> -#include <utility> -class SkImageFilter; class SkMatrix; -class SkPaint; class SkReadBuffer; class SkWriteBuffer; struct SkIPoint; -struct SkPoint3; /** \class SkEmbossMaskFilter @@ -49,8 +44,6 @@ public: bool filterMask(SkMaskBuilder* dst, const SkMask& src, const SkMatrix&, SkIPoint* margin) const override; SkMaskFilterBase::Type type() const override { return SkMaskFilterBase::Type::kEmboss; } - std::pair<sk_sp<SkImageFilter>, bool> asImageFilter(const SkMatrix& ctm, - const SkPaint& paint) const override; protected: SkEmbossMaskFilter(SkScalar blurSigma, const Light& light); @@ -59,28 +52,7 @@ protected: private: SK_FLATTENABLE_HOOKS(SkEmbossMaskFilter) - /** - * Create a filter that calculates the specular illumination from a distant light source, - * interpreting the alpha channel of the input as the height profile of the surface (to - * approximate normal vectors). This is based on the legacy raster implementation of the - * emboss mask filter for clients that still use it. - * @param direction The direction to the distance light. - * @param lightColor The color of the specular light source. - * @param surfaceScale Scale factor to transform from alpha values to physical height. - * @param ks Specular reflectance coefficient. - * @param shininess The specular exponent determining how shiny the surface is. - * @param input The input filter that defines surface normals (as alpha), or uses the - * source bitmap when null. - * @param cropRect Optional rectangle that crops the input and output. - * - * Defined in SkLightingImageFilter.cpp because it overlaps heavily with - * SkImageFilters::DistantLitSpecular and that family of functions. - */ - static sk_sp<SkImageFilter> LegacySpecular(const SkPoint3& direction, SkColor lightColor, - SkScalar surfaceScale, SkScalar ks, - SkScalar shininess, sk_sp<SkImageFilter> input); - - Light fLight; + Light fLight; SkScalar fBlurSigma; using INHERITED = SkMaskFilter; diff --git a/gfx/skia/skia/src/effects/SkShaderMaskFilterImpl.cpp b/gfx/skia/skia/src/effects/SkShaderMaskFilterImpl.cpp @@ -11,7 +11,6 @@ #include "include/core/SkBlendMode.h" #include "include/core/SkCanvas.h" #include "include/core/SkFlattenable.h" -#include "include/core/SkImageFilter.h" #include "include/core/SkMaskFilter.h" #include "include/core/SkPaint.h" #include "include/core/SkPoint.h" @@ -19,7 +18,6 @@ #include "include/core/SkRefCnt.h" #include "include/core/SkScalar.h" #include "include/core/SkShader.h" -#include "include/effects/SkImageFilters.h" #include "include/effects/SkShaderMaskFilter.h" #include "src/core/SkMask.h" #include "src/core/SkReadBuffer.h" @@ -93,12 +91,6 @@ bool SkShaderMaskFilterImpl::filterMask(SkMaskBuilder* dst, const SkMask& src, c return true; } -std::pair<sk_sp<SkImageFilter>, bool> SkShaderMaskFilterImpl::asImageFilter(const SkMatrix&, - const SkPaint&) const { - sk_sp<SkImageFilter> filter = SkImageFilters::Shader(fShader); - return {SkImageFilters::Blend(SkBlendMode::kDstIn, std::move(filter), nullptr), false}; -} - sk_sp<SkMaskFilter> SkShaderMaskFilter::Make(sk_sp<SkShader> shader) { return shader ? sk_sp<SkMaskFilter>(new SkShaderMaskFilterImpl(std::move(shader))) : nullptr; } diff --git a/gfx/skia/skia/src/effects/SkShaderMaskFilterImpl.h b/gfx/skia/skia/src/effects/SkShaderMaskFilterImpl.h @@ -17,9 +17,7 @@ #include <utility> -class SkImageFilter; class SkMatrix; -class SkPaint; class SkReadBuffer; class SkWriteBuffer; struct SkIPoint; @@ -33,8 +31,6 @@ public: bool filterMask(SkMaskBuilder* dst, const SkMask& src, const SkMatrix&, SkIPoint* margin) const override; - std::pair<sk_sp<SkImageFilter>, bool> asImageFilter(const SkMatrix&, - const SkPaint&) const override; void computeFastBounds(const SkRect& src, SkRect* dst) const override { *dst = src; diff --git a/gfx/skia/skia/src/effects/SkTableMaskFilter.cpp b/gfx/skia/skia/src/effects/SkTableMaskFilter.cpp @@ -29,10 +29,8 @@ #include <cmath> #include <cstdint> #include <cstring> -#include <utility> class SkMatrix; -class SkPaint; class SkTableMaskFilterImpl : public SkMaskFilterBase { public: @@ -41,8 +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; } - std::pair<sk_sp<SkImageFilter>, bool> asImageFilter(const SkMatrix&, - const SkPaint&) const override; + sk_sp<SkImageFilter> asImageFilter(const SkMatrix&) const override; protected: ~SkTableMaskFilterImpl() override; @@ -131,13 +128,12 @@ sk_sp<SkFlattenable> SkTableMaskFilterImpl::CreateProc(SkReadBuffer& buffer) { return sk_sp<SkFlattenable>(SkTableMaskFilter::Create(table)); } -std::pair<sk_sp<SkImageFilter>, bool> SkTableMaskFilterImpl::asImageFilter(const SkMatrix&, - const SkPaint&) const { +sk_sp<SkImageFilter> SkTableMaskFilterImpl::asImageFilter(const SkMatrix&) const { sk_sp<SkColorFilter> colorFilter = SkColorFilters::TableARGB(fTable, nullptr, nullptr, nullptr); - return std::make_pair(SkImageFilters::ColorFilter(colorFilter, nullptr), false); + return SkImageFilters::ColorFilter(colorFilter, nullptr); } /////////////////////////////////////////////////////////////////////////////// diff --git a/gfx/skia/skia/src/effects/SkTrimPE.h b/gfx/skia/skia/src/effects/SkTrimPE.h @@ -17,7 +17,7 @@ public: protected: void flatten(SkWriteBuffer&) const override; - bool onFilterPath(SkPathBuilder* dst, const SkPath& src, SkStrokeRec*, const SkRect*, + bool onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec*, const SkRect*, const SkMatrix&) const override; private: diff --git a/gfx/skia/skia/src/effects/SkTrimPathEffect.cpp b/gfx/skia/skia/src/effects/SkTrimPathEffect.cpp @@ -24,14 +24,13 @@ #include <cstdint> class SkMatrix; -class SkPathBuilder; class SkStrokeRec; struct SkRect; namespace { // Returns the number of contours iterated to satisfy the request. -static size_t add_segments(const SkPath& src, SkScalar start, SkScalar stop, SkPathBuilder* dst, +static size_t add_segments(const SkPath& src, SkScalar start, SkScalar stop, SkPath* dst, bool requires_moveto = true) { SkASSERT(start < stop); @@ -64,7 +63,7 @@ static size_t add_segments(const SkPath& src, SkScalar start, SkScalar stop, SkP SkTrimPE::SkTrimPE(SkScalar startT, SkScalar stopT, SkTrimPathEffect::Mode mode) : fStartT(startT), fStopT(stopT), fMode(mode) {} -bool SkTrimPE::onFilterPath(SkPathBuilder* dst, const SkPath& src, SkStrokeRec*, const SkRect*, +bool SkTrimPE::onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec*, const SkRect*, const SkMatrix&) const { if (fStartT >= fStopT) { SkASSERT(fMode == SkTrimPathEffect::Mode::kNormal); diff --git a/gfx/skia/skia/src/effects/colorfilters/SkColorFilterBase.cpp b/gfx/skia/skia/src/effects/colorfilters/SkColorFilterBase.cpp @@ -37,13 +37,7 @@ SkPMColor4f SkColorFilterBase::onFilterColor4f(const SkPMColor4f& color, SkRasterPipeline pipeline(&alloc); pipeline.appendConstantColor(&alloc, color.vec()); SkSurfaceProps props{}; // default OK; colorFilters don't render text - SkStageRec rec = {&pipeline, - &alloc, - kRGBA_F32_SkColorType, - dstCS, - color.unpremul(), - props, - SkRect::MakeEmpty()}; + SkStageRec rec = {&pipeline, &alloc, kRGBA_F32_SkColorType, dstCS, color.unpremul(), props}; if (as_CFB(this)->appendStages(rec, color.fA == 1)) { SkPMColor4f dst; diff --git a/gfx/skia/skia/src/effects/colorfilters/SkMatrixColorFilter.cpp b/gfx/skia/skia/src/effects/colorfilters/SkMatrixColorFilter.cpp @@ -39,7 +39,7 @@ SkMatrixColorFilter::SkMatrixColorFilter(const float array[20], Domain domain, C void SkMatrixColorFilter::flatten(SkWriteBuffer& buffer) const { SkASSERT(sizeof(fMatrix) / sizeof(float) == 20); - buffer.writeScalarArray(fMatrix); + buffer.writeScalarArray(fMatrix, 20); // RGBA flag buffer.writeBool(fDomain == Domain::kRGBA); @@ -48,7 +48,7 @@ void SkMatrixColorFilter::flatten(SkWriteBuffer& buffer) const { sk_sp<SkFlattenable> SkMatrixColorFilter::CreateProc(SkReadBuffer& buffer) { float matrix[20]; - if (!buffer.readScalarArray(matrix)) { + if (!buffer.readScalarArray(matrix, 20)) { return nullptr; } diff --git a/gfx/skia/skia/src/effects/colorfilters/SkWorkingFormatColorFilter.cpp b/gfx/skia/skia/src/effects/colorfilters/SkWorkingFormatColorFilter.cpp @@ -66,8 +66,7 @@ bool SkWorkingFormatColorFilter::appendStages(const SkStageRec& rec, bool shader rec.fDstColorType, workingCS.get(), rec.fPaintColor, - rec.fSurfaceProps, - rec.fDstBounds}; + rec.fSurfaceProps}; dstToWorking->apply(rec.fPipeline); if (!as_CFB(fChild)->appendStages(workingRec, shaderIsOpaque)) { @@ -122,10 +121,10 @@ sk_sp<SkFlattenable> SkWorkingFormatColorFilter::CreateProc(SkReadBuffer& buffer SkAlphaType at; if (!useDstTF) { - buffer.readScalarArray({&tf.g, sizeof(skcms_TransferFunction) / sizeof(SkScalar)}); + buffer.readScalarArray(&tf.g, sizeof(skcms_TransferFunction) / sizeof(SkScalar)); } if (!useDstGamut) { - buffer.readScalarArray({&gamut.vals[0][0], sizeof(skcms_Matrix3x3) / sizeof(SkScalar)}); + buffer.readScalarArray(&gamut.vals[0][0], sizeof(skcms_Matrix3x3) / sizeof(SkScalar)); } if (!useDstAT) { at = buffer.read32LE(kLastEnum_SkAlphaType); @@ -197,10 +196,10 @@ void SkWorkingFormatCalculator::flatten(SkWriteBuffer& buffer) const { buffer.writeBool(fUseDstGamut); buffer.writeBool(fUseDstAT); if (!fUseDstTF) { - buffer.writeScalarArray({&fTF.g, sizeof(skcms_TransferFunction) / sizeof(SkScalar)}); + buffer.writeScalarArray(&fTF.g, sizeof(skcms_TransferFunction) / sizeof(SkScalar)); } if (!fUseDstGamut) { - buffer.writeScalarArray({&fGamut.vals[0][0], sizeof(skcms_Matrix3x3) / sizeof(SkScalar)}); + buffer.writeScalarArray(&fGamut.vals[0][0], sizeof(skcms_Matrix3x3) / sizeof(SkScalar)); } if (!fUseDstAT) { buffer.writeInt(fAT); diff --git a/gfx/skia/skia/src/effects/imagefilters/SkBlurImageFilter.cpp b/gfx/skia/skia/src/effects/imagefilters/SkBlurImageFilter.cpp @@ -7,6 +7,7 @@ #include "include/effects/SkImageFilters.h" +#include "include/core/SkColorType.h" #include "include/core/SkFlattenable.h" #include "include/core/SkImageFilter.h" #include "include/core/SkRect.h" @@ -16,10 +17,12 @@ #include "include/core/SkTileMode.h" #include "include/core/SkTypes.h" #include "include/private/base/SkFloatingPoint.h" +#include "include/private/base/SkTo.h" #include "src/core/SkBlurEngine.h" #include "src/core/SkImageFilterTypes.h" #include "src/core/SkImageFilter_Base.h" #include "src/core/SkReadBuffer.h" +#include "src/core/SkSpecialImage.h" #include "src/core/SkWriteBuffer.h" #include <algorithm> @@ -59,11 +62,12 @@ private: const skif::Mapping& mapping, std::optional<skif::LayerSpace<SkIRect>> contentBounds) const override; - skif::LayerSpace<SkSize> mapSigma(const skif::Mapping& mapping) const; + skif::LayerSpace<SkSize> mapSigma(const skif::Mapping& mapping, bool useBlurEngine) const; skif::LayerSpace<SkIRect> kernelBounds(const skif::Mapping& mapping, - skif::LayerSpace<SkIRect> bounds) const { - skif::LayerSpace<SkSize> sigma = this->mapSigma(mapping); + skif::LayerSpace<SkIRect> bounds, + bool useBlurEngine) const { + skif::LayerSpace<SkSize> sigma = this->mapSigma(mapping, useBlurEngine); bounds.outset(skif::LayerSpace<SkSize>({3 * sigma.width(), 3 * sigma.height()}).ceil()); return bounds; } @@ -153,11 +157,13 @@ static constexpr SkScalar kMaxSigma = 532.f; } // namespace skif::FilterResult SkBlurImageFilter::onFilterImage(const skif::Context& ctx) const { + const bool useBlurEngine = SkToBool(ctx.backend()->getBlurEngine()); + skif::Context inputCtx = ctx.withNewDesiredOutput( - this->kernelBounds(ctx.mapping(), ctx.desiredOutput())); + this->kernelBounds(ctx.mapping(), ctx.desiredOutput(), useBlurEngine)); skif::FilterResult childOutput = this->getChildOutput(0, inputCtx); - skif::LayerSpace<SkSize> sigma = this->mapSigma(ctx.mapping()); + skif::LayerSpace<SkSize> sigma = this->mapSigma(ctx.mapping(), useBlurEngine); if (sigma.width() == 0.f && sigma.height() == 0.f) { // No actual blur, so just return the input unmodified return childOutput; @@ -169,10 +175,8 @@ skif::FilterResult SkBlurImageFilter::onFilterImage(const skif::Context& ctx) co // By default, FilterResult::blur() will calculate a more optimal output automatically, so // convey the original output to it. skif::LayerSpace<SkIRect> maxOutput = ctx.desiredOutput(); - if (fLegacyTileMode != SkTileMode::kDecal) { - // Legacy tiling output is also dependent on the original child output bounds ignoring - // the tile mode's effect. - maxOutput = this->kernelBounds(ctx.mapping(), childOutput.layerBounds()); + if (!useBlurEngine || fLegacyTileMode != SkTileMode::kDecal) { + maxOutput = this->kernelBounds(ctx.mapping(), childOutput.layerBounds(), useBlurEngine); if (!maxOutput.intersect(ctx.desiredOutput())) { return {}; } @@ -186,28 +190,62 @@ skif::FilterResult SkBlurImageFilter::onFilterImage(const skif::Context& ctx) co fLegacyTileMode); } - // For non-legacy tiling, 'maxOutput' is equal to the desired output. For decal's it matches - // what Builder::blur() calculates internally. For legacy tiling, however, it's dependent on - // the original child output's bounds ignoring the tile mode's effect. - skif::Context croppedOutput = ctx.withNewDesiredOutput(maxOutput); - skif::FilterResult::Builder builder{croppedOutput}; - builder.add(childOutput); - return builder.blur(sigma); + // TODO(b/40039877): Once the CPU blur functions can handle tile modes and color types beyond + // N32, there won't be any need to branch on how to apply the blur to the filter result. + if (useBlurEngine) { + // For non-legacy tiling, 'maxOutput' is equal to the desired output. For decal's it matches + // what Builder::blur() calculates internally. For legacy tiling, however, it's dependent on + // the original child output's bounds ignoring the tile mode's effect. + skif::Context croppedOutput = ctx.withNewDesiredOutput(maxOutput); + skif::FilterResult::Builder builder{croppedOutput}; + builder.add(childOutput); + return builder.blur(sigma); + } + + // The legacy CPU blur does not yet support tile modes so explicitly resolve it to a special + // image that has the tiling rendered into the pixels. + + auto [resolvedChildOutput, origin] = childOutput.imageAndOffset(inputCtx); + if (!resolvedChildOutput) { + return {}; + } + SkIRect srcRect = SkIRect::MakeSize(resolvedChildOutput->dimensions()); + SkIRect srcRelativeOutput = SkIRect(maxOutput).makeOffset(-origin.x(), -origin.y()); + + // These parameters will always select the successive box-blur algorithm for 8888 data, + // matching the legacy behavior when that code was defined inline here. + const SkBlurEngine::Algorithm* legacyBlur = SkBlurEngine::GetRasterBlurEngine()->findAlgorithm( + /*sigma=*/{kMaxSigma, kMaxSigma}, /*colorType=*/kN32_SkColorType); + sk_sp<SkSpecialImage> blurResult = legacyBlur->blur(SkSize(sigma), + std::move(resolvedChildOutput), + srcRect, + SkTileMode::kDecal, + srcRelativeOutput); + return skif::FilterResult{std::move(blurResult), maxOutput.topLeft()}; } -skif::LayerSpace<SkSize> SkBlurImageFilter::mapSigma(const skif::Mapping& mapping) const { +skif::LayerSpace<SkSize> SkBlurImageFilter::mapSigma(const skif::Mapping& mapping, + bool useBlurEngine) const { skif::LayerSpace<SkSize> sigma = mapping.paramToLayer(fSigma); // Clamp to the maximum sigma sigma = skif::LayerSpace<SkSize>({std::min(sigma.width(), kMaxSigma), std::min(sigma.height(), kMaxSigma)}); + // TODO(b/294575803) - The legacy CPU blur uses only the successive box-blur which is both + // inaccurate for sigma < 2 and unable to represent blurring when its window is <= 1 (sigma + // below ~0.8). Until everything is using the blur engine, support both notions of identity. + // Disable bluring on axes that are not finite, or that are small enough that the blur is // effectively an identity. - if (!SkIsFinite(sigma.width()) || SkBlurEngine::IsEffectivelyIdentity(sigma.width())) { + if (!SkIsFinite(sigma.width()) || + (!useBlurEngine && SkBlurEngine::BoxBlurWindow(sigma.width()) <= 1) || + (useBlurEngine && SkBlurEngine::IsEffectivelyIdentity(sigma.width()))) { sigma = skif::LayerSpace<SkSize>({0.f, sigma.height()}); } - if (!SkIsFinite(sigma.height()) || SkBlurEngine::IsEffectivelyIdentity(sigma.height())) { + if (!SkIsFinite(sigma.height()) || + (!useBlurEngine && SkBlurEngine::BoxBlurWindow(sigma.height()) <= 1) || + (useBlurEngine && SkBlurEngine::IsEffectivelyIdentity(sigma.height()))) { sigma = skif::LayerSpace<SkSize>({sigma.width(), 0.f}); } @@ -218,8 +256,10 @@ skif::LayerSpace<SkIRect> SkBlurImageFilter::onGetInputLayerBounds( const skif::Mapping& mapping, const skif::LayerSpace<SkIRect>& desiredOutput, std::optional<skif::LayerSpace<SkIRect>> contentBounds) const { + // Use useBlurEngine=true since that has a more sensitive kernel, ensuring any layer input + // bounds will be sufficient for both blur engine and legacy CPU evaluations. skif::LayerSpace<SkIRect> requiredInput = - this->kernelBounds(mapping, desiredOutput); + this->kernelBounds(mapping, desiredOutput, /*useBlurEngine=*/true); return this->getChildInputLayerBounds(0, mapping, requiredInput, contentBounds); } @@ -228,7 +268,9 @@ std::optional<skif::LayerSpace<SkIRect>> SkBlurImageFilter::onGetOutputLayerBoun std::optional<skif::LayerSpace<SkIRect>> contentBounds) const { auto childOutput = this->getChildOutputLayerBounds(0, mapping, contentBounds); if (childOutput) { - return this->kernelBounds(mapping, *childOutput); + // Use useBlurEngine=true since it will ensure output bounds are conservative; legacy + // CPU-based blurs may produce 1px inset from this for very small sigmas. + return this->kernelBounds(mapping, *childOutput, /*useBlurEngine=*/true); } else { return skif::LayerSpace<SkIRect>::Unbounded(); } diff --git a/gfx/skia/skia/src/effects/imagefilters/SkCropImageFilter.cpp b/gfx/skia/skia/src/effects/imagefilters/SkCropImageFilter.cpp @@ -51,7 +51,7 @@ private: bool onAffectsTransparentBlack() const override { return fTileMode != SkTileMode::kDecal; } // Disable recursing in affectsTransparentBlack() if we hit a Crop. - // TODO(skbug.com/40045513): Automatically infer this from the output bounds being finite. + // TODO(skbug.com/14611): Automatically infer this from the output bounds being finite. bool ignoreInputsAffectsTransparentBlack() const override { return true; } skif::FilterResult onFilterImage(const skif::Context& context) const override; diff --git a/gfx/skia/skia/src/effects/imagefilters/SkDisplacementMapImageFilter.cpp b/gfx/skia/skia/src/effects/imagefilters/SkDisplacementMapImageFilter.cpp @@ -38,7 +38,7 @@ class SkDisplacementMapImageFilter final : public SkImageFilter_Base { static constexpr int kDisplacement = 0; static constexpr int kColor = 1; - // TODO(skbug.com/40045448): Use nearest to match historical behavior, but eventually this should + // TODO(skbug.com/14376): Use nearest to match historical behavior, but eventually this should // become a factory option. static constexpr SkSamplingOptions kDisplacementSampling{SkFilterMode::kNearest}; diff --git a/gfx/skia/skia/src/effects/imagefilters/SkImageImageFilter.cpp b/gfx/skia/skia/src/effects/imagefilters/SkImageImageFilter.cpp @@ -88,7 +88,7 @@ sk_sp<SkImageFilter> SkImageFilters::Image(sk_sp<SkImage> image, return sk_sp<SkImageFilter>(new SkImageImageFilter( std::move(image), srcRect, dstRect, sampling)); } else { - SkMatrix srcToDst = SkMatrix::RectToRectOrIdentity(srcRect, dstRect); + SkMatrix srcToDst = SkMatrix::RectToRect(srcRect, dstRect); if (!imageBounds.intersect(srcRect)) { // No overlap, so draw empty return SkImageFilters::Empty(); diff --git a/gfx/skia/skia/src/effects/imagefilters/SkLightingImageFilter.cpp b/gfx/skia/skia/src/effects/imagefilters/SkLightingImageFilter.cpp @@ -28,7 +28,6 @@ #include "src/core/SkReadBuffer.h" #include "src/core/SkRectPriv.h" #include "src/core/SkWriteBuffer.h" -#include "src/effects/SkEmbossMaskFilter.h" #include <optional> #include <utility> @@ -136,8 +135,7 @@ struct Material { enum class Type { kDiffuse, kSpecular, - kEmbossSpecular, - kLast = kEmbossSpecular + kLast = kSpecular }; Type fType; @@ -156,10 +154,6 @@ struct Material { static Material Specular(float k, float shininess, float surfaceDepth) { return {Type::kSpecular, skif::ParameterSpace<ZValue>(surfaceDepth), k, shininess}; } - - static Material EmbossSpecular(float k, float shininess, float surfaceDepth) { - return {Type::kEmbossSpecular , skif::ParameterSpace<ZValue>(surfaceDepth), k, shininess}; - } }; class SkLightingImageFilter final : public SkImageFilter_Base { @@ -245,7 +239,7 @@ sk_sp<SkShader> make_lighting_shader(sk_sp<SkShader> normalMap, builder.uniform("materialAndLightType") = SkV4{surfaceDepth.val(), shininess, - static_cast<float>(matType), + matType == Material::Type::kDiffuse ? 0.f : 1.f, lightType == Light::Type::kPoint ? 0.f : (lightType == Light::Type::kDistant ? -1.f : 1.f)}; builder.uniform("lightPosAndSpotFalloff") = @@ -344,15 +338,6 @@ sk_sp<SkImageFilter> SkImageFilters::SpotLitDiffuse( std::move(input), cropRect); } -// Private factory method for usage in SkEmbossMaskFilter. -sk_sp<SkImageFilter> SkEmbossMaskFilter::LegacySpecular( - const SkPoint3& direction, SkColor lightColor, SkScalar surfaceScale, SkScalar ks, - SkScalar shininess, sk_sp<SkImageFilter> input) { - return make_lighting(::Light::Distant(lightColor, direction), - Material::EmbossSpecular(ks, shininess, surfaceScale), - std::move(input), {}); -} - sk_sp<SkImageFilter> SkImageFilters::DistantLitSpecular( const SkPoint3& direction, SkColor lightColor, SkScalar surfaceScale, SkScalar ks, SkScalar shininess, sk_sp<SkImageFilter> input, const CropRect& cropRect) { diff --git a/gfx/skia/skia/src/effects/imagefilters/SkMatrixConvolutionImageFilter.cpp b/gfx/skia/skia/src/effects/imagefilters/SkMatrixConvolutionImageFilter.cpp @@ -94,7 +94,7 @@ private: // is unique in that it might not produce unbounded output, but we can't calculate the // fast bounds because the kernel is applied in device space and no transform is provided // with that API. - // TODO(skbug.com/40045519): Accept a matrix in computeFastBounds() so that we can handle the + // TODO(skbug.com/14617): Accept a matrix in computeFastBounds() so that we can handle the // layer-space kernel case. // That issue aside, a matrix convolution can affect transparent black when it has a @@ -289,7 +289,7 @@ sk_sp<SkFlattenable> SkMatrixConvolutionImageFilter::CreateProc(SkReadBuffer& bu return nullptr; } AutoSTArray<16, SkScalar> kernel(count); - if (!buffer.readScalarArray(kernel)) { + if (!buffer.readScalarArray(kernel.get(), count)) { return nullptr; } SkScalar gain = buffer.readScalar(); @@ -320,7 +320,7 @@ void SkMatrixConvolutionImageFilter::flatten(SkWriteBuffer& buffer) const { this->SkImageFilter_Base::flatten(buffer); buffer.writeInt(fKernelSize.width()); buffer.writeInt(fKernelSize.height()); - buffer.writeScalarArray(fKernel); + buffer.writeScalarArray(fKernel.data(), fKernel.size()); buffer.writeScalar(fGain); buffer.writeScalar(fBias); buffer.writeInt(fKernelOffset.x()); diff --git a/gfx/skia/skia/src/effects/imagefilters/SkRuntimeImageFilter.cpp b/gfx/skia/skia/src/effects/imagefilters/SkRuntimeImageFilter.cpp @@ -67,7 +67,7 @@ private: bool onAffectsTransparentBlack() const override { return true; } // Currently there is no way for a client to specify the semantics of geometric uniforms that // should respond to the canvas matrix. Forcing translate-only is a hammer that lets the output - // be correct at the expense of resolution when there's a lot of scaling. See skbug.com/40044507. + // be correct at the expense of resolution when there's a lot of scaling. See skbug.com/13416. MatrixCapability onGetCTMCapability() const override { return MatrixCapability::kTranslate; } skif::FilterResult onFilterImage(const skif::Context&) const override; diff --git a/gfx/skia/skia/src/encode/SkICC.cpp b/gfx/skia/skia/src/encode/SkICC.cpp @@ -211,7 +211,7 @@ constexpr uint32_t kCICPTrfnHLG = 18; uint32_t get_cicp_trfn(const skcms_TransferFunction& fn) { switch (skcms_TransferFunction_getType(&fn)) { - default: + case skcms_TFType_Invalid: return 0; case skcms_TFType_sRGBish: if (nearly_equal(fn, SkNamedTransferFn::kSRGB)) { @@ -222,12 +222,10 @@ uint32_t get_cicp_trfn(const skcms_TransferFunction& fn) { return kCICPTrfnLinear; } break; - case skcms_TFType_PQ: case skcms_TFType_PQish: // All PQ transfer functions are mapped to the single PQ value, // ignoring their SDR white level. return kCICPTrfnPQ; - case skcms_TFType_HLG: case skcms_TFType_HLGish: // All HLG transfer functions are mapped to the single HLG value. return kCICPTrfnHLG; @@ -363,11 +361,11 @@ float tone_map_inverse(float y) { // Evaluate PQ and HLG transfer functions without tonemapping. The maximum returned value is // kToneMapInputMax. float hdr_trfn_eval(const skcms_TransferFunction& fn, float x) { - if (skcms_TransferFunction_isHLGish(&fn) || skcms_TransferFunction_isHLG(&fn)) { + if (skcms_TransferFunction_isHLGish(&fn)) { // For HLG this curve is the inverse OETF and then a per-channel OOTF. - x = skcms_TransferFunction_eval(&SkNamedTransferFn::kHLG, x); + x = skcms_TransferFunction_eval(&SkNamedTransferFn::kHLG, x) / 12.f; x *= std::pow(x, 0.2); - } else if (skcms_TransferFunction_isPQish(&fn) || skcms_TransferFunction_isPQ(&fn)) { + } else if (skcms_TransferFunction_isPQish(&fn)) { // For PQ this is the EOTF, scaled so that 1,000 nits maps to 1.0. x = 10.f * skcms_TransferFunction_eval(&SkNamedTransferFn::kPQ, x); x = std::min(x, 1.f); @@ -658,7 +656,7 @@ sk_sp<SkData> SkWriteICCProfile(const skcms_ICCProfile* profile, const char* des size_t last_tag_offset = sizeof(header) + tag_table_size; size_t last_tag_size = 0; for (const auto& tag : tags) { - if (!tag.second->empty()) { + if (!tag.second->isEmpty()) { last_tag_offset = last_tag_offset + last_tag_size; last_tag_size = tag.second->size(); } @@ -673,7 +671,7 @@ sk_sp<SkData> SkWriteICCProfile(const skcms_ICCProfile* profile, const char* des // Write the tags. for (const auto& tag : tags) { - if (tag.second->empty()) continue; + if (tag.second->isEmpty()) continue; memcpy(ptr, tag.second->data(), tag.second->size()); ptr += tag.second->size(); } @@ -707,8 +705,7 @@ sk_sp<SkData> SkWriteICCProfile(const skcms_TransferFunction& fn, const skcms_Ma } // Populate A2B (PQ and HLG only). - if (skcms_TransferFunction_isPQish(&fn) || skcms_TransferFunction_isHLGish(&fn) || - skcms_TransferFunction_isPQ(&fn) || skcms_TransferFunction_isHLG(&fn)) { + if (skcms_TransferFunction_isPQish(&fn) || skcms_TransferFunction_isHLGish(&fn)) { // Populate a 1D curve to perform per-channel conversion to linear and tone mapping. constexpr uint32_t kTrcTableSize = 65; trc_table.resize(kTrcTableSize); @@ -738,7 +735,7 @@ sk_sp<SkData> SkWriteICCProfile(const skcms_TransferFunction& fn, const skcms_Ma } // For HLG, mix the channels according to the OOTF. - if (skcms_TransferFunction_isHLGish(&fn) || skcms_TransferFunction_isHLG(&fn)) { + if (skcms_TransferFunction_isHLGish(&fn)) { // Scale to [0, 1]. for (auto& c : rgb) { c /= kToneMapInputMax; @@ -809,8 +806,7 @@ sk_sp<SkData> SkWriteICCProfile(const skcms_TransferFunction& fn, const skcms_Ma } // Populate CICP. - if (skcms_TransferFunction_isHLGish(&fn) || skcms_TransferFunction_isPQish(&fn) || - skcms_TransferFunction_isHLG(&fn) || skcms_TransferFunction_isPQ(&fn)) { + if (skcms_TransferFunction_isHLGish(&fn) || skcms_TransferFunction_isPQish(&fn)) { profile.has_CICP = true; profile.CICP.color_primaries = get_cicp_primaries(toXYZD50); profile.CICP.transfer_characteristics = get_cicp_trfn(fn); diff --git a/gfx/skia/skia/src/encode/SkImageEncoderFns.h b/gfx/skia/skia/src/encode/SkImageEncoderFns.h @@ -34,10 +34,194 @@ static inline void transform_scanline_A8_to_GrayAlpha(char* dst, const char* src } } -static inline sk_sp<SkData> icc_from_color_space(const SkColorSpace* cs) { + +static void skcms(char* dst, const char* src, int n, + skcms_PixelFormat srcFmt, skcms_AlphaFormat srcAlpha, + skcms_PixelFormat dstFmt, skcms_AlphaFormat dstAlpha) { + SkAssertResult(skcms_Transform(src, srcFmt, srcAlpha, nullptr, + dst, dstFmt, dstAlpha, nullptr, n)); +} + +static inline void transform_scanline_gray(char* dst, const char* src, int width, int) { + skcms(dst, src, width, + skcms_PixelFormat_G_8, skcms_AlphaFormat_Unpremul, + skcms_PixelFormat_RGB_888, skcms_AlphaFormat_Unpremul); +} + +static inline void transform_scanline_565(char* dst, const char* src, int width, int) { + skcms(dst, src, width, + skcms_PixelFormat_BGR_565, skcms_AlphaFormat_Unpremul, + skcms_PixelFormat_RGB_888, skcms_AlphaFormat_Unpremul); +} + +static inline void transform_scanline_RGBX(char* dst, const char* src, int width, int) { + skcms(dst, src, width, + skcms_PixelFormat_RGBA_8888, skcms_AlphaFormat_Unpremul, + skcms_PixelFormat_RGB_888 , skcms_AlphaFormat_Unpremul); +} + +static inline void transform_scanline_BGRX(char* dst, const char* src, int width, int) { + skcms(dst, src, width, + skcms_PixelFormat_BGRA_8888, skcms_AlphaFormat_Unpremul, + skcms_PixelFormat_RGB_888 , skcms_AlphaFormat_Unpremul); +} + +static inline void transform_scanline_444(char* dst, const char* src, int width, int) { + skcms(dst, src, width, + skcms_PixelFormat_ABGR_4444, skcms_AlphaFormat_Unpremul, + skcms_PixelFormat_RGB_888 , skcms_AlphaFormat_Unpremul); +} + +static inline void transform_scanline_rgbA(char* dst, const char* src, int width, int) { + skcms(dst, src, width, + skcms_PixelFormat_RGBA_8888, skcms_AlphaFormat_PremulAsEncoded, + skcms_PixelFormat_RGBA_8888, skcms_AlphaFormat_Unpremul); +} + +static inline void transform_scanline_bgrA(char* dst, const char* src, int width, int) { + skcms(dst, src, width, + skcms_PixelFormat_BGRA_8888, skcms_AlphaFormat_PremulAsEncoded, + skcms_PixelFormat_RGBA_8888, skcms_AlphaFormat_Unpremul); +} + +static inline void transform_scanline_to_premul_legacy(char* dst, const char* src, int width, int) { + skcms(dst, src, width, + skcms_PixelFormat_RGBA_8888, skcms_AlphaFormat_Unpremul, + skcms_PixelFormat_RGBA_8888, skcms_AlphaFormat_PremulAsEncoded); +} + +static inline void transform_scanline_BGRA(char* dst, const char* src, int width, int) { + skcms(dst, src, width, + skcms_PixelFormat_BGRA_8888, skcms_AlphaFormat_Unpremul, + skcms_PixelFormat_RGBA_8888, skcms_AlphaFormat_Unpremul); +} + +static inline void transform_scanline_4444(char* dst, const char* src, int width, int) { + skcms(dst, src, width, + skcms_PixelFormat_ABGR_4444, skcms_AlphaFormat_PremulAsEncoded, + skcms_PixelFormat_RGBA_8888, skcms_AlphaFormat_Unpremul); +} + +static inline void transform_scanline_101010x(char* dst, const char* src, int width, int) { + skcms(dst, src, width, + skcms_PixelFormat_RGBA_1010102, skcms_AlphaFormat_Unpremul, + skcms_PixelFormat_RGB_161616BE, skcms_AlphaFormat_Unpremul); +} + +static inline void transform_scanline_1010102(char* dst, const char* src, int width, int) { + skcms(dst, src, width, + skcms_PixelFormat_RGBA_1010102, skcms_AlphaFormat_Unpremul, + skcms_PixelFormat_RGBA_16161616BE, skcms_AlphaFormat_Unpremul); +} + +static inline void transform_scanline_1010102_premul(char* dst, const char* src, int width, int) { + skcms(dst, src, width, + skcms_PixelFormat_RGBA_1010102, skcms_AlphaFormat_PremulAsEncoded, + skcms_PixelFormat_RGBA_16161616BE, skcms_AlphaFormat_Unpremul); +} + +static inline void transform_scanline_bgr_101010x(char* dst, const char* src, int width, int) { + skcms(dst, src, width, + skcms_PixelFormat_BGRA_1010102, skcms_AlphaFormat_Unpremul, + skcms_PixelFormat_RGB_161616BE, skcms_AlphaFormat_Unpremul); +} + +static inline void transform_scanline_bgra_1010102(char* dst, const char* src, int width, int) { + skcms(dst, src, width, + skcms_PixelFormat_BGRA_1010102, skcms_AlphaFormat_Unpremul, + skcms_PixelFormat_RGBA_16161616BE, skcms_AlphaFormat_Unpremul); +} + +static inline void transform_scanline_bgr_101010x_xr(char* dst, const char* src, int width, int) { + skcms(dst, src, width, + skcms_PixelFormat_BGR_101010x_XR, skcms_AlphaFormat_Unpremul, + skcms_PixelFormat_RGB_161616BE, skcms_AlphaFormat_Unpremul); +} + +static inline void transform_scanline_bgra_10101010_xr(char* dst, const char* src, int width, int) { + skcms(dst, src, width, + skcms_PixelFormat_BGRA_10101010_XR, skcms_AlphaFormat_Unpremul, + skcms_PixelFormat_RGBA_16161616BE, skcms_AlphaFormat_Unpremul); +} + +static inline void transform_scanline_bgra_1010102_premul(char* dst, const char* src, int width, int) { + skcms(dst, src, width, + skcms_PixelFormat_BGRA_1010102, skcms_AlphaFormat_PremulAsEncoded, + skcms_PixelFormat_RGBA_16161616BE, skcms_AlphaFormat_Unpremul); +} + +static inline void transform_scanline_bgra_10101010_xr_premul(char* dst, const char* src, int width, int) { + skcms(dst, src, width, + skcms_PixelFormat_BGRA_10101010_XR, skcms_AlphaFormat_PremulAsEncoded, + skcms_PixelFormat_RGBA_16161616BE, skcms_AlphaFormat_Unpremul); +} + +static inline void transform_scanline_F16(char* dst, const char* src, int width, int) { + skcms(dst, src, width, + skcms_PixelFormat_RGBA_hhhh, skcms_AlphaFormat_Unpremul, + skcms_PixelFormat_RGBA_16161616BE, skcms_AlphaFormat_Unpremul); +} + +static inline void transform_scanline_F16F16F16x(char* dst, const char* src, int width, int) { + skcms(dst, src, width, + skcms_PixelFormat_RGBA_hhhh, skcms_AlphaFormat_Unpremul, + skcms_PixelFormat_RGB_161616BE, skcms_AlphaFormat_Unpremul); +} + +static inline void transform_scanline_F16_premul(char* dst, const char* src, int width, int) { + skcms(dst, src, width, + skcms_PixelFormat_RGBA_hhhh, skcms_AlphaFormat_PremulAsEncoded, + skcms_PixelFormat_RGBA_16161616BE, skcms_AlphaFormat_Unpremul); +} + +static inline void transform_scanline_F16_to_8888(char* dst, const char* src, int width, int) { + skcms(dst, src, width, + skcms_PixelFormat_RGBA_hhhh, skcms_AlphaFormat_Unpremul, + skcms_PixelFormat_RGBA_8888, skcms_AlphaFormat_Unpremul); +} + +static inline void transform_scanline_F16_premul_to_8888(char* dst, + const char* src, + int width, + int) { + skcms(dst, src, width, + skcms_PixelFormat_RGBA_hhhh, skcms_AlphaFormat_PremulAsEncoded, + skcms_PixelFormat_RGBA_8888, skcms_AlphaFormat_Unpremul); +} + +static inline void transform_scanline_F16_to_premul_8888(char* dst, + const char* src, + int width, + int) { + skcms(dst, src, width, + skcms_PixelFormat_RGBA_hhhh, skcms_AlphaFormat_Unpremul, + skcms_PixelFormat_RGBA_8888, skcms_AlphaFormat_PremulAsEncoded); +} + +static inline void transform_scanline_F32(char* dst, const char* src, int width, int) { + skcms(dst, src, width, + skcms_PixelFormat_RGBA_ffff, skcms_AlphaFormat_Unpremul, + skcms_PixelFormat_RGBA_16161616BE, skcms_AlphaFormat_Unpremul); +} + +static inline void transform_scanline_F32_premul(char* dst, const char* src, int width, int) { + skcms(dst, src, width, + skcms_PixelFormat_RGBA_ffff, skcms_AlphaFormat_PremulAsEncoded, + skcms_PixelFormat_RGBA_16161616BE, skcms_AlphaFormat_Unpremul); +} + +static inline sk_sp<SkData> icc_from_color_space(const SkColorSpace* cs, + const skcms_ICCProfile* profile, + const char* profile_description) { + // TODO(ccameron): Remove this check. if (!cs) { return nullptr; } + + if (profile) { + return SkWriteICCProfile(profile, profile_description); + } + skcms_Matrix3x3 toXYZD50; if (cs->toXYZD50(&toXYZD50)) { skcms_TransferFunction fn; @@ -47,8 +231,10 @@ static inline sk_sp<SkData> icc_from_color_space(const SkColorSpace* cs) { return nullptr; } -static inline sk_sp<SkData> icc_from_color_space(const SkImageInfo& info) { - return icc_from_color_space(info.colorSpace()); +static inline sk_sp<SkData> icc_from_color_space(const SkImageInfo& info, + const skcms_ICCProfile* profile, + const char* profile_description) { + return icc_from_color_space(info.colorSpace(), profile, profile_description); } static inline sk_sp<SkData> exif_from_origin(const SkEncodedOrigin origin) { diff --git a/gfx/skia/skia/src/encode/SkJpegEncoderImpl.cpp b/gfx/skia/skia/src/encode/SkJpegEncoderImpl.cpp @@ -414,11 +414,6 @@ bool Encode(SkWStream* dst, return encoder.get() && encoder->encodeRows(src.yuvaInfo().height()); } -sk_sp<SkData> Encode(const SkPixmap& src, const Options& options) { - SkDynamicMemoryWStream stream; - return Encode(&stream, src, options) ? stream.detachAsData() : nullptr; -} - sk_sp<SkData> Encode(GrDirectContext* ctx, const SkImage* img, const Options& options) { if (!img) { return nullptr; @@ -427,7 +422,11 @@ sk_sp<SkData> Encode(GrDirectContext* ctx, const SkImage* img, const Options& op if (!as_IB(img)->getROPixels(ctx, &bm)) { return nullptr; } - return Encode(bm.pixmap(), options); + SkDynamicMemoryWStream stream; + if (Encode(&stream, bm.pixmap(), options)) { + return stream.detachAsData(); + } + return nullptr; } std::unique_ptr<SkEncoder> Make(SkWStream* dst, const SkPixmap& src, const Options& options) { @@ -460,7 +459,8 @@ namespace SkJpegMetadataEncoder { void AppendICC(SegmentList& segmentList, const SkJpegEncoder::Options& options, const SkColorSpace* colorSpace) { - sk_sp<SkData> icc = icc_from_color_space(colorSpace); + sk_sp<SkData> icc = + icc_from_color_space(colorSpace, options.fICCProfile, options.fICCProfileDescription); if (!icc) { return; } diff --git a/gfx/skia/skia/src/encode/SkPngEncoderBase.cpp b/gfx/skia/skia/src/encode/SkPngEncoderBase.cpp @@ -10,7 +10,6 @@ #include <utility> #include "include/core/SkAlphaType.h" -#include "include/core/SkColor.h" #include "include/core/SkColorType.h" #include "include/core/SkImageInfo.h" #include "include/core/SkPixmap.h" @@ -20,9 +19,6 @@ #include "include/private/base/SkTemplates.h" #include "src/base/SkMSAN.h" #include "src/base/SkSafeMath.h" -#include "src/core/SkConvertPixels.h" -#include "src/core/SkImageInfoPriv.h" -#include "src/encode/SkImageEncoderFns.h" namespace { @@ -45,25 +41,24 @@ 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 makeRgba16Info(const SkImageInfo& srcInfo) { - return makeInfo(srcInfo, SkEncodedInfo::kRGBA_Color, 16); +SkEncodedInfo makeRgb16Info(const SkImageInfo& srcInfo) { + return makeInfo(srcInfo, SkEncodedInfo::kRGB_Color, 16); } -SkPngEncoderBase::TargetInfo makeTargetInfo(SkEncodedInfo dstInfo, const SkImageInfo& srcImageInfo, - SkColorType dstCT, SkAlphaType dstAT) { - SkASSERT(dstCT != kAlpha_8_SkColorType); - SkImageInfo dstRowInfo = SkImageInfo::Make(srcImageInfo.width(), 1, dstCT, dstAT); - return SkPngEncoderBase::TargetInfo {srcImageInfo.makeWH(srcImageInfo.width(), 1), - dstRowInfo, - std::move(dstInfo), - dstRowInfo.minRowBytes(),}; +SkEncodedInfo makeRgba16Info(const SkImageInfo& srcInfo) { + return makeInfo(srcInfo, SkEncodedInfo::kRGBA_Color, 16); } -std::optional<SkPngEncoderBase::TargetInfo> makeAlpha8TargetInfo(SkEncodedInfo dstInfo) { +std::optional<SkPngEncoderBase::TargetInfo> makeTargetInfo(SkEncodedInfo dstInfo, + transform_scanline_proc transformProc) { // `static_cast<size_t>`(dstInfo.bitsPerPixel())` uses trustworthy, bounded // data as input - no need to use `SkSafeMath` for this part. SkASSERT(dstInfo.bitsPerComponent() == 8 || dstInfo.bitsPerComponent() == 16); @@ -77,8 +72,8 @@ std::optional<SkPngEncoderBase::TargetInfo> makeAlpha8TargetInfo(SkEncodedInfo d if (!safe.ok()) { return std::nullopt; } - return SkPngEncoderBase::TargetInfo{std::nullopt, std::nullopt, - std::move(dstInfo), dstRowSize}; + + return SkPngEncoderBase::TargetInfo{std::move(dstInfo), transformProc, dstRowSize}; } } // namespace @@ -86,84 +81,156 @@ std::optional<SkPngEncoderBase::TargetInfo> makeAlpha8TargetInfo(SkEncodedInfo d // static std::optional<SkPngEncoderBase::TargetInfo> SkPngEncoderBase::getTargetInfo( const SkImageInfo& srcInfo) { + switch (srcInfo.colorType()) { + case kUnknown_SkColorType: + return std::nullopt; -SkColorType srcCT = srcInfo.colorType(); -SkAlphaType srcAT = srcInfo.alphaType(); -int numChannels = SkColorTypeNumChannels(srcCT); - -switch(numChannels) { - case 1: { - uint32_t srcChannelFlags = SkColorTypeChannelFlags(srcCT); - if (srcChannelFlags == kGray_SkColorChannelFlag) { - SkASSERT(srcInfo.isOpaque()); - return makeTargetInfo(makeGray8Info(srcInfo), srcInfo, srcCT, srcAT); - } - // We support encoding kAlpha_8_SkColorType to GrayAlpha images and just ignore gray. - // Otherwise, there is no sensible way to encode alpha only images. - if (srcCT == kAlpha_8_SkColorType) { - return makeAlpha8TargetInfo(makeGrayAlpha8Info(srcInfo)); - } - break; - } - case 3: { - SkASSERT(srcInfo.isOpaque()); - if (srcAT == kUnknown_SkAlphaType) { - SkDEBUGFAIL("unknown alpha type"); - return std::nullopt; - } - int maxBitsPerChannel = SkColorTypeMaxBitsPerChannel(srcCT); - if (maxBitsPerChannel <= 8) { - return makeTargetInfo(makeRgba8Info(srcInfo), - srcInfo, - kRGB_888x_SkColorType, - kOpaque_SkAlphaType); - } else if (maxBitsPerChannel <= 32) { - return makeTargetInfo(makeRgba16Info(srcInfo), - srcInfo, - kR16G16B16A16_unorm_SkColorType, - kOpaque_SkAlphaType); - } - break; - } - case 4: { - if (srcAT == kUnknown_SkAlphaType) { - SkDEBUGFAIL("unknown alpha type"); - return std::nullopt; - } - int maxBitsPerChannel = SkColorTypeMaxBitsPerChannel(srcCT); - if (maxBitsPerChannel <= 8) { - if (srcAT == kOpaque_SkAlphaType) { - return makeTargetInfo(makeRgba8Info(srcInfo), - srcInfo, - kRGB_888x_SkColorType, - kOpaque_SkAlphaType); - } - return makeTargetInfo(makeRgba8Info(srcInfo), - srcInfo, - kRGBA_8888_SkColorType, - kUnpremul_SkAlphaType); - } else if (maxBitsPerChannel <= 32) { - if (srcAT == kOpaque_SkAlphaType) { - return makeTargetInfo(makeRgba16Info(srcInfo), - srcInfo, - kR16G16B16A16_unorm_SkColorType, - kOpaque_SkAlphaType); - } - return makeTargetInfo(makeRgba16Info(srcInfo), - srcInfo, - kR16G16B16A16_unorm_SkColorType, - kUnpremul_SkAlphaType); - } - } - break; -} -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(src.colorType() == kAlpha_8_SkColorType - || (fTargetInfo.fSrcRowInfo && fTargetInfo.fDstRowInfo)); + SkASSERT(fTargetInfo.fTransformProc); + SkASSERT(fTargetInfo.fDstRowSize > 0); } bool SkPngEncoderBase::onEncodeRows(int numRows) { @@ -186,23 +253,10 @@ bool SkPngEncoderBase::onEncodeRows(int numRows) { sk_msan_assert_initialized(srcRow, (const uint8_t*)srcRow + (fSrc.width() << fSrc.shiftPerPixel())); - if (fSrc.colorType() == kAlpha_8_SkColorType) { - // This is a special case where we store kAlpha_8 images as GrayAlpha in png. - transform_scanline_A8_to_GrayAlpha((char*)fStorage.get(), - (const char*)srcRow, - fSrc.width(), - SkColorTypeBytesPerPixel(fSrc.colorType())); - } else { - SkASSERT(fSrc.width() == fTargetInfo.fSrcRowInfo->width()); - if (!SkConvertPixels(fTargetInfo.fDstRowInfo.value(), - (void*)fStorage.get(), - fTargetInfo.fDstRowSize, - fTargetInfo.fSrcRowInfo.value(), - srcRow, - fTargetInfo.fSrcRowInfo->minRowBytes())) { - return false; - } - } + fTargetInfo.fTransformProc((char*)fStorage.get(), + (const char*)srcRow, + fSrc.width(), + SkColorTypeBytesPerPixel(fSrc.colorType())); SkSpan<const uint8_t> rowToEncode(fStorage.get(), fTargetInfo.fDstRowSize); if (!this->onEncodeRow(rowToEncode)) { diff --git a/gfx/skia/skia/src/encode/SkPngEncoderBase.h b/gfx/skia/skia/src/encode/SkPngEncoderBase.h @@ -11,32 +11,33 @@ #include <cstdint> #include <optional> -#include "include/core/SkImageInfo.h" + #include "include/encode/SkEncoder.h" #include "include/private/SkEncodedInfo.h" +#include "src/encode/SkImageEncoderFns.h" +struct SkImageInfo; class SkPixmap; template <typename T> class SkSpan; // This class implements functionality shared between `SkPngEncoderImpl` and -// `SkPngRustEncoderImpl`. +// `SkPngRustEncoderImpl` (the latter is from `experimental/rust_png`). class SkPngEncoderBase : public SkEncoder { public: struct TargetInfo { - std::optional<SkImageInfo> fSrcRowInfo; - std::optional<SkImageInfo> fDstRowInfo; SkEncodedInfo fDstInfo; + transform_scanline_proc fTransformProc; size_t fDstRowSize; }; - // Gets the `fDstRowInfo` that `fSrcRowInfo` should be converted into before - // encoding and uses SkConvertPixels to transform source rows into + // 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`. - // - // 'fDstInfo' stores extra information for libpng. + // `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<TargetInfo> getTargetInfo(const SkImageInfo& srcInfo); @@ -55,8 +56,6 @@ protected: // `IEND` chunk). virtual bool onFinishEncoding() = 0; - const TargetInfo& targetInfo() const { return fTargetInfo; } - private: TargetInfo fTargetInfo; bool fFinishedEncoding = false; diff --git a/gfx/skia/skia/src/encode/SkPngEncoderImpl.cpp b/gfx/skia/skia/src/encode/SkPngEncoderImpl.cpp @@ -80,12 +80,13 @@ public: * Does not take ownership of stream */ static std::unique_ptr<SkPngEncoderMgr> Make(SkWStream* stream); - bool setHeader(const SkPngEncoderBase::TargetInfo& targetInfo, + + bool setHeader(const SkEncodedInfo& dstInfo, const SkImageInfo& srcInfo, const SkPngEncoder::Options& options); bool setColorSpace(const SkImageInfo& info, const SkPngEncoder::Options& options); - bool setHdrMetadata(const SkPngEncoder::Options& options); - bool writeInfo(const SkImageInfo& srcInfo,const SkPngEncoderBase::TargetInfo& targetInfo); + bool setV0Gainmap(const SkPngEncoder::Options& options); + bool writeInfo(const SkImageInfo& srcInfo); png_structp pngPtr() { return fPngPtr; } png_infop infoPtr() { return fInfoPtr; } @@ -118,25 +119,20 @@ std::unique_ptr<SkPngEncoderMgr> SkPngEncoderMgr::Make(SkWStream* stream) { return std::unique_ptr<SkPngEncoderMgr>(new SkPngEncoderMgr(pngPtr, infoPtr)); } -bool SkPngEncoderMgr::setHeader(const SkPngEncoderBase::TargetInfo& targetInfo, +bool SkPngEncoderMgr::setHeader(const SkEncodedInfo& dstInfo, const SkImageInfo& srcInfo, const SkPngEncoder::Options& options) { if (setjmp(png_jmpbuf(fPngPtr))) { return false; } - const SkEncodedInfo& dstInfo = targetInfo.fDstInfo; - const std::optional<SkImageInfo>& dstRowInfo = targetInfo.fDstRowInfo; - int pngColorType; switch (dstInfo.color()) { case SkEncodedInfo::kRGB_Color: pngColorType = PNG_COLOR_TYPE_RGB; break; case SkEncodedInfo::kRGBA_Color: - SkASSERT(dstRowInfo); - pngColorType = dstRowInfo->isOpaque() ? PNG_COLOR_TYPE_RGB - : PNG_COLOR_TYPE_RGB_ALPHA; + pngColorType = PNG_COLOR_TYPE_RGB_ALPHA; break; case SkEncodedInfo::kGray_Color: pngColorType = PNG_COLOR_TYPE_GRAY; @@ -150,7 +146,6 @@ bool SkPngEncoderMgr::setHeader(const SkPngEncoderBase::TargetInfo& targetInfo, } png_color_8 sigBit; - bool sigBitSet = true; switch (srcInfo.colorType()) { case kRGBA_F16Norm_SkColorType: case kRGBA_F16_SkColorType: @@ -168,6 +163,13 @@ bool SkPngEncoderMgr::setHeader(const SkPngEncoderBase::TargetInfo& targetInfo, case kGray_8_SkColorType: sigBit.gray = 8; break; + case kRGBA_8888_SkColorType: + case kBGRA_8888_SkColorType: + sigBit.red = 8; + sigBit.green = 8; + sigBit.blue = 8; + sigBit.alpha = 8; + break; case kRGB_888x_SkColorType: sigBit.red = 8; sigBit.green = 8; @@ -189,7 +191,6 @@ bool SkPngEncoderMgr::setHeader(const SkPngEncoderBase::TargetInfo& targetInfo, sigBit.alpha = 8; break; case kRGBA_1010102_SkColorType: - case kBGRA_1010102_SkColorType: sigBit.red = 10; sigBit.green = 10; sigBit.blue = 10; @@ -197,7 +198,6 @@ bool SkPngEncoderMgr::setHeader(const SkPngEncoderBase::TargetInfo& targetInfo, break; case kBGR_101010x_XR_SkColorType: case kRGB_101010x_SkColorType: - case kBGR_101010x_SkColorType: sigBit.red = 10; sigBit.green = 10; sigBit.blue = 10; @@ -208,16 +208,8 @@ bool SkPngEncoderMgr::setHeader(const SkPngEncoderBase::TargetInfo& targetInfo, sigBit.blue = 10; sigBit.alpha = 10; break; - case kRGBA_8888_SkColorType: - case kBGRA_8888_SkColorType: - sigBit.red = 8; - sigBit.green = 8; - sigBit.blue = 8; - sigBit.alpha = 8; - break; default: - SkDEBUGFAIL("Unable to set sigBit for src colortype, unhandled value\n"); - sigBitSet = false; + return false; } png_set_IHDR(fPngPtr, @@ -229,9 +221,7 @@ bool SkPngEncoderMgr::setHeader(const SkPngEncoderBase::TargetInfo& targetInfo, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); - if (sigBitSet) { - png_set_sBIT(fPngPtr, fInfoPtr, &sigBit); - } + png_set_sBIT(fPngPtr, fInfoPtr, &sigBit); int filters = (int)options.fFilterFlags & (int)SkPngEncoder::FilterFlag::kAll; SkASSERT(filters == (int)options.fFilterFlags); @@ -275,8 +265,12 @@ bool SkPngEncoderMgr::setHeader(const SkPngEncoderBase::TargetInfo& targetInfo, return true; } -static void set_icc(png_structp png_ptr, png_infop info_ptr, const SkImageInfo& info) { - sk_sp<SkData> icc = icc_from_color_space(info); +static void set_icc(png_structp png_ptr, + png_infop info_ptr, + const SkImageInfo& info, + const skcms_ICCProfile* profile, + const char* profile_description) { + sk_sp<SkData> icc = icc_from_color_space(info, profile, profile_description); if (!icc) { return; } @@ -300,57 +294,26 @@ bool SkPngEncoderMgr::setColorSpace(const SkImageInfo& info, const SkPngEncoder: if (info.colorSpace() && info.colorSpace()->isSRGB()) { png_set_sRGB(fPngPtr, fInfoPtr, PNG_sRGB_INTENT_PERCEPTUAL); } else { - set_icc(fPngPtr, fInfoPtr, info); + set_icc(fPngPtr, fInfoPtr, info, options.fICCProfile, options.fICCProfileDescription); } return true; } -bool SkPngEncoderMgr::setHdrMetadata(const SkPngEncoder::Options& options) { +bool SkPngEncoderMgr::setV0Gainmap(const SkPngEncoder::Options& options) { #ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED if (setjmp(png_jmpbuf(fPngPtr))) { return false; } - // List all chunks that might be included. - const char* hdrChunkNames = "gmAP\0" - "gdAT\0" - "mDCV\0" - "cLLI\0"; - constexpr int numHdrChunkNames = 4; - png_set_keep_unknown_chunks(fPngPtr, PNG_HANDLE_CHUNK_ALWAYS, - (png_const_bytep)hdrChunkNames, numHdrChunkNames); - - // In the below `png_unknown_chunk` structures, the `data` member is a non-const pointer, - // even though it will not be written to. In fact, `data` will be copied by the call to - // `png_set_unknown_chunks`, so it is safe for it to be deallocated immediately after - // the call. - skhdr::ContentLightLevelInformation clli; - if (options.fHdrMetadata.getContentLightLevelInformation(&clli)) { - auto data = clli.serializePngChunk(); - png_unknown_chunk chunk = { - {'c', 'L', 'L', 'I', 0}, - reinterpret_cast<png_byte*>(data->writable_data()), - data->size(), - PNG_HAVE_IHDR, - }; - png_set_unknown_chunks(fPngPtr, fInfoPtr, &chunk, 1); - } - - skhdr::MasteringDisplayColorVolume mdcv; - if (options.fHdrMetadata.getMasteringDisplayColorVolume(&mdcv)) { - auto data = mdcv.serialize(); - png_unknown_chunk chunk = { - {'m', 'D', 'C', 'V', 0}, - reinterpret_cast<png_byte*>(data->writable_data()), - data->size(), - PNG_HAVE_IHDR, - }; - png_set_unknown_chunks(fPngPtr, fInfoPtr, &chunk, 1); + // We require some gainmap information. + if (!options.fGainmapInfo) { + return false; } - if (options.fGainmapInfo && options.fGainmap) { + if (options.fGainmap) { sk_sp<SkData> 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. @@ -375,63 +338,57 @@ bool SkPngEncoderMgr::setHdrMetadata(const SkPngEncoder::Options& options) { modifiedOptions.fGainmapInfo = &gainmapInfo; } - sk_sp<SkData> gainmapData = SkPngEncoder::Encode(gainmapPixels, modifiedOptions); - if (!gainmapData) { + bool result = SkPngEncoder::Encode(&gainmapStream, gainmapPixels, modifiedOptions); + if (!result) { return false; } + sk_sp<SkData> 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. - png_unknown_chunk gmapChunk = { - {'g', 'm', 'A', 'P', 0}, - reinterpret_cast<png_byte*>(gainmapVersion->writable_data()), - gainmapVersion->size(), - PNG_HAVE_IHDR, - }; - png_set_unknown_chunks(fPngPtr, fInfoPtr, &gmapChunk, 1); - - png_unknown_chunk gdatChunk = { - {'g', 'd', 'A', 'T', 0}, - reinterpret_cast<png_byte*>(gainmapData->writable_data()), - gainmapData->size(), - PNG_HAVE_IHDR, - }; - png_set_unknown_chunks(fPngPtr, fInfoPtr, &gdatChunk, 1); - } else if (options.fGainmapInfo) { + std::array<png_unknown_chunk, 2> chunks; + auto& gmapChunk = chunks.at(0); + std::strcpy(reinterpret_cast<char*>(gmapChunk.name), "gmAP\0"); + gmapChunk.data = reinterpret_cast<png_byte*>(gainmapVersion->writable_data()); + gmapChunk.size = gainmapVersion->size(); + gmapChunk.location = PNG_HAVE_IHDR; + + auto& gdatChunk = chunks.at(1); + std::strcpy(reinterpret_cast<char*>(gdatChunk.name), "gdAT\0"); + gdatChunk.data = reinterpret_cast<png_byte*>(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<SkData> data = options.fGainmapInfo->serialize(); - png_unknown_chunk chunk = { - {'g', 'm', 'A', 'P', 0}, - reinterpret_cast<png_byte*>(data->writable_data()), - data->size(), - PNG_HAVE_IHDR, - }; + png_unknown_chunk chunk; + std::strcpy(reinterpret_cast<char*>(chunk.name), "gmAP\0"); + chunk.data = reinterpret_cast<png_byte*>(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, const SkPngEncoderBase::TargetInfo& targetInfo) { - if (setjmp(png_jmpbuf(fPngPtr))) { - return false; - } - png_write_info(fPngPtr, fInfoPtr); - - const SkEncodedInfo& dstInfo = targetInfo.fDstInfo; - const std::optional<SkImageInfo>& dstRowInfo = targetInfo.fDstRowInfo; - - // Strip input data that has 4 or 8 bytes per pixel down to 3 or 6 bytes if we don't want alpha. - if (dstInfo.color() == SkEncodedInfo::kRGBA_Color) { - SkASSERT(dstRowInfo); - if (dstRowInfo->isOpaque()) { - png_set_filler(fPngPtr, 0, PNG_FILLER_AFTER); - } - } - return true; +bool SkPngEncoderMgr::writeInfo(const SkImageInfo& srcInfo) { + if (setjmp(png_jmpbuf(fPngPtr))) { + return false; + } + + png_write_info(fPngPtr, fInfoPtr); + return true; } SkPngEncoderImpl::SkPngEncoderImpl(TargetInfo targetInfo, @@ -449,13 +406,6 @@ bool SkPngEncoderImpl::onEncodeRow(SkSpan<const uint8_t> row) { // `png_bytep` is `uint8_t*` rather than `const uint8_t*`. png_bytep rowPtr = const_cast<png_bytep>(row.data()); - // Swap to big endian if we are storing more than a byte per color channel - // (SkColorTypes are little endian by default). - // By this point our data will either be 8888 or 16161616, so we only check that case. - if (png_get_bit_depth(fEncoderMgr->pngPtr(), fEncoderMgr->infoPtr()) == 16) { - png_set_swap(fEncoderMgr->pngPtr()); - } - png_write_rows(fEncoderMgr->pngPtr(), &rowPtr, 1); return true; } @@ -486,21 +436,22 @@ std::unique_ptr<SkEncoder> Make(SkWStream* dst, const SkPixmap& src, const Optio return nullptr; } - if (!encoderMgr->setHeader(targetInfo.value(), src.info(), options)) { - return nullptr; + if (!encoderMgr->setHeader(targetInfo->fDstInfo, src.info(), options)) { + return nullptr; } if (!encoderMgr->setColorSpace(src.info(), options)) { return nullptr; } - if (!encoderMgr->setHdrMetadata(options)) { + if (options.fGainmapInfo && !encoderMgr->setV0Gainmap(options)) { return nullptr; } - if (!encoderMgr->writeInfo(src.info(), targetInfo.value())) { + if (!encoderMgr->writeInfo(src.info())) { return nullptr; } + return std::make_unique<SkPngEncoderImpl>(std::move(*targetInfo), std::move(encoderMgr), src); } @@ -509,11 +460,6 @@ bool Encode(SkWStream* dst, const SkPixmap& src, const Options& options) { return encoder.get() && encoder->encodeRows(src.height()); } -sk_sp<SkData> Encode(const SkPixmap& src, const Options& options) { - SkDynamicMemoryWStream stream; - return Encode(&stream, src, options) ? stream.detachAsData() : nullptr; -} - sk_sp<SkData> Encode(GrDirectContext* ctx, const SkImage* img, const Options& options) { if (!img) { return nullptr; @@ -522,7 +468,11 @@ sk_sp<SkData> Encode(GrDirectContext* ctx, const SkImage* img, const Options& op if (!as_IB(img)->getROPixels(ctx, &bm)) { return nullptr; } - return Encode(bm.pixmap(), options); + SkDynamicMemoryWStream stream; + if (Encode(&stream, bm.pixmap(), options)) { + return stream.detachAsData(); + } + return nullptr; } } // namespace SkPngEncoder diff --git a/gfx/skia/skia/src/encode/SkPngRustEncoder.cpp b/gfx/skia/skia/src/encode/SkPngRustEncoder.cpp @@ -1,28 +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/encode/SkPngRustEncoder.h" - -#include <memory> - -#include "include/encode/SkEncoder.h" -#include "src/encode/SkPngRustEncoderImpl.h" - -namespace SkPngRustEncoder { - -bool Encode(SkWStream* dst, const SkPixmap& src, const Options& options) { - std::unique_ptr<SkEncoder> encoder = Make(dst, src, options); - return encoder && encoder->encodeRows(src.height()); -} - -SK_API std::unique_ptr<SkEncoder> Make(SkWStream* dst, - const SkPixmap& src, - const Options& options) { - return SkPngRustEncoderImpl::Make(dst, src, options); -} - -} // namespace SkPngRustEncoder diff --git a/gfx/skia/skia/src/encode/SkPngRustEncoderImpl.cpp b/gfx/skia/skia/src/encode/SkPngRustEncoderImpl.cpp @@ -1,273 +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/encode/SkPngRustEncoderImpl.h" - -#include <limits> -#include <memory> -#include <optional> -#include <utility> - -#include "include/core/SkSpan.h" -#include "include/core/SkStream.h" -#include "include/encode/SkPngRustEncoder.h" -#include "include/private/SkEncodedInfo.h" -#include "include/private/base/SkAssert.h" -#include "rust/png/FFI.rs.h" -#include "rust/png/UtilsForFFI.h" -#include "src/base/SkSafeMath.h" -#include "src/encode/SkImageEncoderFns.h" -#include "src/encode/SkImageEncoderPriv.h" -#include "third_party/rust/cxx/v1/cxx.h" - -#ifdef __clang__ -#pragma clang diagnostic error "-Wconversion" -#endif - -namespace { - -rust_png::Compression ToCompression(SkPngRustEncoder::CompressionLevel level) { - switch (level) { - case SkPngRustEncoder::CompressionLevel::kLow: - return rust_png::Compression::Level1WithUpFilter; - case SkPngRustEncoder::CompressionLevel::kMedium: -#ifdef SK_RUST_PNG_MAP_MEDIUM_COMPRESSION_LEVEL_TO_FDEFLATE_FAST - // TODO(https://crbug.com/406072770): Consider using `Fast` instead - // of `Balanced` compression here. See the bug for details. - return rust_png::Compression::Fast; -#else - return rust_png::Compression::Balanced; -#endif - case SkPngRustEncoder::CompressionLevel::kHigh: - return rust_png::Compression::High; - } - SkUNREACHABLE; -} - -rust::Slice<const uint8_t> getDataTableEntry(const SkDataTable& table, int index) { - SkASSERT_RELEASE((0 <= index) && (index < table.count())); - - size_t size = 0; - const uint8_t* entry = table.atT<uint8_t>(index, &size); - while (size > 0 && entry[size - 1] == 0) { - // Ignore trailing NUL characters - these are *not* part of Rust `&str`. - size--; - } - - return rust::Slice<const uint8_t>(entry, size); -} - -rust_png::EncodingResult EncodeComments(rust_png::Writer& writer, - const sk_sp<SkDataTable>& comments) { - if (comments != nullptr) { - if (comments->count() % 2 != 0) { - return rust_png::EncodingResult::ParameterError; - } - - for (int i = 0; i < comments->count() / 2; ++i) { - rust::Slice<const uint8_t> keyword = getDataTableEntry(*comments, 2 * i); - rust::Slice<const uint8_t> text = getDataTableEntry(*comments, 2 * i + 1); - rust_png::EncodingResult result = writer.write_text_chunk(keyword, text); - if (result != rust_png::EncodingResult::Success) { - return result; - } - } - } - - return rust_png::EncodingResult::Success; -} - -// This helper class adapts `SkWStream` to expose the API required by Rust FFI -// (i.e. the `WriteTrait` API). -class WriteTraitAdapterForSkWStream final : public rust_png::WriteTrait { -public: - // SAFETY: The caller needs to guarantee that `stream` will be alive for - // as long as `WriteTraitAdapterForSkWStream`. - explicit WriteTraitAdapterForSkWStream(SkWStream* stream) : fStream(stream) { - SkASSERT_RELEASE(fStream); - } - - ~WriteTraitAdapterForSkWStream() override = default; - - // Non-copyable and non-movable. - WriteTraitAdapterForSkWStream(const WriteTraitAdapterForSkWStream&) = delete; - WriteTraitAdapterForSkWStream& operator=(const WriteTraitAdapterForSkWStream&) = delete; - WriteTraitAdapterForSkWStream(WriteTraitAdapterForSkWStream&&) = delete; - WriteTraitAdapterForSkWStream& operator=(WriteTraitAdapterForSkWStream&&) = delete; - - // Implementation of the `std::io::Read::read` method. See `RustTrait`'s - // doc comments and - // https://doc.rust-lang.org/nightly/std/io/trait.Read.html#tymethod.read - // for guidance on the desired implementation and behavior of this method. - bool write(rust::Slice<const uint8_t> buffer) override { - SkSpan<const uint8_t> span = ToSkSpan(buffer); - return fStream->write(span.data(), span.size()); - } - - void flush() override { fStream->flush(); } - -private: - SkWStream* fStream = nullptr; // Non-owning pointer. -}; - -} // namespace - -// static -std::unique_ptr<SkEncoder> SkPngRustEncoderImpl::Make(SkWStream* dst, - const SkPixmap& src, - const SkPngRustEncoder::Options& options) { - if (!SkPixmapIsValid(src)) { - return nullptr; - } - - std::optional<TargetInfo> maybeTargetInfo = SkPngEncoderBase::getTargetInfo(src.info()); - if (!maybeTargetInfo.has_value()) { - return nullptr; - } - const SkEncodedInfo& dstInfo = maybeTargetInfo->fDstInfo; - const std::optional<SkImageInfo>& maybeDstRowInfo = maybeTargetInfo->fDstRowInfo; - - SkSafeMath safe; - uint32_t width = safe.castTo<uint32_t>(dstInfo.width()); - uint32_t height = safe.castTo<uint32_t>(dstInfo.height()); - if (!safe.ok()) { - return nullptr; - } - - sk_sp<SkData> encodedProfile; - rust::Slice<const uint8_t> encodedProfileSlice; - if (const SkColorSpace* colorSpace = src.colorSpace(); colorSpace && !colorSpace->isSRGB()) { - encodedProfile = icc_from_color_space(colorSpace); - if (encodedProfile) { - encodedProfileSlice = - rust::Slice<const uint8_t>(encodedProfile->bytes(), encodedProfile->size()); - } - } - - rust_png::ColorType rustEncoderColorType; - ExtraRowTransform extraRowTransform = kNone_ExtraRowTransform; - switch (dstInfo.color()) { - case SkEncodedInfo::kRGB_Color: - rustEncoderColorType = rust_png::ColorType::Rgb; - break; - case SkEncodedInfo::kRGBA_Color: - rustEncoderColorType = rust_png::ColorType::Rgba; - if (maybeDstRowInfo) { - if (maybeDstRowInfo->isOpaque()) { - rustEncoderColorType = rust_png::ColorType::Rgb; - if (maybeDstRowInfo->colorType() == kR16G16B16A16_unorm_SkColorType) { - extraRowTransform = kRgba16leToRgb16be_ExtraRowTransform; - } else { - SkASSERT_RELEASE(maybeDstRowInfo->colorType() == kRGB_888x_SkColorType); - extraRowTransform = kRgba8ToRgb8_ExtraRowTransform; - } - } else if (maybeDstRowInfo->colorType() == kR16G16B16A16_unorm_SkColorType) { - extraRowTransform = kRgba16leToRgba16be_ExtraRowTransform; - } - } - break; - case SkEncodedInfo::kGray_Color: - rustEncoderColorType = rust_png::ColorType::Grayscale; - break; - case SkEncodedInfo::kGrayAlpha_Color: - rustEncoderColorType = rust_png::ColorType::GrayscaleAlpha; - break; - default: - SkUNREACHABLE; - } - - auto writeTraitAdapter = std::make_unique<WriteTraitAdapterForSkWStream>(dst); - rust::Box<rust_png::ResultOfWriter> resultOfWriter = - rust_png::new_writer(std::move(writeTraitAdapter), - width, - height, - rustEncoderColorType, - dstInfo.bitsPerComponent(), - ToCompression(options.fCompressionLevel), - encodedProfileSlice); - if (resultOfWriter->err() != rust_png::EncodingResult::Success) { - return nullptr; - } - rust::Box<rust_png::Writer> writer = resultOfWriter->unwrap(); - - if (EncodeComments(*writer, options.fComments) != rust_png::EncodingResult::Success) { - return nullptr; - } - - rust::Box<rust_png::ResultOfStreamWriter> resultOfStreamWriter = - rust_png::convert_writer_into_stream_writer(std::move(writer)); - if (resultOfStreamWriter->err() != rust_png::EncodingResult::Success) { - return nullptr; - } - rust::Box<rust_png::StreamWriter> stream_writer = resultOfStreamWriter->unwrap(); - - return std::make_unique<SkPngRustEncoderImpl>( - std::move(*maybeTargetInfo), src, std::move(stream_writer), extraRowTransform); -} - -SkPngRustEncoderImpl::SkPngRustEncoderImpl(TargetInfo targetInfo, - const SkPixmap& src, - rust::Box<rust_png::StreamWriter> stream_writer, - ExtraRowTransform extraRowTransform) - : SkPngEncoderBase(std::move(targetInfo), src) - , fStreamWriter(std::move(stream_writer)) - , fExtraRowTransform(extraRowTransform) {} - -SkPngRustEncoderImpl::~SkPngRustEncoderImpl() = default; - -bool SkPngRustEncoderImpl::onEncodeRow(SkSpan<const uint8_t> row) { - rust::Slice<const uint8_t> rustRow; - if (this->fExtraRowTransform != kNone_ExtraRowTransform) { - skcms_PixelFormat srcFmt, dstFmt; - size_t srcRowBytes = this->targetInfo().fDstRowInfo->minRowBytes(); - size_t dstRowBytes; - switch (this->fExtraRowTransform) { - case kRgba8ToRgb8_ExtraRowTransform: - srcFmt = skcms_PixelFormat_RGBA_8888; - dstFmt = skcms_PixelFormat_RGB_888; - dstRowBytes = srcRowBytes - (srcRowBytes / 4); - break; - case kRgba16leToRgba16be_ExtraRowTransform: - srcFmt = skcms_PixelFormat_RGBA_16161616LE; - dstFmt = skcms_PixelFormat_RGBA_16161616BE; - dstRowBytes = srcRowBytes; - break; - case kRgba16leToRgb16be_ExtraRowTransform: - srcFmt = skcms_PixelFormat_RGBA_16161616LE; - dstFmt = skcms_PixelFormat_RGB_161616BE; - dstRowBytes = srcRowBytes - (srcRowBytes / 4); - break; - default: - SkUNREACHABLE; - } - - fExtraRowBuffer.resize(dstRowBytes, 0x00); - SkSafeMath safe; - size_t width = safe.castTo<size_t>(this->targetInfo().fDstRowInfo->width()); - if (!safe.ok()) { - return false; - } - - bool success = skcms_Transform( - row.data(), srcFmt, skcms_AlphaFormat_Unpremul, nullptr, - fExtraRowBuffer.data(), dstFmt, skcms_AlphaFormat_Unpremul, nullptr, - width); - if (!success) { - return false; - } - - rustRow = rust::Slice<const uint8_t>(fExtraRowBuffer); - } else { - SkASSERT(this->fExtraRowTransform == kNone_ExtraRowTransform); - rustRow = rust::Slice<const uint8_t>(row); - } - return fStreamWriter->write(rustRow) == rust_png::EncodingResult::Success; -} - -bool SkPngRustEncoderImpl::onFinishEncoding() { - return rust_png::finish_encoding(std::move(fStreamWriter)) == rust_png::EncodingResult::Success; -} diff --git a/gfx/skia/skia/src/encode/SkPngRustEncoderImpl.h b/gfx/skia/skia/src/encode/SkPngRustEncoderImpl.h @@ -1,80 +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 SkPngRustEncoderImpl_DEFINED -#define SkPngRustEncoderImpl_DEFINED - -#include <memory> - -#include "rust/png/FFI.rs.h" -#include "src/encode/SkPngEncoderBase.h" -#include "third_party/rust/cxx/v1/cxx.h" - -class SkWStream; -struct SkEncodedInfo; -class SkPixmap; -class SkPngEncoderMgr; -template <typename T> class SkSpan; - -namespace SkPngRustEncoder { -struct Options; -} // namespace SkPngRustEncoder - -// This class provides the Skia image encoding API (`SkEncoder`) on top of the -// third-party `png` crate (PNG compression and encoding implemented in Rust). -// -// TODO(https://crbug.com/379312510): Derive from `SkPngEncoderBase` (see -// http://review.skia.org/923336 and http://review.skia.org/922676). -class SkPngRustEncoderImpl final : public SkPngEncoderBase { -public: - enum ExtraRowTransform { - // `kNone...` indicates that pixels from `SkPngEncoderBase` can be fed - // directly into Rust `png`. - kNone_ExtraRowTransform, - - // `kRgba8ToRgb8...` indicates that `SkPngEncoderBase` gives RGBx (8-bit) - // pixels to `onEncodeRow`. And since Rust `png` can't ignore the - // alpha channel (`libpng` can do this via `png_set_filler`), we need to - // do an extra RGBA => RGB transformation before feeding the data into - // Rust. - kRgba8ToRgb8_ExtraRowTransform, - - // `kRgba16leToRgba16be...` indicates that `SkPngEncoderBase` gives RGBA (16-bit) - // little endian pixels to `onEncodeRow`. And since Rust `png` can't swap the - // endianess (`libpng` can do this via `png_set_swap`), we need to - // do an extra LE => BE transformation before feeding the data into - // Rust. - kRgba16leToRgba16be_ExtraRowTransform, - - // `kRgba16leToRgb16be...` indicates that `SkPngEncoderBase` gives RGBA (16-bit) - // little endian pixels to `onEncodeRow`. And we must ignore alpha + swap endianess. - kRgba16leToRgb16be_ExtraRowTransform, - }; - - static std::unique_ptr<SkEncoder> Make(SkWStream*, - const SkPixmap&, - const SkPngRustEncoder::Options& options); - - // `public` to support `std::make_unique<SkPngRustEncoderImpl>(...)`. - SkPngRustEncoderImpl(TargetInfo targetInfo, - const SkPixmap& src, - rust::Box<rust_png::StreamWriter> streamWriter, - ExtraRowTransform extraRowTransform); - - ~SkPngRustEncoderImpl() override; - -protected: - bool onEncodeRow(SkSpan<const uint8_t> row) override; - bool onFinishEncoding() override; - -private: - rust::Box<rust_png::StreamWriter> fStreamWriter; - - ExtraRowTransform fExtraRowTransform; - std::vector<uint8_t> fExtraRowBuffer; -}; - -#endif // SkPngRustEncoderImpl_DEFINED diff --git a/gfx/skia/skia/src/encode/SkWebpEncoderImpl.cpp b/gfx/skia/skia/src/encode/SkWebpEncoderImpl.cpp @@ -150,7 +150,8 @@ bool Encode(SkWStream* stream, const SkPixmap& pixmap, const Options& opts) { // If there is no need to embed an ICC profile, we write directly to the input stream. // Otherwise, we will first encode to |tmp| and use a mux to add the ICC chunk. libwebp // forces us to have an encoded image before we can add a profile. - sk_sp<SkData> icc = icc_from_color_space(pixmap.info()); + sk_sp<SkData> icc = + icc_from_color_space(pixmap.info(), opts.fICCProfile, opts.fICCProfileDescription); SkDynamicMemoryWStream tmp; pic.custom_ptr = icc ? (void*)&tmp : (void*)stream; pic.writer = stream_writer; @@ -187,11 +188,6 @@ bool Encode(SkWStream* stream, const SkPixmap& pixmap, const Options& opts) { return true; } -sk_sp<SkData> Encode(const SkPixmap& pixmap, const Options& opts) { - SkDynamicMemoryWStream stream; - return Encode(&stream, pixmap, opts) ? stream.detachAsData() : nullptr; -} - bool EncodeAnimated(SkWStream* stream, SkSpan<const SkEncoder::Frame> frames, const Options& opts) { if (!stream || frames.empty()) { return false; @@ -264,7 +260,11 @@ sk_sp<SkData> Encode(GrDirectContext* ctx, const SkImage* img, const Options& op if (!as_IB(img)->getROPixels(ctx, &bm)) { return nullptr; } - return Encode(bm.pixmap(), options); + SkDynamicMemoryWStream stream; + if (Encode(&stream, bm.pixmap(), options)) { + return stream.detachAsData(); + } + return nullptr; } } // namespace SkWebpEncoder diff --git a/gfx/skia/skia/src/image/SkImage.cpp b/gfx/skia/skia/src/image/SkImage.cpp @@ -15,7 +15,6 @@ #include "include/core/SkPaint.h" #include "include/core/SkPixmap.h" #include "include/core/SkSurface.h" -#include "include/core/SkSurfaceProps.h" #include "include/core/SkTileMode.h" #include "include/core/SkTypes.h" #include "src/core/SkColorSpacePriv.h" @@ -49,21 +48,9 @@ bool SkImage::readPixels(GrDirectContext* dContext, const SkImageInfo& dstInfo, return as_IB(this)->onReadPixels(dContext, dstInfo, dstPixels, dstRowBytes, srcX, srcY, chint); } -sk_sp<SkImage> SkImage::makeScaled(const SkImageInfo& newInfo, - const SkSamplingOptions& sampling) const { - return makeScaled(nullptr, newInfo, sampling, SkSurfaceProps{}); -} - -sk_sp<SkImage> SkImage::makeScaled(SkRecorder* recorder, +sk_sp<SkImage> SkImage::makeScaled(skgpu::graphite::Recorder* recorder, const SkImageInfo& newInfo, const SkSamplingOptions& sampling) const { - return makeScaled(recorder, newInfo, sampling, SkSurfaceProps{}); -} - -sk_sp<SkImage> SkImage::makeScaled(SkRecorder* recorder, - const SkImageInfo& newInfo, - const SkSamplingOptions& sampling, - const SkSurfaceProps& props) const { if (!SkImageInfoIsValid(newInfo)) { return nullptr; } diff --git a/gfx/skia/skia/src/image/SkImage_Base.cpp b/gfx/skia/skia/src/image/SkImage_Base.cpp @@ -18,6 +18,7 @@ #include "include/core/SkTypes.h" #include "include/private/base/SkDebug.h" #include "src/core/SkBitmapCache.h" +#include "src/core/SkColorSpacePriv.h" #include "src/image/SkRescaleAndReadPixels.h" #include <atomic> @@ -76,7 +77,25 @@ bool SkImage_Base::onAsLegacyBitmap(GrDirectContext* dContext, SkBitmap* bitmap) return true; } -sk_sp<SkImage> SkImage_Base::makeSubset(SkRecorder* recorder, +sk_sp<SkImage> SkImage_Base::makeSubset(GrDirectContext* direct, const SkIRect& subset) const { + if (subset.isEmpty()) { + return nullptr; + } + + const SkIRect bounds = SkIRect::MakeWH(this->width(), this->height()); + if (!bounds.contains(subset)) { + return nullptr; + } + + // optimization : return self if the subset == our bounds + if (bounds == subset) { + return sk_ref_sp(const_cast<SkImage_Base*>(this)); + } + + return this->onMakeSubset(direct, subset); +} + +sk_sp<SkImage> SkImage_Base::makeSubset(skgpu::graphite::Recorder* recorder, const SkIRect& subset, RequiredProperties requiredProps) const { if (subset.isEmpty()) { @@ -105,8 +124,43 @@ void SkImage_Base::onAsyncRescaleAndReadPixelsYUV420(SkYUVColorSpace, callback(context, nullptr); } -sk_sp<SkImage> SkImage_Base::makeColorSpace(SkRecorder* recorder, +sk_sp<SkImage> SkImage_Base::makeColorSpace(GrDirectContext* direct, + sk_sp<SkColorSpace> target) const { + return this->makeColorTypeAndColorSpace(direct, this->colorType(), std::move(target)); +} + +sk_sp<SkImage> SkImage_Base::makeColorSpace(skgpu::graphite::Recorder* recorder, sk_sp<SkColorSpace> target, RequiredProperties props) const { return this->makeColorTypeAndColorSpace(recorder, this->colorType(), std::move(target), props); } + +sk_sp<SkImage> SkImage_Base::makeColorTypeAndColorSpace(GrDirectContext* dContext, + SkColorType targetColorType, + sk_sp<SkColorSpace> targetCS) const { + if (kUnknown_SkColorType == targetColorType || !targetCS) { + return nullptr; + } + + SkColorType colorType = this->colorType(); + SkColorSpace* colorSpace = this->colorSpace(); + if (!colorSpace) { + colorSpace = sk_srgb_singleton(); + } + if (colorType == targetColorType && + (SkColorSpace::Equals(colorSpace, targetCS.get()) || this->isAlphaOnly())) { + return sk_ref_sp(const_cast<SkImage_Base*>(this)); + } + + return this->onMakeColorTypeAndColorSpace(targetColorType, std::move(targetCS), dContext); +} + +sk_sp<SkImage> SkImage_Base::makeColorTypeAndColorSpace(skgpu::graphite::Recorder*, + SkColorType ct, + sk_sp<SkColorSpace> cs, + RequiredProperties) const { + // Default to the ganesh version which should be backend agnostic if this + // image is, for example, a raster backed image. The graphite subclass overrides + // this method and things work correctly. + return this->makeColorTypeAndColorSpace(nullptr, ct, std::move(cs)); +} diff --git a/gfx/skia/skia/src/image/SkImage_Base.h b/gfx/skia/skia/src/image/SkImage_Base.h @@ -23,8 +23,8 @@ class GrImageContext; class SkBitmap; class SkColorSpace; class SkPixmap; -class SkRecorder; class SkSurface; +enum SkColorType : int; enum SkYUVColorSpace : int; struct SkIRect; struct SkISize; @@ -34,16 +34,31 @@ enum { kNeedNewImageUniqueID = 0 }; +namespace skgpu::graphite { +class Recorder; +} + class SkImage_Base : public SkImage { public: ~SkImage_Base() override; // From SkImage.h - sk_sp<SkImage> makeColorSpace(SkRecorder*, + sk_sp<SkImage> makeColorSpace(GrDirectContext*, sk_sp<SkColorSpace>) const override; + sk_sp<SkImage> makeColorSpace(skgpu::graphite::Recorder*, sk_sp<SkColorSpace>, RequiredProperties) const override; + sk_sp<SkImage> makeColorTypeAndColorSpace(GrDirectContext* dContext, + SkColorType targetColorType, + sk_sp<SkColorSpace> targetCS) const override; + sk_sp<SkImage> makeColorTypeAndColorSpace(skgpu::graphite::Recorder*, + SkColorType, + sk_sp<SkColorSpace>, + RequiredProperties) const override; + sk_sp<SkImage> makeSubset(GrDirectContext* direct, const SkIRect& subset) const override; + sk_sp<SkImage> makeSubset(skgpu::graphite::Recorder*, + const SkIRect&, + RequiredProperties) const override; - sk_sp<SkImage> makeSubset(SkRecorder*, const SkIRect&, RequiredProperties) const override; size_t textureSize() const override { return 0; } // Methods that we want to use elsewhere in Skia, but not be a part of the public API. @@ -59,7 +74,14 @@ public: int srcY, CachingHint) const = 0; - virtual bool readPixelsGraphite(SkRecorder*, const SkPixmap& dst, int srcX, int srcY) const { + // used by makeScaled() + virtual sk_sp<SkSurface> onMakeSurface(skgpu::graphite::Recorder*, + const SkImageInfo&) const = 0; + + virtual bool readPixelsGraphite(skgpu::graphite::Recorder*, + const SkPixmap& dst, + int srcX, + int srcY) const { return false; } @@ -108,22 +130,17 @@ public: virtual bool getROPixels(GrDirectContext*, SkBitmap*, CachingHint = kAllow_CachingHint) const = 0; - virtual sk_sp<SkImage> onMakeSubset(SkRecorder*, const SkIRect&, RequiredProperties) const = 0; + virtual sk_sp<SkImage> onMakeSubset(GrDirectContext*, const SkIRect&) const = 0; virtual sk_sp<SkData> onRefEncoded() const { return nullptr; } virtual bool onAsLegacyBitmap(GrDirectContext*, SkBitmap*) const; - // Create the surface used by makeScaled. If this is a GPU backed image, the surface - // should be Ganesh or Graphite backed (as appropriate), otherwise this can raster backed. - virtual sk_sp<SkSurface> onMakeSurface(SkRecorder*, const SkImageInfo&) const = 0; - enum class Type { kRaster, kRasterPinnable, kLazy, kLazyPicture, - kLazyTexture, kGanesh, kGaneshYUVA, kGraphite, @@ -134,8 +151,7 @@ public: // True for picture-backed and codec-backed bool isLazyGenerated() const override { - return this->type() == Type::kLazy || this->type() == Type::kLazyPicture || - this->type() == Type::kLazyTexture; + return this->type() == Type::kLazy || this->type() == Type::kLazyPicture; } bool isRasterBacked() const { @@ -166,6 +182,9 @@ public: fAddedToRasterCache.store(true); } + virtual sk_sp<SkImage> onMakeColorTypeAndColorSpace(SkColorType, sk_sp<SkColorSpace>, + GrDirectContext*) const = 0; + virtual sk_sp<SkImage> onReinterpretColorSpace(sk_sp<SkColorSpace>) const = 0; // on failure, returns nullptr @@ -174,12 +193,15 @@ public: return nullptr; } + virtual sk_sp<SkImage> onMakeSubset(skgpu::graphite::Recorder*, + const SkIRect&, + RequiredProperties) const = 0; + protected: SkImage_Base(const SkImageInfo& info, uint32_t uniqueID); private: // Set true by caches when they cache content that's derived from the current pixels. - mutable std::atomic<bool> fAddedToRasterCache; }; diff --git a/gfx/skia/skia/src/image/SkImage_Lazy.cpp b/gfx/skia/skia/src/image/SkImage_Lazy.cpp @@ -8,14 +8,12 @@ #include "src/image/SkImage_Lazy.h" #include "include/core/SkBitmap.h" -#include "include/core/SkCPURecorder.h" #include "include/core/SkColorSpace.h" #include "include/core/SkData.h" #include "include/core/SkImageGenerator.h" #include "include/core/SkPixmap.h" -#include "include/core/SkRecorder.h" #include "include/core/SkSize.h" -#include "include/core/SkSurface.h" // IWYU pragma: keep +#include "include/core/SkSurface.h" #include "include/core/SkYUVAInfo.h" #include "src/core/SkBitmapCache.h" #include "src/core/SkCachedData.h" @@ -187,12 +185,20 @@ sk_sp<SkData> SkImage_Lazy::onRefEncoded() const { return nullptr; } -bool SkImage_Lazy::isValid(SkRecorder* recorder) const { +bool SkImage_Lazy::isValid(GrRecordingContext* context) const { ScopedGenerator generator(fSharedGenerator); - return generator->isValid(recorder); + return generator->isValid(context); } -sk_sp<SkImage> SkImage_Lazy::onMakeSubset(SkRecorder*, + +sk_sp<SkImage> SkImage_Lazy::onMakeSubset(GrDirectContext*, const SkIRect& subset) const { + // neither picture-backed nor codec-backed lazy images need the context to do readbacks. + // The subclass for cross-context images *does* use the direct context. + auto pixels = this->makeRasterImage(nullptr); + return pixels ? pixels->makeSubset(nullptr, subset) : nullptr; +} + +sk_sp<SkImage> SkImage_Lazy::onMakeSubset(skgpu::graphite::Recorder*, const SkIRect& subset, RequiredProperties props) const { // TODO: can we do this more efficiently, by telling the generator we want to @@ -204,28 +210,23 @@ sk_sp<SkImage> SkImage_Lazy::onMakeSubset(SkRecorder*, return nonLazyImg->makeSubset(nullptr, subset, props); } -sk_sp<SkSurface> SkImage_Lazy::onMakeSurface(SkRecorder* recorder, const SkImageInfo& info) const { - if (!recorder) { - // TODO(kjlubick) remove this after old SkImage::makeScaled(image info, sampling) API gone - recorder = skcpu::Recorder::TODO(); - } +sk_sp<SkSurface> SkImage_Lazy::onMakeSurface(skgpu::graphite::Recorder*, + const SkImageInfo& info) const { const SkSurfaceProps* props = nullptr; - constexpr size_t rowBytes = 0; - return recorder->cpuRecorder()->makeBitmapSurface(info, rowBytes, props); + const size_t rowBytes = 0; + return SkSurfaces::Raster(info, rowBytes, props); } -sk_sp<SkImage> SkImage_Lazy::makeColorTypeAndColorSpace(SkRecorder*, - SkColorType targetColorType, - sk_sp<SkColorSpace> targetColorSpace, - RequiredProperties) const { +sk_sp<SkImage> SkImage_Lazy::onMakeColorTypeAndColorSpace(SkColorType targetCT, + sk_sp<SkColorSpace> targetCS, + GrDirectContext*) const { SkAutoMutexExclusive autoAquire(fOnMakeColorTypeAndSpaceMutex); if (fOnMakeColorTypeAndSpaceResult && - targetColorType == fOnMakeColorTypeAndSpaceResult->colorType() && - SkColorSpace::Equals(targetColorSpace.get(), - fOnMakeColorTypeAndSpaceResult->colorSpace())) { + targetCT == fOnMakeColorTypeAndSpaceResult->colorType() && + SkColorSpace::Equals(targetCS.get(), fOnMakeColorTypeAndSpaceResult->colorSpace())) { return fOnMakeColorTypeAndSpaceResult; } - Validator validator(fSharedGenerator, &targetColorType, targetColorSpace); + Validator validator(fSharedGenerator, &targetCT, targetCS); sk_sp<SkImage> result = validator ? sk_sp<SkImage>(new SkImage_Lazy(&validator)) : nullptr; if (result) { fOnMakeColorTypeAndSpaceResult = result; @@ -245,7 +246,7 @@ sk_sp<SkImage> SkImage_Lazy::onReinterpretColorSpace(sk_sp<SkColorSpace> newCS) pixmap.setColorSpace(this->refColorSpace()); if (ScopedGenerator(fSharedGenerator)->getPixels(pixmap)) { bitmap.setImmutable(); - return SkImages::RasterFromBitmap(bitmap); + return bitmap.asImage(); } } return nullptr; diff --git a/gfx/skia/skia/src/image/SkImage_Lazy.h b/gfx/skia/skia/src/image/SkImage_Lazy.h @@ -24,16 +24,18 @@ #include <memory> class GrDirectContext; +class GrRecordingContext; class SharedGenerator; class SkBitmap; class SkCachedData; class SkData; class SkPixmap; -class SkRecorder; class SkSurface; enum SkColorType : int; struct SkIRect; +namespace skgpu { namespace graphite { class Recorder; } } + class SkImage_Lazy : public SkImage_Base { public: struct Validator { @@ -50,11 +52,7 @@ public: SkImage_Lazy(Validator* validator); // From SkImage.h - bool isValid(SkRecorder*) const override; - sk_sp<SkImage> makeColorTypeAndColorSpace(SkRecorder*, - SkColorType targetColorType, - sk_sp<SkColorSpace> targetColorSpace, - RequiredProperties) const override; + bool isValid(GrRecordingContext*) const override; // From SkImage_Base.h bool onHasMipmaps() const override { @@ -67,24 +65,27 @@ public: bool onReadPixels(GrDirectContext*, const SkImageInfo&, void*, size_t, int srcX, int srcY, CachingHint) const override; sk_sp<SkData> onRefEncoded() const override; + sk_sp<SkImage> onMakeSubset(GrDirectContext*, const SkIRect&) const override; + sk_sp<SkImage> onMakeSubset(skgpu::graphite::Recorder*, + const SkIRect&, + RequiredProperties) const override; - sk_sp<SkImage> onMakeSubset(SkRecorder*, const SkIRect&, RequiredProperties) const override; - - sk_sp<SkSurface> onMakeSurface(SkRecorder*, const SkImageInfo&) const override; + sk_sp<SkSurface> onMakeSurface(skgpu::graphite::Recorder*, const SkImageInfo&) const override; bool getROPixels(GrDirectContext*, SkBitmap*, CachingHint) const override; SkImage_Base::Type type() const override { return SkImage_Base::Type::kLazy; } - + sk_sp<SkImage> onMakeColorTypeAndColorSpace(SkColorType, sk_sp<SkColorSpace>, + GrDirectContext*) const override; sk_sp<SkImage> onReinterpretColorSpace(sk_sp<SkColorSpace>) const final; void addUniqueIDListener(sk_sp<SkIDChangeListener>) const; sk_sp<SkCachedData> getPlanes(const SkYUVAPixmapInfo::SupportedDataTypes& supportedDataTypes, SkYUVAPixmaps* pixmaps) const; + // Be careful with this. You need to acquire the mutex, as the generator might be shared // among several images. sk_sp<SharedGenerator> generator() const; - protected: virtual bool readPixelsProxy(GrDirectContext*, const SkPixmap&) const { return false; } diff --git a/gfx/skia/skia/src/image/SkImage_Picture.cpp b/gfx/skia/skia/src/image/SkImage_Picture.cpp @@ -59,12 +59,10 @@ void SkImage_Picture::replay(SkCanvas* canvas) const { canvas->clear(SkColors::kTransparent); canvas->drawPicture(pictureIG->fPicture, &pictureIG->fMatrix, - SkOptAddressOrNull(pictureIG->fPaint)); + pictureIG->fPaint.getMaybeNull()); } -sk_sp<SkImage> SkImage_Picture::onMakeSubset(SkRecorder*, - const SkIRect& subset, - RequiredProperties) const { +sk_sp<SkImage> SkImage_Picture::onMakeSubset(GrDirectContext*, const SkIRect& subset) const { auto sharedGenerator = this->generator(); auto pictureIG = static_cast<SkPictureImageGenerator*>(sharedGenerator->fGenerator.get()); @@ -75,10 +73,19 @@ sk_sp<SkImage> SkImage_Picture::onMakeSubset(SkRecorder*, : SkImages::BitDepth::kU8; return SkImage_Picture::Make(pictureIG->fPicture, subset.size(), - &matrix, SkOptAddressOrNull(pictureIG->fPaint), + &matrix, pictureIG->fPaint.getMaybeNull(), bitDepth, this->refColorSpace(), pictureIG->fProps); } +sk_sp<SkImage> 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 { @@ -86,7 +93,7 @@ bool SkImage_Picture::getImageKeyValues( SkAutoMutexExclusive mutex(sharedGenerator->fMutex); auto pictureIG = static_cast<SkPictureImageGenerator*>(sharedGenerator->fGenerator.get()); - if (pictureIG->fPaint.has_value()) { + if (pictureIG->fPaint.getMaybeNull()) { // A full paint complicates the potential key too much. return false; } diff --git a/gfx/skia/skia/src/image/SkImage_Picture.h b/gfx/skia/skia/src/image/SkImage_Picture.h @@ -14,18 +14,19 @@ #include <cstdint> +class GrDirectContext; class SkCanvas; class SkColorSpace; class SkImage; class SkMatrix; class SkPaint; class SkPicture; -class SkRecorder; class SkSurfaceProps; struct SkIRect; struct SkISize; namespace SkImages { enum class BitDepth; } +namespace skgpu::graphite { class Recorder; } class SkImage_Picture : public SkImage_Lazy { public: @@ -44,7 +45,10 @@ public: // Call drawPicture on the provided canvas taking care of any required mutex locking. void replay(SkCanvas*) const; - sk_sp<SkImage> onMakeSubset(SkRecorder*, const SkIRect&, RequiredProperties) const override; + sk_sp<SkImage> onMakeSubset(GrDirectContext*, const SkIRect&) const override; + sk_sp<SkImage> 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. diff --git a/gfx/skia/skia/src/image/SkImage_Raster.cpp b/gfx/skia/skia/src/image/SkImage_Raster.cpp @@ -7,7 +7,6 @@ #include "src/image/SkImage_Raster.h" #include "include/core/SkBitmap.h" -#include "include/core/SkCPURecorder.h" #include "include/core/SkColorSpace.h" #include "include/core/SkData.h" #include "include/core/SkImage.h" @@ -15,10 +14,10 @@ #include "include/core/SkPixelRef.h" #include "include/core/SkPixmap.h" #include "include/core/SkPoint.h" -#include "include/core/SkRecorder.h" #include "include/core/SkRect.h" #include "include/core/SkRefCnt.h" #include "include/core/SkSize.h" +#include "include/core/SkSurface.h" #include "include/core/SkTypes.h" #include "src/base/SkRectMemcpy.h" #include "src/core/SkImageInfoPriv.h" @@ -32,7 +31,7 @@ class GrDirectContext; class SkSurfaceProps; -// fixes skbug.com/40036261 +// fixes https://bug.skia.org/5096 static bool is_not_subset(const SkBitmap& bm) { SkASSERT(bm.pixelRef()); SkISize dim = SkISize::Make(bm.pixelRef()->width(), bm.pixelRef()->height()); @@ -83,15 +82,11 @@ bool SkImage_Raster::getROPixels(GrDirectContext*, SkBitmap* dst, CachingHint) c return true; } -sk_sp<SkSurface> SkImage_Raster::onMakeSurface(SkRecorder* recorder, +sk_sp<SkSurface> SkImage_Raster::onMakeSurface(skgpu::graphite::Recorder*, const SkImageInfo& info) const { - if (!recorder) { - // TODO(kjlubick) remove this after old SkImage::makeScaled(image info, sampling) API gone - recorder = skcpu::Recorder::TODO(); - } const SkSurfaceProps* props = nullptr; - constexpr size_t rowBytes = 0; - return recorder->cpuRecorder()->makeBitmapSurface(info, rowBytes, props); + const size_t rowBytes = 0; + return SkSurfaces::Raster(info, rowBytes, props); } static SkBitmap copy_bitmap_subset(const SkBitmap& orig, const SkIRect& subset) { @@ -115,6 +110,15 @@ static SkBitmap copy_bitmap_subset(const SkBitmap& orig, const SkIRect& subset) return bitmap; } +sk_sp<SkImage> SkImage_Raster::onMakeSubset(GrDirectContext*, const SkIRect& subset) const { + SkBitmap copy = copy_bitmap_subset(fBitmap, subset); + if (copy.isNull()) { + return nullptr; + } else { + return copy.asImage(); + } +} + static sk_sp<SkMipmap> copy_mipmaps(const SkBitmap& src, SkMipmap* srcMips) { if (!srcMips) { return nullptr; @@ -137,7 +141,7 @@ static sk_sp<SkMipmap> copy_mipmaps(const SkBitmap& src, SkMipmap* srcMips) { return dst; } -sk_sp<SkImage> SkImage_Raster::onMakeSubset(SkRecorder*, +sk_sp<SkImage> SkImage_Raster::onMakeSubset(skgpu::graphite::Recorder*, const SkIRect& subset, RequiredProperties requiredProperties) const { sk_sp<SkImage> img; @@ -162,7 +166,7 @@ sk_sp<SkImage> SkImage_Raster::onMakeSubset(SkRecorder*, } else { SkBitmap copy = copy_bitmap_subset(fBitmap, subset); if (!copy.isNull()) { - img = SkImages::RasterFromBitmap(copy); + img = copy.asImage(); } } @@ -212,22 +216,20 @@ bool SkImage_Raster::onAsLegacyBitmap(GrDirectContext*, SkBitmap* bitmap) const /////////////////////////////////////////////////////////////////////////////// -sk_sp<SkImage> SkImage_Raster::makeColorTypeAndColorSpace(SkRecorder*, - SkColorType targetColorType, - sk_sp<SkColorSpace> targetColorSpace, - RequiredProperties) const { +sk_sp<SkImage> SkImage_Raster::onMakeColorTypeAndColorSpace(SkColorType targetCT, + sk_sp<SkColorSpace> targetCS, + GrDirectContext*) const { SkPixmap src; SkAssertResult(fBitmap.peekPixels(&src)); SkBitmap dst; - if (!dst.tryAllocPixels( - fBitmap.info().makeColorType(targetColorType).makeColorSpace(targetColorSpace))) { + if (!dst.tryAllocPixels(fBitmap.info().makeColorType(targetCT).makeColorSpace(targetCS))) { return nullptr; } SkAssertResult(dst.writePixels(src)); dst.setImmutable(); - return SkImages::RasterFromBitmap(dst); + return dst.asImage(); } sk_sp<SkImage> SkImage_Raster::onReinterpretColorSpace(sk_sp<SkColorSpace> newCS) const { diff --git a/gfx/skia/skia/src/image/SkImage_Raster.h b/gfx/skia/skia/src/image/SkImage_Raster.h @@ -11,7 +11,6 @@ #include "include/core/SkBitmap.h" #include "include/core/SkImage.h" #include "include/core/SkPixelRef.h" -#include "include/core/SkRecorder.h" #include "include/core/SkRefCnt.h" #include "include/core/SkTypes.h" #include "include/private/base/SkTo.h" @@ -24,6 +23,7 @@ #include <utility> class GrDirectContext; +class GrRecordingContext; class SkColorSpace; class SkData; class SkPixmap; @@ -32,6 +32,8 @@ enum SkColorType : int; struct SkIRect; struct SkImageInfo; +namespace skgpu { namespace graphite { class Recorder; } } + class SkImage_Raster : public SkImage_Base { public: SkImage_Raster(const SkImageInfo&, sk_sp<SkData>, size_t rb, @@ -40,19 +42,7 @@ public: ~SkImage_Raster() override; // From SkImage.h - bool isValid(SkRecorder* recorder) const override { - if (!recorder) { - return false; - } - if (!recorder->cpuRecorder()) { - return false; - } - return true; - } - sk_sp<SkImage> makeColorTypeAndColorSpace(SkRecorder*, - SkColorType targetColorType, - sk_sp<SkColorSpace> targetColorSpace, - RequiredProperties) const override; + bool isValid(GrRecordingContext* context) const override { return true; } // From SkImage_Base.h bool onReadPixels(GrDirectContext*, const SkImageInfo&, void*, size_t, int srcX, int srcY, @@ -61,15 +51,20 @@ public: const SkBitmap* onPeekBitmap() const override { return &fBitmap; } bool getROPixels(GrDirectContext*, SkBitmap*, CachingHint) const override; + sk_sp<SkImage> onMakeSubset(GrDirectContext*, const SkIRect&) const override; + sk_sp<SkImage> onMakeSubset(skgpu::graphite::Recorder*, + const SkIRect&, + RequiredProperties) const override; - sk_sp<SkImage> onMakeSubset(SkRecorder*, const SkIRect&, RequiredProperties) const override; - - sk_sp<SkSurface> onMakeSurface(SkRecorder*, const SkImageInfo&) const final; + sk_sp<SkSurface> onMakeSurface(skgpu::graphite::Recorder*, const SkImageInfo&) const override; SkPixelRef* getPixelRef() const { return fBitmap.pixelRef(); } bool onAsLegacyBitmap(GrDirectContext*, SkBitmap*) const override; + sk_sp<SkImage> onMakeColorTypeAndColorSpace(SkColorType, sk_sp<SkColorSpace>, + GrDirectContext*) const override; + sk_sp<SkImage> onReinterpretColorSpace(sk_sp<SkColorSpace>) const override; void notifyAddedToRasterCache() const override { @@ -105,6 +100,7 @@ public: SkImage_Base::Type type() const override { return SkImage_Base::Type::kRaster; } SkBitmap bitmap() const { return fBitmap; } + private: SkBitmap fBitmap; }; diff --git a/gfx/skia/skia/src/image/SkPictureImageGenerator.cpp b/gfx/skia/skia/src/image/SkPictureImageGenerator.cpp @@ -74,7 +74,7 @@ SkPictureImageGenerator::SkPictureImageGenerator(const SkImageInfo& info, sk_sp< } if (paint) { - fPaint = *paint; + fPaint.set(*paint); } } @@ -85,6 +85,6 @@ bool SkPictureImageGenerator::onGetPixels(const SkImageInfo& info, void* pixels, return false; } canvas->clear(0); - canvas->drawPicture(fPicture, &fMatrix, SkOptAddressOrNull(fPaint)); + canvas->drawPicture(fPicture, &fMatrix, fPaint.getMaybeNull()); return true; } diff --git a/gfx/skia/skia/src/image/SkPictureImageGenerator.h b/gfx/skia/skia/src/image/SkPictureImageGenerator.h @@ -14,6 +14,7 @@ #include "include/core/SkPicture.h" #include "include/core/SkRefCnt.h" #include "include/core/SkSurfaceProps.h" +#include "src/base/SkTLazy.h" #include <cstddef> @@ -28,10 +29,10 @@ protected: bool onGetPixels(const SkImageInfo&, void* pixels, size_t rowBytes, const Options&) override; private: - sk_sp<SkPicture> fPicture; - SkMatrix fMatrix; - std::optional<SkPaint> fPaint; - const SkSurfaceProps fProps; + sk_sp<SkPicture> fPicture; + SkMatrix fMatrix; + SkTLazy<SkPaint> fPaint; + const SkSurfaceProps fProps; friend class SkImage_Picture; }; diff --git a/gfx/skia/skia/src/image/SkSurface.cpp b/gfx/skia/skia/src/image/SkSurface.cpp @@ -235,8 +235,6 @@ GrRecordingContext* SkSurface::recordingContext() const { skgpu::graphite::Recorder* SkSurface::recorder() const { return asConstSB(this)->onGetRecorder(); } -SkRecorder* SkSurface::baseRecorder() const { return asConstSB(this)->onGetBaseRecorder(); } - bool SkSurface::wait(int numSemaphores, const GrBackendSemaphore* waitSemaphores, bool deleteSemaphoresAfterWait) { return asSB(this)->onWait(numSemaphores, waitSemaphores, deleteSemaphoresAfterWait); diff --git a/gfx/skia/skia/src/image/SkSurface_Base.cpp b/gfx/skia/skia/src/image/SkSurface_Base.cpp @@ -22,6 +22,7 @@ #include <atomic> #include <cstdint> +#include <memory> class GrRecordingContext; class SkPaint; @@ -38,14 +39,12 @@ SkSurface_Base::~SkSurface_Base() { // in case the canvas outsurvives us, we null the callback if (fCachedCanvas) { fCachedCanvas->setSurfaceBase(nullptr); - fCachedCanvas->onSurfaceDelete(); } } GrRecordingContext* SkSurface_Base::onGetRecordingContext() const { return nullptr; } skgpu::graphite::Recorder* SkSurface_Base::onGetRecorder() const { return nullptr; } -SkRecorder* SkSurface_Base::onGetBaseRecorder() const { return nullptr; } void SkSurface_Base::onDraw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkSamplingOptions& sampling, const SkPaint* paint) { diff --git a/gfx/skia/skia/src/image/SkSurface_Base.h b/gfx/skia/skia/src/image/SkSurface_Base.h @@ -10,7 +10,6 @@ #include "include/core/SkCanvas.h" #include "include/core/SkImage.h" -#include "include/core/SkRecorder.h" #include "include/core/SkRefCnt.h" #include "include/core/SkSamplingOptions.h" #include "include/core/SkScalar.h" @@ -72,7 +71,6 @@ public: virtual GrRecordingContext* onGetRecordingContext() const; virtual skgpu::graphite::Recorder* onGetRecorder() const; - virtual SkRecorder* onGetBaseRecorder() const; /** * Allocate a canvas that will draw into this surface. We will cache this @@ -179,12 +177,7 @@ public: uint32_t newGenerationID(); private: - // fCachedCanvas is the raw pointer to the canvas that is returned to the client. - // It can point to either the base canvas or a capture canvas wrapper. - SkCanvas* fCachedCanvas = nullptr; - // SkSurface_Base must always own the base canvas. During capture, SkCaptureManager owns any - // wrapping capture canvas that fCachedCanvas may point to. - std::unique_ptr<SkCanvas> fOwnedBaseCanvas = nullptr; + std::unique_ptr<SkCanvas> fCachedCanvas = nullptr; sk_sp<SkImage> fCachedImage = nullptr; // Returns false if drawing should not take place (allocation failure). @@ -200,19 +193,12 @@ private: SkCanvas* SkSurface_Base::getCachedCanvas() { if (nullptr == fCachedCanvas) { - fOwnedBaseCanvas = std::unique_ptr<SkCanvas>(this->onNewCanvas()); - - if (this->baseRecorder()) { - fCachedCanvas = this->baseRecorder()->makeCaptureCanvas(fOwnedBaseCanvas.get()); - } - if (!fCachedCanvas) { - fCachedCanvas = fOwnedBaseCanvas.get(); - } + fCachedCanvas = std::unique_ptr<SkCanvas>(this->onNewCanvas()); if (fCachedCanvas) { fCachedCanvas->setSurfaceBase(this); } } - return fCachedCanvas; + return fCachedCanvas.get(); } sk_sp<SkImage> SkSurface_Base::refCachedImage() { diff --git a/gfx/skia/skia/src/image/SkSurface_Raster.cpp b/gfx/skia/skia/src/image/SkSurface_Raster.cpp @@ -4,10 +4,10 @@ * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ + #include "src/image/SkSurface_Raster.h" #include "include/core/SkBitmap.h" -#include "include/core/SkCPURecorder.h" #include "include/core/SkCanvas.h" #include "include/core/SkCapabilities.h" #include "include/core/SkImageInfo.h" @@ -20,8 +20,6 @@ #include "include/private/base/SkAssert.h" #include "include/private/base/SkMath.h" #include "src/core/SkBitmapDevice.h" -#include "src/core/SkCPURecorderImpl.h" -#include "src/core/SkDevice.h" #include "src/core/SkImageInfoPriv.h" #include "src/core/SkImagePriv.h" #include "src/core/SkSurfacePriv.h" @@ -57,50 +55,28 @@ bool SkSurfaceValidateRasterInfo(const SkImageInfo& info, size_t rowBytes) { return true; } -SkSurface_Raster::SkSurface_Raster(const SkImageInfo& info, - void* pixels, - size_t rb, - void (*releaseProc)(void* pixels, void* context), - void* context, - const SkSurfaceProps* props) - : SkSurface_Raster( - asRRI(skcpu::Recorder::TODO()), info, pixels, rb, releaseProc, context, props) {} - -SkSurface_Raster::SkSurface_Raster(const SkImageInfo& info, - sk_sp<SkPixelRef> pr, +SkSurface_Raster::SkSurface_Raster(const SkImageInfo& info, void* pixels, size_t rb, + void (*releaseProc)(void* pixels, void* context), void* context, const SkSurfaceProps* props) - : SkSurface_Raster(asRRI(skcpu::Recorder::TODO()), info, pr, props) {} - -SkSurface_Raster::SkSurface_Raster(skcpu::RecorderImpl* recorder, - const SkImageInfo& info, - void* pixels, - size_t rowBytes, - void (*releaseProc)(void* pixels, void* context), - void* context, - const SkSurfaceProps* props) - : SkSurface_Base(info, props), fRecorder(recorder) { - fBitmap.installPixels(info, pixels, rowBytes, releaseProc, context); + : INHERITED(info, props) +{ + fBitmap.installPixels(info, pixels, rb, releaseProc, context); fWeOwnThePixels = false; // We are "Direct" } -SkSurface_Raster::SkSurface_Raster(skcpu::RecorderImpl* recorder, - const SkImageInfo& info, - sk_sp<SkPixelRef> pr, +SkSurface_Raster::SkSurface_Raster(const SkImageInfo& info, sk_sp<SkPixelRef> pr, const SkSurfaceProps* props) - : SkSurface_Base(pr->width(), pr->height(), props), fRecorder(recorder) { + : INHERITED(pr->width(), pr->height(), props) +{ fBitmap.setInfo(info, pr->rowBytes()); fBitmap.setPixelRef(std::move(pr), 0, 0); fWeOwnThePixels = true; } -SkCanvas* SkSurface_Raster::onNewCanvas() { - SkASSERT(fRecorder); - return new SkCanvas(sk_make_sp<SkBitmapDevice>(fRecorder, fBitmap, this->props())); -} +SkCanvas* SkSurface_Raster::onNewCanvas() { return new SkCanvas(fBitmap, this->props()); } sk_sp<SkSurface> SkSurface_Raster::onNewSurface(const SkImageInfo& info) { - SkASSERT(fRecorder); - return fRecorder->makeBitmapSurface(info, 0, &this->props()); + return SkSurfaces::Raster(info, &this->props()); } void SkSurface_Raster::onDraw(SkCanvas* canvas, SkScalar x, SkScalar y, @@ -179,10 +155,6 @@ sk_sp<const SkCapabilities> SkSurface_Raster::onCapabilities() { return SkCapabilities::RasterBackend(); } -SkRecorder* SkSurface_Raster::onGetBaseRecorder() const { - return fRecorder; -} - /////////////////////////////////////////////////////////////////////////////// namespace SkSurfaces { sk_sp<SkSurface> WrapPixels(const SkImageInfo& info, @@ -227,30 +199,3 @@ sk_sp<SkSurface> Raster(const SkImageInfo& info, size_t rowBytes, const SkSurfac } } // namespace SkSurfaces - -namespace skcpu { - -sk_sp<SkSurface> Recorder::makeBitmapSurface(const SkImageInfo& imageInfo, - const SkSurfaceProps* surfaceProps) { - return this->makeBitmapSurface(imageInfo, 0, surfaceProps); -} - -sk_sp<SkSurface> Recorder::makeBitmapSurface(const SkImageInfo& imageInfo, - size_t rowBytes, - const SkSurfaceProps* surfaceProps) { - if (!SkSurfaceValidateRasterInfo(imageInfo)) { - return nullptr; - } - - sk_sp<SkPixelRef> pr = SkMallocPixelRef::MakeAllocate(imageInfo, rowBytes); - if (!pr) { - return nullptr; - } - if (rowBytes) { - SkASSERT(pr->rowBytes() == rowBytes); - } - - return sk_make_sp<SkSurface_Raster>(asRRI(this), imageInfo, std::move(pr), surfaceProps); -} - -} // namespace skcpu diff --git a/gfx/skia/skia/src/image/SkSurface_Raster.h b/gfx/skia/skia/src/image/SkSurface_Raster.h @@ -13,7 +13,6 @@ #include "include/core/SkRefCnt.h" #include "include/core/SkSamplingOptions.h" #include "include/core/SkScalar.h" -#include "include/core/SkSurface.h" #include "src/image/SkSurface_Base.h" #include <cstring> @@ -24,12 +23,10 @@ class SkImage; class SkPaint; class SkPixelRef; class SkPixmap; -class SkRecorder; +class SkSurface; class SkSurfaceProps; struct SkIRect; -namespace skcpu { class RecorderImpl; } - class SkSurface_Raster : public SkSurface_Base { public: SkSurface_Raster(const SkImageInfo&, void*, size_t rb, @@ -37,18 +34,6 @@ public: const SkSurfaceProps*); SkSurface_Raster(const SkImageInfo& info, sk_sp<SkPixelRef>, const SkSurfaceProps*); - SkSurface_Raster(skcpu::RecorderImpl* recorder, - const SkImageInfo&, - void* pixels, - size_t rowBytes, - SkSurfaces::PixelsReleaseProc releaseProc, - void* context, - const SkSurfaceProps*); - SkSurface_Raster(skcpu::RecorderImpl* recorder, - const SkImageInfo&, - sk_sp<SkPixelRef>, - const SkSurfaceProps*); - // From SkSurface.h SkImageInfo imageInfo() const override { return fBitmap.info(); } @@ -63,12 +48,12 @@ public: bool onCopyOnWrite(ContentChangeMode) override; void onRestoreBackingMutability() override; sk_sp<const SkCapabilities> onCapabilities() override; - SkRecorder* onGetBaseRecorder() const override; private: - skcpu::RecorderImpl* fRecorder; - SkBitmap fBitmap; - bool fWeOwnThePixels; + SkBitmap fBitmap; + bool fWeOwnThePixels; + + using INHERITED = SkSurface_Base; }; #endif diff --git a/gfx/skia/skia/src/opts/SkBlitMask_opts.h b/gfx/skia/skia/src/opts/SkBlitMask_opts.h @@ -180,12 +180,11 @@ namespace SK_OPTS_NS { return __lsx_vsrlri_h(__lsx_vand_v(tmp, mask), 8); } - template <bool isTranslucent> - static void blit_mask_d32_a8_lsx(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); + template <bool isColor> + static void D32_A8_Opaque_Color_lsx(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); SkPMColor* SK_RESTRICT device = (SkPMColor*)dst; const uint8_t* SK_RESTRICT mask = (const uint8_t*)maskPtr; __m128i vpmc_b = __lsx_vldi(0); @@ -201,7 +200,7 @@ namespace SK_OPTS_NS { vpmc_b = __lsx_vreplgr2vr_h(SkGetPackedB32(pmc)); vpmc_g = __lsx_vreplgr2vr_h(SkGetPackedG32(pmc)); vpmc_r = __lsx_vreplgr2vr_h(SkGetPackedR32(pmc)); - vpmc_a = __lsx_vreplgr2vr_h(colorAlpha); + vpmc_a = __lsx_vreplgr2vr_h(SkGetPackedA32(pmc)); } const __m128i zeros = __lsx_vldi(0); @@ -228,7 +227,7 @@ namespace SK_OPTS_NS { vmask = __lsx_vilvl_b(zeros, vmask); __m128i vscale, vmask256 = __lsx_vadd_h(vmask, __lsx_vreplgr2vr_h(1)); - if constexpr (isTranslucent) { + if (isColor) { __m128i tmp = SkAlphaMul_lsx(vpmc_a, vmask256); vscale = __lsx_vsub_h(__lsx_vreplgr2vr_h(256), tmp); } else { @@ -253,19 +252,14 @@ namespace SK_OPTS_NS { w -= 8; } - 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); + while (w--) { + unsigned aa = *mask++; + if (isColor) { + *device = SkBlendARGB32(pmc, *device, aa); } else { - vscale = 256 - vmask; + *device = SkAlphaMulQ(pmc, SkAlpha255To256(aa)) + + SkAlphaMulQ(*device, SkAlpha255To256(255 - aa)); } - *device = SkAlphaMulQ(pmc, vmask256) - + SkAlphaMulQ(*device, vscale); device += 1; } @@ -278,13 +272,13 @@ 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) { - blit_mask_d32_a8_lsx<true>(dst, dstRB, mask, maskRB, color, w, h); + D32_A8_Opaque_Color_lsx<true>(dst, dstRB, mask, maskRB, color, w, h); } static void blit_mask_d32_a8_opaque(SkPMColor* dst, size_t dstRB, const SkAlpha* mask, size_t maskRB, SkColor color, int w, int h) { - blit_mask_d32_a8_lsx<false>(dst, dstRB, mask, maskRB, color, w, h); + D32_A8_Opaque_Color_lsx<false>(dst, dstRB, mask, maskRB, color, w, h); } // Same as _opaque, but assumes color == SK_ColorBLACK, a very common and even simpler case. diff --git a/gfx/skia/skia/src/opts/SkRasterPipeline_opts.h b/gfx/skia/skia/src/opts/SkRasterPipeline_opts.h @@ -167,8 +167,6 @@ namespace SK_OPTS_NS { template <typename T> SI T gather(const T* p, U32 ix) { return p[ix]; } - template <typename T> - SI T gather_unaligned(const T* p, U32 ix) { return gather<T>(p, ix); } SI void scatter_masked(I32 src, int* dst, U32 ix, I32 mask) { dst[ix] = mask ? src : dst[ix]; @@ -282,23 +280,9 @@ namespace SK_OPTS_NS { #endif template <typename T> - SI V<T> gather(const T* ptr, U32 ix) { - // The compiler assumes ptr is aligned, which caused crashes on some - // arm32 chips because a register was marked as "aligned to 32 bits" - // incorrectly. skbug.com/409859319 - SkASSERTF(reinterpret_cast<uintptr_t>(ptr) % alignof(T) == 0, - "Should use gather_unaligned"); - return V<T>{ptr[ix[0]], ptr[ix[1]], ptr[ix[2]], ptr[ix[3]]}; - } - template <typename T> - SI V<T> gather_unaligned(const T* ptr, U32 ix) { - // This tells the compiler ptr might not be aligned appropriately, so - // it generates better assembly. - typedef T __attribute__ ((aligned (1))) unaligned_ptr; - const unaligned_ptr* uptr = static_cast<const unaligned_ptr*>(ptr); - return V<T>{uptr[ix[0]], uptr[ix[1]], uptr[ix[2]], uptr[ix[3]]}; + SI V<T> gather(const T* p, U32 ix) { + return V<T>{p[ix[0]], p[ix[1]], p[ix[2]], p[ix[3]]}; } - SI void scatter_masked(I32 src, int* dst, U32 ix, I32 mask) { I32 before = gather(dst, ix); I32 after = if_then_else(mask, src, before); @@ -411,11 +395,6 @@ namespace SK_OPTS_NS { }; return sk_bit_cast<U64>(parts); } - template <typename T> - SI V<T> gather_unaligned(const T* p, U32 ix) { - return gather(p, ix); - } - template <typename V, typename S> SI void scatter_masked(V src, S* dst, U32 ix, I32 mask) { V before = gather(dst, ix); @@ -654,11 +633,6 @@ namespace SK_OPTS_NS { }; return sk_bit_cast<U64>(parts); } - template <typename T> - SI V<T> gather_unaligned(const T* p, U32 ix) { - return gather(p, ix); - } - SI void scatter_masked(I32 src, int* dst, U32 ix, I32 mask) { I32 before = gather(dst, ix); I32 after = if_then_else(mask, src, before); @@ -859,10 +833,6 @@ namespace SK_OPTS_NS { SI V<T> gather(const T* p, U32 ix) { return V<T>{p[ix[0]], p[ix[1]], p[ix[2]], p[ix[3]]}; } - template <typename T> - SI V<T> gather_unaligned(const T* p, U32 ix) { - return gather(p, ix); - } SI void scatter_masked(I32 src, int* dst, U32 ix, I32 mask) { I32 before = gather(dst, ix); I32 after = if_then_else(mask, src, before); @@ -1024,10 +994,6 @@ namespace SK_OPTS_NS { return V<T>{ p[ix[0]], p[ix[1]], p[ix[2]], p[ix[3]], p[ix[4]], p[ix[5]], p[ix[6]], p[ix[7]], }; } - template <typename T> - SI V<T> gather_unaligned(const T* p, U32 ix) { - return gather(p, ix); - } template <typename V, typename S> SI void scatter_masked(V src, S* dst, U32 ix, I32 mask) { @@ -1235,10 +1201,6 @@ namespace SK_OPTS_NS { ret = (F)__lsx_vinsgr2vr_w(ret, p[ix3], 3); return ret; } - template <typename T> - SI V<T> gather_unaligned(const T* p, U32 ix) { - return gather(p, ix); - } template <typename V, typename S> SI void scatter_masked(V src, S* dst, U32 ix, I32 mask) { @@ -2852,17 +2814,6 @@ HIGHP_STAGE(HLGinvish, const skcms_TransferFunction* ctx) { b = fn(b); } -HIGHP_STAGE(ootf, const float* ctx) { - F Y = ctx[0] * r + ctx[1] * g + ctx[2] * b; - - U32 sign; - Y = strip_sign(Y, &sign); - F Y_to_gamma_minus_one = apply_sign(approx_powf(Y, ctx[3]), sign); - r = r * Y_to_gamma_minus_one; - g = g * Y_to_gamma_minus_one; - b = b * Y_to_gamma_minus_one; -} - HIGHP_STAGE(load_a8, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy<const uint8_t>(ctx, dx,dy); @@ -2909,7 +2860,7 @@ HIGHP_STAGE(load_565_dst, const SkRasterPipelineContexts::MemoryCtx* 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_unaligned(ptr, ix), &r,&g,&b); + from_565(gather(ptr, ix), &r,&g,&b); a = F1; } HIGHP_STAGE(store_565, const SkRasterPipelineContexts::MemoryCtx* ctx) { @@ -2932,7 +2883,7 @@ HIGHP_STAGE(load_4444_dst, const SkRasterPipelineContexts::MemoryCtx* 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_unaligned(ptr, ix), &r,&g,&b,&a); + from_4444(gather(ptr, ix), &r,&g,&b,&a); } HIGHP_STAGE(store_4444, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy<uint16_t>(ctx, dx,dy); @@ -2954,7 +2905,7 @@ HIGHP_STAGE(load_8888_dst, const SkRasterPipelineContexts::MemoryCtx* 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_unaligned(ptr, ix), &r,&g,&b,&a); + from_8888(gather(ptr, ix), &r,&g,&b,&a); } HIGHP_STAGE(store_8888, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy<uint32_t>(ctx, dx,dy); @@ -2981,7 +2932,7 @@ HIGHP_STAGE(load_rg88_dst, const SkRasterPipelineContexts::MemoryCtx* 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_unaligned(ptr, ix), &r, &g); + from_88(gather(ptr, ix), &r, &g); b = F0; a = F1; } @@ -3005,7 +2956,7 @@ 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_unaligned(ptr, ix)); + a = from_short(gather(ptr, ix)); } HIGHP_STAGE(store_a16, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy<uint16_t>(ctx, dx,dy); @@ -3029,7 +2980,7 @@ HIGHP_STAGE(load_rg1616_dst, const SkRasterPipelineContexts::MemoryCtx* 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_unaligned(ptr, ix), &r, &g); + from_1616(gather(ptr, ix), &r, &g); b = F0; a = F1; } @@ -3052,7 +3003,7 @@ HIGHP_STAGE(load_16161616_dst, const SkRasterPipelineContexts::MemoryCtx* 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_unaligned(ptr, ix), &r, &g, &b, &a); + from_16161616(gather(ptr, ix), &r, &g, &b, &a); } HIGHP_STAGE(store_16161616, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy<uint16_t>(ctx, 4*dx,4*dy); @@ -3076,7 +3027,7 @@ HIGHP_STAGE(load_10x6_dst, const SkRasterPipelineContexts::MemoryCtx* 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_unaligned(ptr, ix), &r, &g, &b, &a); + from_10x6(gather(ptr, ix), &r, &g, &b, &a); } HIGHP_STAGE(store_10x6, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy<uint16_t>(ctx, 4*dx,4*dy); @@ -3108,17 +3059,17 @@ HIGHP_STAGE(load_1010102_xr_dst, const SkRasterPipelineContexts::MemoryCtx* 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_unaligned(ptr, ix), &r,&g,&b,&a); + from_1010102(gather(ptr, ix), &r,&g,&b,&a); } 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_unaligned(ptr, ix), &r,&g,&b,&a); + from_1010102_xr(gather(ptr, ix), &r,&g,&b,&a); } 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_unaligned(ptr, ix), &r, &g, &b, &a); + from_10101010_xr(gather(ptr, ix), &r, &g, &b, &a); } HIGHP_STAGE(load_10101010_xr, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy<const uint64_t>(ctx, dx, dy); @@ -3154,7 +3105,7 @@ HIGHP_STAGE(store_1010102_xr, const SkRasterPipelineContexts::MemoryCtx* ctx) { // 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) << 20 + | to_unorm(b, /*scale=*/510, /*bias=*/384, /*maxI=*/1023) << 10 | to_unorm(a, /*scale=*/3) << 30; store(ptr, px); } @@ -3182,7 +3133,7 @@ HIGHP_STAGE(load_f16_dst, const SkRasterPipelineContexts::MemoryCtx* 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_unaligned(ptr, ix); + auto px = gather(ptr, ix); U16 R,G,B,A; load4((const uint16_t*)&px, &R,&G,&B,&A); @@ -3219,7 +3170,7 @@ 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_unaligned(ptr, ix)); + a = from_half(gather(ptr, ix)); } HIGHP_STAGE(store_af16, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy<uint16_t>(ctx, dx,dy); @@ -3249,7 +3200,7 @@ HIGHP_STAGE(load_rgf16_dst, const SkRasterPipelineContexts::MemoryCtx* 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_unaligned(ptr, ix); + auto px = gather(ptr, ix); U16 R,G; load2((const uint16_t*)&px, &R, &G); @@ -3275,10 +3226,10 @@ HIGHP_STAGE(load_f32_dst, const SkRasterPipelineContexts::MemoryCtx* ctx) { HIGHP_STAGE(gather_f32, const SkRasterPipelineContexts::GatherCtx* ctx) { const float* ptr; U32 ix = ix_and_ptr(&ptr, ctx, r,g); - r = gather_unaligned(ptr, 4*ix + 0); - g = gather_unaligned(ptr, 4*ix + 1); - b = gather_unaligned(ptr, 4*ix + 2); - a = gather_unaligned(ptr, 4*ix + 3); + r = gather(ptr, 4*ix + 0); + g = gather(ptr, 4*ix + 1); + b = gather(ptr, 4*ix + 2); + a = gather(ptr, 4*ix + 3); } HIGHP_STAGE(store_f32, const SkRasterPipelineContexts::MemoryCtx* ctx) { auto ptr = ptr_at_xy<float>(ctx, 4*dx,4*dy); @@ -4662,29 +4613,16 @@ SI void mul_fn(T* dst, T* src) { *dst *= *src; } -SI void div_fn(I32* dst, I32* src) { - I32 divisor = *src; - // Integer division crashes when we divide by 0, but we can divide by -1 to not crash (the - // result will be non-sensical). The mask will be 0xFFFFFFF if true, which happens to be -1. - divisor |= ((I32)cond_to_mask(divisor == 0)); - // Dividing by -1 works for all numerators *except* INT_MIN, so we can add -1 once more if - // we are in that case. - divisor += ((I32)cond_to_mask(divisor == -1 && *dst == std::numeric_limits<int32_t>::lowest())); - *dst /= divisor; -} - -SI void div_fn(U32* dst, U32* src) { - U32 divisor = *src; - // Integer division crashes when we divide by 0, but we can divide by something else to not - // crash (the result will be non-sensical). The mask will be 0xFFFFFFF if true. - divisor |= ((U32)cond_to_mask(divisor == 0)); +template <typename T> +SI void div_fn(T* dst, T* src) { + T divisor = *src; + if constexpr (!std::is_same_v<T, F>) { + // We will crash if we integer-divide against zero. Convert 0 to ~0 to avoid this. + divisor |= (T)cond_to_mask(divisor == 0); + } *dst /= divisor; } -SI void div_fn(F* dst, F* src) { - *dst /= *src; -} - SI void bitwise_and_fn(I32* dst, I32* src) { *dst &= *src; } @@ -5052,11 +4990,11 @@ HIGHP_STAGE(gauss_a_to_rgba, NoCtx) { b = a; } -SI void bilerp_clamp_large(const SkRasterPipelineContexts::GatherCtx* ctx, - F* r, F* g, F* b, F* a) { +// A specialized fused image shader for clamp-x, clamp-y, non-sRGB sampling. +HIGHP_STAGE(bilerp_clamp_8888, const SkRasterPipelineContexts::GatherCtx* ctx) { // (cx,cy) are the center of our sample. - F cx = *r, - cy = *g; + F cx = r, + cy = g; // All sample points are at the same fractional offset (fx,fy). // They're the 4 corners of a logical 1x1 pixel surrounding (x,y) at (0.5,0.5) offsets. @@ -5064,7 +5002,7 @@ SI void bilerp_clamp_large(const SkRasterPipelineContexts::GatherCtx* ctx, fy = fract(cy + 0.5f); // We'll accumulate the color of all four samples into {r,g,b,a} directly. - *r = *g = *b = *a = F0; + r = g = b = a = F0; for (float py = -0.5f; py <= +0.5f; py += 1.0f) for (float px = -0.5f; px <= +0.5f; px += 1.0f) { @@ -5077,7 +5015,7 @@ SI void bilerp_clamp_large(const SkRasterPipelineContexts::GatherCtx* ctx, U32 ix = ix_and_ptr(&ptr, ctx, x,y); F sr,sg,sb,sa; - from_8888(gather_unaligned(ptr, ix), &sr,&sg,&sb,&sa); + from_8888(gather(ptr, ix), &sr,&sg,&sb,&sa); // In bilinear interpolation, the 4 pixels at +/- 0.5 offsets from the sample pixel center // are combined in direct proportion to their area overlapping that logical query pixel. @@ -5087,25 +5025,14 @@ SI void bilerp_clamp_large(const SkRasterPipelineContexts::GatherCtx* ctx, sy = (py > 0) ? fy : 1.0f - fy, area = sx * sy; - *r += sr * area; - *g += sg * area; - *b += sb * area; - *a += sa * area; + r += sr * area; + g += sg * area; + b += sb * area; + a += sa * area; } } // A specialized fused image shader for clamp-x, clamp-y, non-sRGB sampling. -HIGHP_STAGE(bilerp_clamp_8888, const SkRasterPipelineContexts::GatherCtx* ctx) { - bilerp_clamp_large(ctx, &r, &g, &b, &a); -} - -// A specialized fused image shader for clamp-x, clamp-y, non-sRGB sampling. -// This version exists to allow a shader to force high precision. -HIGHP_STAGE(bilerp_clamp_8888_force_highp, const SkRasterPipelineContexts::GatherCtx* ctx) { - bilerp_clamp_large(ctx, &r, &g, &b, &a); -} - -// A specialized fused image shader for clamp-x, clamp-y, non-sRGB sampling. HIGHP_STAGE(bicubic_clamp_8888, const SkRasterPipelineContexts::GatherCtx* ctx) { // (cx,cy) are the center of our sample. F cx = r, @@ -5140,7 +5067,7 @@ HIGHP_STAGE(bicubic_clamp_8888, const SkRasterPipelineContexts::GatherCtx* ctx) U32 ix = ix_and_ptr(&ptr, ctx, sample_x, sample_y); F sr,sg,sb,sa; - from_8888(gather_unaligned(ptr, ix), &sr,&sg,&sb,&sa); + from_8888(gather(ptr, ix), &sr,&sg,&sb,&sa); r = mad(scale, sr, r); g = mad(scale, sg, g); @@ -5956,10 +5883,6 @@ SI void store(T* ptr, V v) { return (U32)_mm512_i32gather_epi32((__m512i)ix, ptr, 4); } - template <typename V, typename T> - SI V gather_unaligned(const T* ptr, U32 ix) { - return gather<V, T>(ptr, ix); - } #elif defined(SKRP_CPU_HSW) template <typename V, typename T> SI V gather(const T* ptr, U32 ix) { @@ -5986,11 +5909,6 @@ SI void store(T* ptr, V v) { return join<U32>(_mm256_i32gather_epi32((const int*)ptr, lo, 4), _mm256_i32gather_epi32((const int*)ptr, hi, 4)); } - - template <typename V, typename T> - SI V gather_unaligned(const T* ptr, U32 ix) { - return gather<V, T>(ptr, ix); - } #elif defined(SKRP_CPU_LASX) template <typename V, typename T> SI V gather(const T* ptr, U32 ix) { @@ -5999,45 +5917,15 @@ SI void store(T* ptr, V v) { ptr[ix[ 8]], ptr[ix[ 9]], ptr[ix[10]], ptr[ix[11]], ptr[ix[12]], ptr[ix[13]], ptr[ix[14]], ptr[ix[15]], }; } - - template <typename V, typename T> - SI V gather_unaligned(const T* ptr, U32 ix) { - return gather<V, T>(ptr, ix); - } -#elif defined(SKRP_CPU_NEON) - template <typename V, typename T> - SI V gather(const T* ptr, U32 ix) { - // The compiler assumes ptr is aligned, which caused crashes on some - // arm32 chips because a register was marked as "aligned to 32 bits" - // incorrectly. skbug.com/409859319 - SkASSERTF(reinterpret_cast<uintptr_t>(ptr) % alignof(T) == 0, - "Should use gather_unaligned"); - return V{ ptr[ix[ 0]], ptr[ix[ 1]], ptr[ix[ 2]], ptr[ix[ 3]], - ptr[ix[ 4]], ptr[ix[ 5]], ptr[ix[ 6]], ptr[ix[ 7]], }; - } - - template <typename V, typename T> - SI V gather_unaligned(const T* ptr, U32 ix) { - // This tells the compiler ptr might not be aligned appropriately, so - // it generates better assembly. - typedef T __attribute__ ((aligned (1))) unaligned_ptr; - const unaligned_ptr* uptr = static_cast<const unaligned_ptr*>(ptr); - return V{ uptr[ix[ 0]], uptr[ix[ 1]], uptr[ix[ 2]], uptr[ix[ 3]], - uptr[ix[ 4]], uptr[ix[ 5]], uptr[ix[ 6]], uptr[ix[ 7]], }; - } #else template <typename V, typename T> SI V gather(const T* ptr, U32 ix) { return V{ ptr[ix[ 0]], ptr[ix[ 1]], ptr[ix[ 2]], ptr[ix[ 3]], ptr[ix[ 4]], ptr[ix[ 5]], ptr[ix[ 6]], ptr[ix[ 7]], }; } - - template <typename V, typename T> - SI V gather_unaligned(const T* ptr, U32 ix) { - return gather<V, T>(ptr, ix); - } #endif + // ~~~~~~ 32-bit memory loads and stores ~~~~~~ // SI void from_8888(U32 rgba, U16* r, U16* g, U16* b, U16* a) { @@ -6167,7 +6055,7 @@ LOWP_STAGE_PP(store_8888, const SkRasterPipelineContexts::MemoryCtx* 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_unaligned<U32>(ptr, ix), &r, &g, &b, &a); + from_8888(gather<U32>(ptr, ix), &r, &g, &b, &a); } // ~~~~~~ 16-bit memory loads and stores ~~~~~~ // @@ -6217,7 +6105,7 @@ LOWP_STAGE_PP(store_565, const SkRasterPipelineContexts::MemoryCtx* 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_unaligned<U16>(ptr, ix), &r, &g, &b); + from_565(gather<U16>(ptr, ix), &r, &g, &b); a = U16_255; } @@ -6267,7 +6155,7 @@ LOWP_STAGE_PP(store_4444, const SkRasterPipelineContexts::MemoryCtx* 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_unaligned<U16>(ptr, ix), &r,&g,&b,&a); + from_4444(gather<U16>(ptr, ix), &r,&g,&b,&a); } SI void from_88(U16 rg, U16* r, U16* g) { @@ -6316,7 +6204,7 @@ LOWP_STAGE_PP(store_rg88, const SkRasterPipelineContexts::MemoryCtx* 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_unaligned<U16>(ptr, ix), &r, &g); + from_88(gather<U16>(ptr, ix), &r, &g); b = U16_0; a = U16_255; } @@ -6743,11 +6631,11 @@ LOWP_STAGE_GP(bilerp_clamp_8888, const SkRasterPipelineContexts::GatherCtx* ctx) const uint32_t* ptr; U32 ix = ix_and_ptr(&ptr, ctx, sx, sy); U16 leftR, leftG, leftB, leftA; - from_8888(gather_unaligned<U32>(ptr, ix), &leftR,&leftG,&leftB,&leftA); + from_8888(gather<U32>(ptr, ix), &leftR,&leftG,&leftB,&leftA); ix = ix_and_ptr(&ptr, ctx, sx+1, sy); U16 rightR, rightG, rightB, rightA; - from_8888(gather_unaligned<U32>(ptr, ix), &rightR,&rightG,&rightB,&rightA); + from_8888(gather<U32>(ptr, ix), &rightR,&rightG,&rightB,&rightA); U16 topR = lerpX(leftR, rightR), topG = lerpX(leftG, rightG), @@ -6755,10 +6643,10 @@ LOWP_STAGE_GP(bilerp_clamp_8888, const SkRasterPipelineContexts::GatherCtx* ctx) topA = lerpX(leftA, rightA); ix = ix_and_ptr(&ptr, ctx, sx, sy+1); - from_8888(gather_unaligned<U32>(ptr, ix), &leftR,&leftG,&leftB,&leftA); + from_8888(gather<U32>(ptr, ix), &leftR,&leftG,&leftB,&leftA); ix = ix_and_ptr(&ptr, ctx, sx+1, sy+1); - from_8888(gather_unaligned<U32>(ptr, ix), &rightR,&rightG,&rightB,&rightA); + from_8888(gather<U32>(ptr, ix), &rightR,&rightG,&rightB,&rightA); U16 bottomR = lerpX(leftR, rightR), bottomG = lerpX(leftG, rightG), diff --git a/gfx/skia/skia/src/pathops/SkOpAngle.cpp b/gfx/skia/skia/src/pathops/SkOpAngle.cpp @@ -603,7 +603,7 @@ bool SkOpAngle::endsIntersect(SkOpAngle* rh) { double maxWidth = std::max(maxX - minX, maxY - minY); delta = sk_ieee_double_divide(delta, maxWidth); // FIXME: move these magic numbers - // This fixes skbug.com/40039654 + // This fixes skbug.com/8380 // Larger changes (like changing the constant in the next block) cause other // tests to fail as documented in the bug. // This could probably become a more general test: e.g., if translating the diff --git a/gfx/skia/skia/src/pathops/SkOpBuilder.cpp b/gfx/skia/skia/src/pathops/SkOpBuilder.cpp @@ -31,7 +31,7 @@ static bool one_contour(const SkPath& path) { SkSTArenaAlloc<256> allocator; int verbCount = path.countVerbs(); uint8_t* verbs = (uint8_t*) allocator.makeArrayDefault<uint8_t>(verbCount); - (void) path.getVerbs({verbs, verbCount}); + (void) path.getVerbs(verbs, verbCount); for (int index = 1; index < verbCount; ++index) { if (verbs[index] == SkPath::kMove_Verb) { return false; @@ -41,13 +41,13 @@ static bool one_contour(const SkPath& path) { } void SkOpBuilder::ReversePath(SkPath* path) { - auto lastPt = path->getLastPt(); - SkASSERT(lastPt.has_value()); - SkPathBuilder temp; - temp.moveTo(*lastPt); - SkPathPriv::ReversePathTo(&temp, *path); + SkPath temp; + SkPoint lastPt; + SkAssertResult(path->getLastPt(&lastPt)); + temp.moveTo(lastPt); + temp.reversePathTo(*path); temp.close(); - *path = temp.detach(); + *path = temp; } bool SkOpBuilder::FixWinding(SkPath* path) { @@ -105,8 +105,8 @@ bool SkOpBuilder::FixWinding(SkPath* path) { path->setFillType(fillType); return true; } - - SkPathWriter woundPath(fillType); + SkPath empty; + SkPathWriter woundPath(empty); SkOpContour* test = &contourHead; do { if (!test->count()) { @@ -118,7 +118,8 @@ bool SkOpBuilder::FixWinding(SkPath* path) { test->toPath(&woundPath); } } while ((test = test->next())); - *path = woundPath.nativePath(); + *path = *woundPath.nativePath(); + path->setFillType(fillType); return true; } @@ -139,7 +140,8 @@ void SkOpBuilder::reset() { /* OPTIMIZATION: Union doesn't need to be all-or-nothing. A run of three or more convex paths with union ops could be locally resolved and still improve over doing the ops one at a time. */ -std::optional<SkPath> SkOpBuilder::resolve() { +bool SkOpBuilder::resolve(SkPath* result) { + SkPath original = *result; int count = fOps.size(); bool allUnion = true; SkPathFirstDirection firstDir = SkPathFirstDirection::kUnknown; @@ -174,35 +176,37 @@ std::optional<SkPath> SkOpBuilder::resolve() { } } if (!allUnion) { - SkPath result = fPathRefs[0]; + *result = fPathRefs[0]; for (int index = 1; index < count; ++index) { - if (auto res = Op(result, fPathRefs[index], fOps[index])) { - result = *res; - } else { + if (!Op(*result, fPathRefs[index], fOps[index], result)) { reset(); - return {}; + *result = original; + return false; } } reset(); - return result; + return true; } SkPath sum; for (int index = 0; index < count; ++index) { - auto result = Simplify(fPathRefs[index]); - if (!result.has_value()) { + if (!Simplify(fPathRefs[index], &fPathRefs[index])) { reset(); - return {}; + *result = original; + return false; } - fPathRefs[index] = *result; if (!fPathRefs[index].isEmpty()) { // convert the even odd result back to winding form before accumulating it if (!FixWinding(&fPathRefs[index])) { - return {}; + *result = original; + return false; } sum.addPath(fPathRefs[index]); } } reset(); - - return Simplify(sum); + bool success = Simplify(sum, result); + if (!success) { + *result = original; + } + return success; } diff --git a/gfx/skia/skia/src/pathops/SkOpEdgeBuilder.h b/gfx/skia/skia/src/pathops/SkOpEdgeBuilder.h @@ -20,6 +20,16 @@ class SkPath; class SkOpEdgeBuilder { public: + SkOpEdgeBuilder(const SkPathWriter& path, SkOpContourHead* contours2, + SkOpGlobalState* globalState) + : fGlobalState(globalState) + , fPath(path.nativePath()) + , fContourBuilder(contours2) + , fContoursHead(contours2) + , fAllowOpenContours(true) { + init(); + } + SkOpEdgeBuilder(const SkPath& path, SkOpContourHead* contours2, SkOpGlobalState* globalState) : fGlobalState(globalState) , fPath(&path) diff --git a/gfx/skia/skia/src/pathops/SkOpSegment.h b/gfx/skia/skia/src/pathops/SkOpSegment.h @@ -82,7 +82,7 @@ public: SkOpSegment* addLine(SkPoint pts[2], SkOpContour* parent) { SkASSERT(pts[0] != pts[1]); init(pts, 1, parent, SkPath::kLine_Verb); - fBounds.setBounds({pts, 2}); + fBounds.setBounds(pts, 2); return this; } diff --git a/gfx/skia/skia/src/pathops/SkPathOpsAsWinding.cpp b/gfx/skia/skia/src/pathops/SkPathOpsAsWinding.cpp @@ -49,27 +49,17 @@ struct Contour { bool fReverse{false}; }; -static int VerbPtCount(SkPathVerb verb) { - static const int kPtCount[] = { 1, 1, 2, 2, 3, 0 }; - unsigned index = static_cast<unsigned>(verb); - SkASSERT(index < std::size(kPtCount)); - return kPtCount[index]; -} - -static int VerbPtIndex(SkPathVerb verb) { - static const int kPtIndex[] = { 0, 1, 1, 1, 1, 0 }; - unsigned index = static_cast<unsigned>(verb); - SkASSERT(index < std::size(kPtIndex)); - return kPtIndex[index]; -} +static const int kPtCount[] = { 1, 1, 2, 2, 3, 0 }; +static const int kPtIndex[] = { 0, 1, 1, 1, 1, 0 }; static Contour::Direction to_direction(SkScalar dy) { return dy > 0 ? Contour::Direction::kCCW : dy < 0 ? Contour::Direction::kCW : Contour::Direction::kNone; } -static int contains_edge(const SkPoint pts[4], SkPathVerb verb, SkScalar weight, const SkPoint& edge) { - SkRect bounds = SkRect::BoundsOrEmpty({pts, VerbPtCount(verb) + 1}); +static int contains_edge(SkPoint pts[4], SkPath::Verb verb, SkScalar weight, const SkPoint& edge) { + SkRect bounds; + bounds.setBounds(pts, kPtCount[verb] + 1); if (bounds.fTop > edge.fY) { return 0; } @@ -83,11 +73,11 @@ static int contains_edge(const SkPoint pts[4], SkPathVerb verb, SkScalar weight, double tVals[3]; Contour::Direction directions[3]; // must intersect horz ray with curve in case it intersects more than once - int count = (*CurveIntercept[(int)verb * 2])(pts, weight, edge.fY, tVals); + int count = (*CurveIntercept[verb * 2])(pts, weight, edge.fY, tVals); SkASSERT(between(0, count, 3)); // remove results to the right of edge for (int index = 0; index < count; ) { - SkScalar intersectX = (*CurvePointAtT[(int)verb])(pts, weight, tVals[index]).fX; + SkScalar intersectX = (*CurvePointAtT[verb])(pts, weight, tVals[index]).fX; if (intersectX < edge.fX) { ++index; continue; @@ -97,7 +87,7 @@ static int contains_edge(const SkPoint pts[4], SkPathVerb verb, SkScalar weight, continue; } // if intersect x equals edge x, we need to determine if pts is to the left or right of edge - if (pts[0].fX < edge.fX && pts[VerbPtCount(verb)].fX < edge.fX) { + if (pts[0].fX < edge.fX && pts[kPtCount[verb]].fX < edge.fX) { ++index; continue; } @@ -108,7 +98,7 @@ static int contains_edge(const SkPoint pts[4], SkPathVerb verb, SkScalar weight, } // use first derivative to determine if intersection is contributing +1 or -1 to winding for (int index = 0; index < count; ++index) { - directions[index] = to_direction((*CurveSlopeAtT[(int)verb])(pts, weight, tVals[index]).fY); + directions[index] = to_direction((*CurveSlopeAtT[verb])(pts, weight, tVals[index]).fY); } for (int index = 0; index < count; ++index) { // skip intersections that end at edge and go up @@ -120,18 +110,18 @@ static int contains_edge(const SkPoint pts[4], SkPathVerb verb, SkScalar weight, return winding; // note winding indicates containership, not contour direction } -static float conic_weight(const SkPath::IterRec& rec) { - return rec.fVerb == SkPathVerb::kConic ? rec.conicWeight() : 1; +static SkScalar conic_weight(const SkPath::Iter& iter, SkPath::Verb verb) { + return SkPath::kConic_Verb == verb ? iter.conicWeight() : 1; } -static SkPoint left_edge(const SkPoint pts[4], SkPathVerb verb, SkScalar weight) { - SkASSERT(SkPathVerb::kLine <= verb && verb <= SkPathVerb::kCubic); +static SkPoint left_edge(SkPoint pts[4], SkPath::Verb verb, SkScalar weight) { + SkASSERT(SkPath::kLine_Verb <= verb && verb <= SkPath::kCubic_Verb); SkPoint result; double t SK_INIT_TO_AVOID_WARNING; int roots = 0; - if (SkPathVerb::kLine == verb) { + if (SkPath::kLine_Verb == verb) { result = pts[0].fX < pts[1].fX ? pts[0] : pts[1]; - } else if (SkPathVerb::kQuad == verb) { + } else if (SkPath::kQuad_Verb == verb) { SkDQuad quad; quad.set(pts); if (!quad.monotonicInX()) { @@ -142,7 +132,7 @@ static SkPoint left_edge(const SkPoint pts[4], SkPathVerb verb, SkScalar weight) } else { result = pts[0].fX < pts[2].fX ? pts[0] : pts[2]; } - } else if (SkPathVerb::kConic == verb) { + } else if (SkPath::kConic_Verb == verb) { SkDConic conic; conic.set(pts, weight); if (!conic.monotonicInX()) { @@ -154,7 +144,7 @@ static SkPoint left_edge(const SkPoint pts[4], SkPathVerb verb, SkScalar weight) result = pts[0].fX < pts[2].fX ? pts[0] : pts[2]; } } else { - SkASSERT(SkPathVerb::kCubic == verb); + SkASSERT(SkPath::kCubic_Verb == verb); SkDCubic cubic; cubic.set(pts); if (!cubic.monotonicInX()) { @@ -199,12 +189,11 @@ public: containers->emplace_back(bounds, lastStart, verbStart); lastStart = verbStart; } - bounds.setBounds({&pts[VerbPtIndex(SkPathVerb::kMove)], - VerbPtCount(SkPathVerb::kMove)}); + bounds.setBounds(&pts[kPtIndex[SkPath::kMove_Verb]], kPtCount[SkPath::kMove_Verb]); } if (SkPathVerb::kLine <= verb && verb <= SkPathVerb::kCubic) { SkRect verbBounds; - verbBounds.setBounds({&pts[VerbPtIndex(verb)], VerbPtCount(verb)}); + verbBounds.setBounds(&pts[kPtIndex[(int)verb]], kPtCount[(int)verb]); bounds.joinPossiblyEmptyRect(verbBounds); } ++verbStart; @@ -217,58 +206,61 @@ public: Contour::Direction getDirection(Contour& contour) { SkPath::Iter iter(fPath, true); int verbCount = -1; + SkPath::Verb verb; + SkPoint pts[4]; SkScalar total_signed_area = 0; - while (auto rec = iter.next()) { + do { + verb = iter.next(pts); if (++verbCount < contour.fVerbStart) { continue; } if (verbCount >= contour.fVerbEnd) { continue; } - if (SkPathVerb::kLine > rec->fVerb || rec->fVerb > SkPathVerb::kCubic) { + if (SkPath::kLine_Verb > verb || verb > SkPath::kCubic_Verb) { continue; } - SkSpan<const SkPoint> pts = rec->fPoints; - switch (rec->fVerb) { - case SkPathVerb::kLine: + switch (verb) + { + case SkPath::kLine_Verb: total_signed_area += (pts[0].fY - pts[1].fY) * (pts[0].fX + pts[1].fX); break; - case SkPathVerb::kQuad: - case SkPathVerb::kConic: + case SkPath::kQuad_Verb: + case SkPath::kConic_Verb: total_signed_area += (pts[0].fY - pts[2].fY) * (pts[0].fX + pts[2].fX); break; - case SkPathVerb::kCubic: + case SkPath::kCubic_Verb: total_signed_area += (pts[0].fY - pts[3].fY) * (pts[0].fX + pts[3].fX); break; default: break; } - } + } while (SkPath::kDone_Verb != verb); return total_signed_area < 0 ? Contour::Direction::kCCW: Contour::Direction::kCW; } int nextEdge(Contour& contour, Edge edge) { SkPath::Iter iter(fPath, true); + SkPoint pts[4]; + SkPath::Verb verb; int verbCount = -1; int winding = 0; - while (auto rec = iter.next()) { - const SkPathVerb verb = rec->fVerb; + do { + verb = iter.next(pts); if (++verbCount < contour.fVerbStart) { continue; } if (verbCount >= contour.fVerbEnd) { continue; } - if (SkPathVerb::kLine > verb || verb > SkPathVerb::kCubic) { + if (SkPath::kLine_Verb > verb || verb > SkPath::kCubic_Verb) { continue; } - - SkSpan<const SkPoint> pts = rec->fPoints; bool horizontal = true; - for (int index = 1; index <= VerbPtCount(verb); ++index) { + for (int index = 1; index <= kPtCount[verb]; ++index) { if (pts[0].fY != pts[index].fY) { horizontal = false; break; @@ -278,12 +270,11 @@ public: continue; } if (edge == Edge::kCompare) { - winding += contains_edge(pts.data(), verb, conic_weight(*rec), - contour.fMinXY); + winding += contains_edge(pts, verb, conic_weight(iter, verb), contour.fMinXY); continue; } SkASSERT(edge == Edge::kInitial); - SkPoint minXY = left_edge(pts.data(), verb, conic_weight(*rec)); + SkPoint minXY = left_edge(pts, verb, conic_weight(iter, verb)); if (minXY.fX > contour.fMinXY.fX) { continue; } @@ -293,7 +284,7 @@ public: } } contour.fMinXY = minXY; - } + } while (SkPath::kDone_Verb != verb); return winding; } @@ -408,26 +399,32 @@ private: const SkPath& fPath; }; -std::optional<SkPath> AsWinding(const SkPath& path) { +static bool set_result_path(SkPath* result, const SkPath& path, SkPathFillType fillType) { + *result = path; + result->setFillType(fillType); + return true; +} + +bool AsWinding(const SkPath& path, SkPath* result) { if (!path.isFinite()) { - return {}; + return false; } SkPathFillType fillType = path.getFillType(); if (fillType == SkPathFillType::kWinding || fillType == SkPathFillType::kInverseWinding ) { - return path; + return set_result_path(result, path, fillType); } fillType = path.isInverseFillType() ? SkPathFillType::kInverseWinding : SkPathFillType::kWinding; if (path.isEmpty() || path.isConvex()) { - return path.makeFillType(fillType); + return set_result_path(result, path, fillType); } // count contours vector<Contour> contours; // one per contour OpAsWinding winder(path); winder.contourBounds(&contours); if (contours.size() <= 1) { - return path.makeFillType(fillType); + return set_result_path(result, path, fillType); } // create contour bounding box tree Contour sorted(SkRect(), 0, 0); @@ -437,14 +434,14 @@ std::optional<SkPath> AsWinding(const SkPath& path) { // if sorted has no grandchildren, no child has to fix its children's winding if (std::all_of(sorted.fChildren.begin(), sorted.fChildren.end(), [](const Contour* contour) -> bool { return contour->fChildren.empty(); } )) { - return path.makeFillType(fillType); + return set_result_path(result, path, fillType); } // starting with outermost and moving inward, see if one path contains another for (auto contour : sorted.fChildren) { winder.nextEdge(*contour, OpAsWinding::Edge::kInitial); contour->fDirection = winder.getDirection(*contour); if (!winder.checkContainerChildren(nullptr, contour)) { - return {}; + return false; } } // starting with outermost and moving inward, mark paths to reverse @@ -453,7 +450,8 @@ std::optional<SkPath> AsWinding(const SkPath& path) { reversed |= winder.markReverse(nullptr, contour); } if (!reversed) { - return path.makeFillType(fillType); + return set_result_path(result, path, fillType); } - return winder.reverseMarkedContours(contours, fillType); + *result = winder.reverseMarkedContours(contours, fillType); + return true; } diff --git a/gfx/skia/skia/src/pathops/SkPathOpsCommon.h b/gfx/skia/skia/src/pathops/SkPathOpsCommon.h @@ -30,11 +30,7 @@ SkOpSpan* FindUndone(SkOpContourHead* ); bool FixWinding(SkPath* path); bool SortContourList(SkOpContourHead** , bool evenOdd, bool oppEvenOdd); bool HandleCoincidence(SkOpContourHead* , SkOpCoincidence* ); - -std::optional<SkPath> OpDebug(const SkPath& one, const SkPath& two, SkPathOp op - SkDEBUGPARAMS(bool skipAssert) - SkDEBUGPARAMS(const char* testName)); -std::optional<SkPath> SimplifyDebug(const SkPath& path +bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result SkDEBUGPARAMS(bool skipAssert) SkDEBUGPARAMS(const char* testName)); diff --git a/gfx/skia/skia/src/pathops/SkPathOpsCubic.cpp b/gfx/skia/skia/src/pathops/SkPathOpsCubic.cpp @@ -130,7 +130,7 @@ SkDCubicPair SkDCubic::chopAt(double t) const { return dst; } -// TODO(skbug.com/40045140) deduplicate this with SkBezierCubic::ConvertToPolynomial +// TODO(skbug.com/14063) deduplicate this with SkBezierCubic::ConvertToPolynomial void SkDCubic::Coefficients(const double* src, double* A, double* B, double* C, double* D) { *A = src[6]; // d *B = src[4] * 3; // 3*c @@ -376,7 +376,7 @@ int SkDCubic::searchRoots(double extremeTs[6], int extrema, double axisIntercept // cubic roots // from SkGeometry.cpp (and Numeric Solutions, 5.6) -// // TODO(skbug.com/40045140) Deduplicate with SkCubics::RootsValidT +// // TODO(skbug.com/14063) Deduplicate with SkCubics::RootsValidT int SkDCubic::RootsValidT(double A, double B, double C, double D, double t[3]) { double s[3]; int realRoots = RootsReal(A, B, C, D, s); @@ -406,7 +406,7 @@ nextRoot: return foundRoots; } -// TODO(skbug.com/40045140) Deduplicate with SkCubics::RootsReal +// TODO(skbug.com/14063) Deduplicate with SkCubics::RootsReal int SkDCubic::RootsReal(double A, double B, double C, double D, double s[3]) { #ifdef SK_DEBUG #if ONE_OFF_DEBUG && ONE_OFF_DEBUG_MATHEMATICA diff --git a/gfx/skia/skia/src/pathops/SkPathOpsOp.cpp b/gfx/skia/skia/src/pathops/SkPathOpsOp.cpp @@ -249,7 +249,7 @@ extern void (*gVerboseFinalize)(); #endif -std::optional<SkPath> OpDebug(const SkPath& one, const SkPath& two, SkPathOp op +bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result SkDEBUGPARAMS(bool skipAssert) SkDEBUGPARAMS(const char* testName)) { #if DEBUG_DUMP_VERIFY #ifndef SK_DEBUG @@ -265,9 +265,12 @@ std::optional<SkPath> OpDebug(const SkPath& one, const SkPath& two, SkPathOp op SkPathFillType::kEvenOdd; SkRect rect1, rect2; if (kIntersect_SkPathOp == op && one.isRect(&rect1) && two.isRect(&rect2)) { - SkPath result = rect1.intersect(rect2) ? SkPath::Rect(rect1) - : SkPath(); - return result.makeFillType(fillType); + result->reset(); + result->setFillType(fillType); + if (rect1.intersect(rect2)) { + result->addRect(rect1); + } + return true; } if (one.isEmpty() || two.isEmpty()) { SkPath work; @@ -294,7 +297,7 @@ std::optional<SkPath> OpDebug(const SkPath& one, const SkPath& two, SkPathOp op if (inverseFill != work.isInverseFillType()) { work.toggleInverseFillType(); } - return Simplify(work); + return Simplify(work, result); } SkSTArenaAlloc<4096> allocator; // FIXME: add a constant expression here, tune SkOpContour contour; @@ -315,12 +318,12 @@ std::optional<SkPath> OpDebug(const SkPath& one, const SkPath& two, SkPathOp op // turn path into list of segments SkOpEdgeBuilder builder(*minuend, contourList, &globalState); if (builder.unparseable()) { - return {}; + return false; } const int xorMask = builder.xorMask(); builder.addOperand(*subtrahend); if (!builder.finish()) { - return {}; + return false; } #if DEBUG_DUMP_SEGMENTS contourList->dumpSegments("seg", op); @@ -329,7 +332,9 @@ std::optional<SkPath> OpDebug(const SkPath& one, const SkPath& two, SkPathOp op const int xorOpMask = builder.xorMask(); if (!SortContourList(&contourList, xorMask == kEvenOdd_PathOpsMask, xorOpMask == kEvenOdd_PathOpsMask)) { - return SkPath().makeFillType(fillType); + result->reset(); + result->setFillType(fillType); + return true; } // find all intersections between segments SkOpContour* current = contourList; @@ -347,18 +352,21 @@ std::optional<SkPath> OpDebug(const SkPath& one, const SkPath& two, SkPathOp op globalState.debugAddToGlobalCoinDicts(); #endif if (!success) { - return {}; + return false; } #if DEBUG_ALIGNMENT contourList->dumpSegments("aligned"); #endif // construct closed contours - SkPathWriter wrapper(fillType); + SkPath original = *result; + result->reset(); + result->setFillType(fillType); + SkPathWriter wrapper(*result); if (!bridgeOp(contourList, op, xorMask, xorOpMask, &wrapper)) { - return {}; + *result = original; + return false; } wrapper.assemble(); // if some edges could not be resolved, assemble remaining - SkPath result = wrapper.nativePath(); #if DEBUG_T_SECT_LOOP_COUNT static SkMutex& debugWorstLoop = *(new SkMutex); { @@ -369,23 +377,19 @@ std::optional<SkPath> OpDebug(const SkPath& one, const SkPath& two, SkPathOp op debugWorstState.debugDoYourWorst(&globalState); } #endif - return result; + return true; } -std::optional<SkPath> Op(const SkPath& one, const SkPath& two, SkPathOp op) { +bool Op(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result) { #if DEBUG_DUMP_VERIFY if (SkPathOpsDebug::gVerifyOp) { - if (auto result = OpDebug(one, two, op SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr))) { - VerifyOp(one, two, op, &result.value()); - return *result; - } else { + if (!OpDebug(one, two, op, result SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr))) { ReportOpFail(one, two, op); - return {}; + return false; } + VerifyOp(one, two, op, *result); + return true; } #endif - if (auto result = OpDebug(one, two, op SkDEBUGPARAMS(true) SkDEBUGPARAMS(nullptr))) { - return *result; - } - return {}; + return OpDebug(one, two, op, result SkDEBUGPARAMS(true) SkDEBUGPARAMS(nullptr)); } diff --git a/gfx/skia/skia/src/pathops/SkPathOpsQuad.cpp b/gfx/skia/skia/src/pathops/SkPathOpsQuad.cpp @@ -165,7 +165,7 @@ and using the roots t2 = C / Q */ // this does not discard real roots <= 0 or >= 1 -// TODO(skbug.com/40045140) Deduplicate with SkQuads::RootsReal +// TODO(skbug.com/14063) Deduplicate with SkQuads::RootsReal int SkDQuad::RootsReal(const double A, const double B, const double C, double s[2]) { if (!A) { return handle_zero(B, C, s); diff --git a/gfx/skia/skia/src/pathops/SkPathOpsSimplify.cpp b/gfx/skia/skia/src/pathops/SkPathOpsSimplify.cpp @@ -150,6 +150,9 @@ static bool bridgeXor(SkOpContourHead* contourList, SkPathWriter* writer) { static bool path_is_trivial(const SkPath& path) { SkPath::Iter iter(path, true); + SkPath::Verb verb; + SkPoint points[4]; + class Trivializer { SkPoint prevPt{0,0}; SkVector prevVec{0,0}; @@ -174,43 +177,46 @@ static bool path_is_trivial(const SkPath& path) { } } triv; - while (auto rec = iter.next()) { - SkSpan<const SkPoint> points = rec->fPoints; - switch (rec->fVerb) { - case SkPathVerb::kMove: + while ((verb = iter.next(points)) != SkPath::kDone_Verb) { + switch (verb) { + case SkPath::kMove_Verb: triv.moveTo(points[0]); break; - case SkPathVerb::kCubic: + case SkPath::kCubic_Verb: if (!triv.addTrivialContourPoint(points[3])) { return false; } [[fallthrough]]; - case SkPathVerb::kConic: - case SkPathVerb::kQuad: + case SkPath::kConic_Verb: + case SkPath::kQuad_Verb: if (!triv.addTrivialContourPoint(points[2])) { return false; } [[fallthrough]]; - case SkPathVerb::kLine: + case SkPath::kLine_Verb: if (!triv.addTrivialContourPoint(points[1])) { return false; } if (!triv.addTrivialContourPoint(points[0])) { return false; } break; - case SkPathVerb::kClose: + case SkPath::kClose_Verb: + case SkPath::kDone_Verb: break; } } return true; } -std::optional<SkPath> SimplifyDebug(const SkPath& path SkDEBUGPARAMS(bool skipAssert) - SkDEBUGPARAMS(const char* testName)) { +// FIXME : add this as a member of SkPath +bool SimplifyDebug(const SkPath& path, SkPath* result + SkDEBUGPARAMS(bool skipAssert) SkDEBUGPARAMS(const char* testName)) { // returns 1 for evenodd, -1 for winding, regardless of inverse-ness SkPathFillType fillType = path.isInverseFillType() ? SkPathFillType::kInverseEvenOdd : SkPathFillType::kEvenOdd; if (path.isConvex()) { - SkPath result; - // If the path is trivially convex, simplify to empty, else copy - if (!path_is_trivial(path)) { - result = path; + // If the path is trivially convex, simplify to empty. + if (path_is_trivial(path)) { + result->reset(); + } else if (result != &path) { + *result = path; } - return result.makeFillType(fillType); + result->setFillType(fillType); + return true; } // turn path into list of segments SkSTArenaAlloc<4096> allocator; // FIXME: constant-ize, tune @@ -232,13 +238,15 @@ std::optional<SkPath> SimplifyDebug(const SkPath& path SkDEBUGPARAMS(bool skipAs #endif SkOpEdgeBuilder builder(path, contourList, &globalState); if (!builder.finish()) { - return {}; + return false; } #if DEBUG_DUMP_SEGMENTS contour.dumpSegments(); #endif if (!SortContourList(&contourList, false, false)) { - return SkPath().makeFillType(fillType); + result->reset(); + result->setFillType(fillType); + return true; } // find all intersections between segments SkOpContour* current = contourList; @@ -255,31 +263,33 @@ std::optional<SkPath> SimplifyDebug(const SkPath& path SkDEBUGPARAMS(bool skipAs globalState.debugAddToGlobalCoinDicts(); #endif if (!success) { - return {}; + return false; } #if DEBUG_DUMP_ALIGNMENT contour.dumpSegments("aligned"); #endif // construct closed contours - SkPathWriter wrapper(fillType); + result->reset(); + result->setFillType(fillType); + SkPathWriter wrapper(*result); if (builder.xorMask() == kWinding_PathOpsMask ? !bridgeWinding(contourList, &wrapper) : !bridgeXor(contourList, &wrapper)) { - return {}; + return false; } wrapper.assemble(); // if some edges could not be resolved, assemble remaining - return wrapper.nativePath(); + return true; } -std::optional<SkPath> Simplify(const SkPath& path) { - auto result = SimplifyDebug(path SkDEBUGPARAMS(true) SkDEBUGPARAMS(nullptr)); +bool Simplify(const SkPath& path, SkPath* result) { #if DEBUG_DUMP_VERIFY if (SkPathOpsDebug::gVerifyOp) { - if (result.has_value()) { - VerifySimplify(path, *result); - } else { + if (!SimplifyDebug(path, result SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr))) { ReportSimplifyFail(path); + return false; } + VerifySimplify(path, *result); + return true; } #endif - return result; + return SimplifyDebug(path, result SkDEBUGPARAMS(true) SkDEBUGPARAMS(nullptr)); } diff --git a/gfx/skia/skia/src/pathops/SkPathWriter.cpp b/gfx/skia/skia/src/pathops/SkPathWriter.cpp @@ -9,7 +9,6 @@ #include "include/core/SkTypes.h" #include "include/private/base/SkMath.h" #include "src/base/SkTSort.h" -#include "src/core/SkPathPriv.h" #include "src/pathops/SkOpSegment.h" #include "src/pathops/SkOpSpan.h" #include "src/pathops/SkPathOpsDebug.h" @@ -17,7 +16,10 @@ using namespace skia_private; -SkPathWriter::SkPathWriter(SkPathFillType ft) : fBuilder(ft) { +// wrap path to keep track of whether the contour is initialized and non-empty +SkPathWriter::SkPathWriter(SkPath& path) + : fPathPtr(&path) +{ init(); } @@ -30,7 +32,7 @@ void SkPathWriter::close() { SkDebugf("path.close();\n"); #endif fCurrent.close(); - fBuilder.addPath(fCurrent); + fPathPtr->addPath(fCurrent); fCurrent.reset(); init(); } @@ -226,7 +228,8 @@ void SkPathWriter::assemble() { // lengthen any partial contour adjacent to a simple segment for (int pIndex = 0; pIndex < endCount; pIndex++) { SkOpPtT* opPtT = const_cast<SkOpPtT*>(runs[pIndex]); - SkPathWriter partWriter(SkPathFillType::kDefault); + SkPath p; + SkPathWriter partWriter(p); do { if (!zero_or_one(opPtT->fT)) { break; @@ -353,19 +356,16 @@ void SkPathWriter::assemble() { do { const SkPath& contour = fPartials[rIndex]; if (!first) { - auto prior = fBuilder.getLastPt(); - if (!prior) { + SkPoint prior, next; + if (!fPathPtr->getLastPt(&prior)) { return; } - SkPoint next; if (forward) { next = contour.getPoint(0); } else { - auto lastPt = contour.getLastPt(); - SkASSERT(lastPt.has_value()); - next = *lastPt; + SkAssertResult(contour.getLastPt(&next)); } - if (*prior != next) { + if (prior != next) { /* TODO: if there is a gap between open path written so far and path to come, connect by following segments from one to the other, rather than introducing a diagonal to connect the two. @@ -373,11 +373,11 @@ void SkPathWriter::assemble() { } } if (forward) { - fBuilder.addPath(contour, + fPathPtr->addPath(contour, first ? SkPath::kAppend_AddPathMode : SkPath::kExtend_AddPathMode); } else { SkASSERT(!first); - SkPathPriv::ReversePathTo(&fBuilder, contour); + fPathPtr->reversePathTo(contour); } if (first) { first = false; @@ -388,7 +388,7 @@ void SkPathWriter::assemble() { sIndex == ((rIndex != eIndex) ^ forward ? eIndex : ~eIndex)); #endif if (sIndex == ((rIndex != eIndex) ^ forward ? eIndex : ~eIndex)) { - fBuilder.close(); + fPathPtr->close(); break; } if (forward) { diff --git a/gfx/skia/skia/src/pathops/SkPathWriter.h b/gfx/skia/skia/src/pathops/SkPathWriter.h @@ -8,7 +8,6 @@ #define SkPathWriter_DEFINED #include "include/core/SkPath.h" -#include "include/core/SkPathBuilder.h" #include "include/core/SkPoint.h" #include "include/core/SkScalar.h" #include "include/private/base/SkTArray.h" @@ -22,7 +21,7 @@ class SkOpPtT; class SkPathWriter { public: - SkPathWriter(SkPathFillType); + SkPathWriter(SkPath& path); void assemble(); void conicTo(const SkPoint& pt1, const SkOpPtT* pt2, SkScalar weight); void cubicTo(const SkPoint& pt1, const SkPoint& pt2, const SkOpPtT* pt3); @@ -32,7 +31,7 @@ public: bool hasMove() const { return !fFirstPtT; } void init(); bool isClosed() const; - SkPath nativePath() { return fBuilder.detach(); } + const SkPath* nativePath() const { return fPathPtr; } void quadTo(const SkPoint& pt1, const SkOpPtT* pt2); private: @@ -46,10 +45,10 @@ private: bool someAssemblyRequired(); SkPoint update(const SkOpPtT* pt); - SkPathBuilder fBuilder; SkPath fCurrent; // contour under construction skia_private::TArray<SkPath> fPartials; // contours with mismatched starts and ends SkTDArray<const SkOpPtT*> fEndPtTs; // possible pt values for partial starts and ends + SkPath* fPathPtr; // closed contours are written here const SkOpPtT* fDefer[2]; // [0] deferred move, [1] deferred line const SkOpPtT* fFirstPtT; // first in current contour }; diff --git a/gfx/skia/skia/src/pdf/SkKeyedImage.cpp b/gfx/skia/skia/src/pdf/SkKeyedImage.cpp @@ -39,7 +39,7 @@ SkKeyedImage::SkKeyedImage(const SkBitmap& bm) : fImage(bm.asImage()) { SkKeyedImage SkKeyedImage::subset(SkIRect subset) const { SkKeyedImage img; if (fImage && subset.intersect(fImage->bounds())) { - img.fImage = fImage->makeSubset(nullptr, subset, {}); + img.fImage = fImage->makeSubset(nullptr, subset); if (img.fImage) { img.fKey = {subset.makeOffset(fKey.fSubset.topLeft()), fKey.fID}; } diff --git a/gfx/skia/skia/src/pdf/SkPDFBitmap.cpp b/gfx/skia/skia/src/pdf/SkPDFBitmap.cpp @@ -98,11 +98,8 @@ void emit_image_stream(SkPDFDocument* doc, SkISize size, SkPDFUnion&& colorSpace, SkPDFIndirectReference sMask, - size_t length, + int length, SkPDFStreamFormat format) { - if (!ref) { - return; - } SkPDFDict pdfDict("XObject"); pdfDict.insertName("Subtype", "Image"); pdfDict.insertInt("Width", size.width()); @@ -124,7 +121,7 @@ void emit_image_stream(SkPDFDocument* doc, doc->emitStream(pdfDict, std::move(writeStream), ref); } -size_t do_deflated_alpha(const SkPixmap& pm, SkPDFDocument* doc, SkPDFIndirectReference ref) { +void do_deflated_alpha(const SkPixmap& pm, SkPDFDocument* doc, SkPDFIndirectReference ref) { SkPDF::Metadata::CompressionLevel compressionLevel = doc->metadata().fCompressionLevel; SkPDFStreamFormat format = compressionLevel == SkPDF::Metadata::CompressionLevel::None ? SkPDFStreamFormat::Uncompressed @@ -162,11 +159,10 @@ size_t do_deflated_alpha(const SkPixmap& pm, SkPDFDocument* doc, SkPDFIndirectRe deflateWStream->finalize(); } - size_t length = SkToInt(buffer.bytesWritten()); + int length = SkToInt(buffer.bytesWritten()); emit_image_stream(doc, ref, [&buffer](SkWStream* stream) { buffer.writeToAndReset(stream); }, pm.info().dimensions(), SkPDFUnion::Name("DeviceGray"), SkPDFIndirectReference(), length, format); - return length; } SkPDFUnion write_icc_profile(SkPDFDocument* doc, sk_sp<SkData>&& icc, int channels) { @@ -200,21 +196,23 @@ bool icc_channel_mismatch(const skcms_ICCProfile* iccProfile, int expectedChanne return 0 < iccChannels && expectedChannels != iccChannels; } -size_t do_deflated_image(const SkPixmap& pm, - SkPDFDocument* doc, - bool isOpaque, - SkPDFIndirectReference ref) { +void do_deflated_image(const SkPixmap& pm, + SkPDFDocument* doc, + bool isOpaque, + SkPDFIndirectReference ref) { + SkPDFIndirectReference sMask; + if (!isOpaque) { + sMask = doc->reserveRef(); + } SkPDF::Metadata::CompressionLevel compressionLevel = doc->metadata().fCompressionLevel; SkPDFStreamFormat format = compressionLevel == SkPDF::Metadata::CompressionLevel::None ? SkPDFStreamFormat::Uncompressed : SkPDFStreamFormat::Flate; - SkDynamicMemoryWStream dynamic; - SkNullWStream writeOnly; - SkWStream* buffer = ref ? static_cast<SkWStream*>(&dynamic) : &writeOnly; - SkWStream* stream = buffer; + SkDynamicMemoryWStream buffer; + SkWStream* stream = &buffer; std::optional<SkDeflateWStream> deflateWStream; if (format == SkPDFStreamFormat::Flate) { - deflateWStream.emplace(buffer, SkToInt(compressionLevel)); + deflateWStream.emplace(&buffer, SkToInt(compressionLevel)); stream = &*deflateWStream; } SkPDFUnion colorSpace = SkPDFUnion::Name("DeviceGray"); @@ -226,7 +224,7 @@ size_t do_deflated_image(const SkPixmap& pm, break; case kGray_8_SkColorType: channels = 1; - SkASSERT(isOpaque); + SkASSERT(sMask.fValue = -1); SkASSERT(pm.rowBytes() == (size_t)pm.width()); stream->write(pm.addr8(), pm.width() * pm.height()); break; @@ -271,32 +269,23 @@ size_t do_deflated_image(const SkPixmap& pm, } } - size_t length = buffer->bytesWritten(); - SkPDFIndirectReference sMask; - if (!isOpaque && ref) { - sMask = doc->reserveRef(); - } - emit_image_stream(doc, ref, [&dynamic](SkWStream* stream) { dynamic.writeToAndReset(stream); }, + 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); if (!isOpaque) { - length += do_deflated_alpha(pm, doc, sMask); + do_deflated_alpha(pm, doc, sMask); } - return length; } -size_t do_jpeg(sk_sp<SkData> data, SkColorSpace* imageColorSpace, SkPDFDocument* doc, SkISize size, +bool do_jpeg(sk_sp<SkData> data, SkColorSpace* imageColorSpace, SkPDFDocument* doc, SkISize size, SkPDFIndirectReference ref) { - if (!ref) { - return data->size(); - } - SkPDF::DecodeJpegCallback decodeJPEG = doc->metadata().jpegDecoder; if (!decodeJPEG) { - return 0; + return false; } std::unique_ptr<SkCodec> codec = decodeJPEG(data); if (!codec) { - return 0; + return false; } SkISize jpegSize = codec->dimensions(); @@ -309,7 +298,7 @@ size_t do_jpeg(sk_sp<SkData> data, SkColorSpace* imageColorSpace, SkPDFDocument* if (jpegSize != size // Safety check. || !goodColorType || kTopLeft_SkEncodedOrigin != exifOrientation) { - return 0; + return false; } int channels = yuv ? 3 : 1; @@ -337,7 +326,7 @@ size_t do_jpeg(sk_sp<SkData> data, SkColorSpace* imageColorSpace, SkPDFDocument* [&data](SkWStream* dst) { dst->write(data->data(), data->size()); }, jpegSize, std::move(colorSpace), SkPDFIndirectReference(), SkToInt(data->size()), SkPDFStreamFormat::DCT); - return data->size(); + return true; } SkBitmap to_pixels(const SkImage* image) { @@ -365,18 +354,18 @@ SkBitmap to_pixels(const SkImage* image) { return bm; } -size_t serialize_image(const SkImage* img, - int encodingQuality, - SkPDFDocument* doc, - SkPDFIndirectReference ref) { +void serialize_image(const SkImage* img, + int encodingQuality, + SkPDFDocument* doc, + SkPDFIndirectReference ref) { SkASSERT(img); SkASSERT(doc); SkASSERT(encodingQuality >= 0); SkISize dimensions = img->dimensions(); if (sk_sp<SkData> data = img->refEncodedData()) { - if (size_t size = do_jpeg(std::move(data), img->colorSpace(), doc, dimensions, ref)) { - return size; + if (do_jpeg(std::move(data), img->colorSpace(), doc, dimensions, ref)) { + return; } } SkBitmap bm = to_pixels(img); @@ -387,20 +376,16 @@ size_t serialize_image(const SkImage* img, if (encodeJPEG && encodingQuality <= 100 && isOpaque) { SkDynamicMemoryWStream stream; if (encodeJPEG(&stream, pm, encodingQuality)) { - if (size_t size = do_jpeg(stream.detachAsData(), pm.colorSpace(), doc, dimensions, ref)) { - return size; + if (do_jpeg(stream.detachAsData(), pm.colorSpace(), doc, dimensions, ref)) { + return; } } } - return do_deflated_image(pm, doc, isOpaque, ref); + do_deflated_image(pm, doc, isOpaque, ref); } } // namespace -size_t SkPDFSerializeImageSize(const SkImage* img, SkPDFDocument* doc, int encodingQuality) { - return serialize_image(img, encodingQuality, doc, SkPDFIndirectReference()); -} - SkPDFIndirectReference SkPDFSerializeImage(const SkImage* img, SkPDFDocument* doc, int encodingQuality) { diff --git a/gfx/skia/skia/src/pdf/SkPDFBitmap.h b/gfx/skia/skia/src/pdf/SkPDFBitmap.h @@ -23,9 +23,7 @@ struct SkPDFIndirectReference; */ SkPDFIndirectReference SkPDFSerializeImage(const SkImage* img, SkPDFDocument* doc, - int encodingQuality); - -size_t SkPDFSerializeImageSize(const SkImage* img, SkPDFDocument* doc, int encodingQuality); + int encodingQuality = 101); struct SkPDFIccProfileKey { sk_sp<SkData> fData; diff --git a/gfx/skia/skia/src/pdf/SkPDFDevice.cpp b/gfx/skia/skia/src/pdf/SkPDFDevice.cpp @@ -23,10 +23,8 @@ #include "include/core/SkImageInfo.h" #include "include/core/SkM44.h" #include "include/core/SkMaskFilter.h" -#include "include/core/SkMatrix.h" #include "include/core/SkPaint.h" #include "include/core/SkPath.h" -#include "include/core/SkPathBuilder.h" #include "include/core/SkPathEffect.h" #include "include/core/SkPathTypes.h" #include "include/core/SkPathUtils.h" @@ -61,13 +59,11 @@ #include "src/core/SkMask.h" #include "src/core/SkMaskFilterBase.h" #include "src/core/SkPaintPriv.h" -#include "src/core/SkPathPriv.h" #include "src/core/SkRasterClip.h" #include "src/core/SkSpecialImage.h" #include "src/core/SkStrikeSpec.h" #include "src/pdf/SkBitmapKey.h" #include "src/pdf/SkClusterator.h" -#include "src/pdf/SkKeyedImage.h" #include "src/pdf/SkPDFBitmap.h" #include "src/pdf/SkPDFDocumentPriv.h" #include "src/pdf/SkPDFFont.h" @@ -148,21 +144,21 @@ void SkPDFDevice::MarkedContentManager::beginMark() { fNextMarksElemId == SkPDF::NodeID::PaginationFooterArtifact || fNextMarksElemId == SkPDF::NodeID::PaginationWatermarkArtifact) { - fOut->writeText(" <</Type /Pagination"); + fOut->writeText(" <</Type Pagination"); if (fNextMarksElemId == SkPDF::NodeID::PaginationHeaderArtifact) { - fOut->writeText(" /Subtype /Header"); + fOut->writeText(" /Subtype Header"); } else if (fNextMarksElemId == SkPDF::NodeID::PaginationFooterArtifact) { - fOut->writeText(" /Subtype /Footer"); + fOut->writeText(" /Subtype Footer"); } else if (fNextMarksElemId == SkPDF::NodeID::PaginationWatermarkArtifact) { - fOut->writeText(" /Subtype /Watermark"); + fOut->writeText(" /Subtype Watermark"); } fOut->writeText(" >>BDC\n"); } else if (fNextMarksElemId == SkPDF::NodeID::LayoutArtifact) { - fOut->writeText(" <</Type /Layout >>BDC\n"); + fOut->writeText(" <</Type Layout >>BDC\n"); } else if (fNextMarksElemId == SkPDF::NodeID::PageArtifact) { - fOut->writeText(" <</Type /Page >>BDC\n"); + fOut->writeText(" <</Type Page >>BDC\n"); } else if (fNextMarksElemId == SkPDF::NodeID::BackgroundArtifact) { - fOut->writeText(" <</Type /Background >>BDC\n"); + fOut->writeText(" <</Type Background >>BDC\n"); } fCurrentMarksElemId = fNextMarksElemId; } @@ -227,16 +223,17 @@ static int add_resource(THashSet<SkPDFIndirectReference>& resources, SkPDFIndire } static void draw_points(SkCanvas::PointMode mode, - SkSpan<const SkPoint> points, + size_t count, + const SkPoint* points, const SkPaint& paint, const SkIRect& bounds, SkDevice* device) { SkRasterClip rc(bounds); - skcpu::Draw draw; + SkDraw draw; draw.fDst = SkPixmap(SkImageInfo::MakeUnknown(bounds.right(), bounds.bottom()), nullptr, 0); draw.fCTM = &device->localToDevice(); draw.fRC = &rc; - draw.drawPoints(mode, points, paint, device); + draw.drawPoints(mode, count, points, paint, device); } static void transform_shader(SkPaint* paint, const SkMatrix& ctm) { @@ -292,11 +289,7 @@ static void set_style(SkTCopyOnFirstWrite<SkPaint>* paint, SkPaint::Style style) static bool calculate_inverse_path(const SkRect& bounds, const SkPath& invPath, SkPath* outPath) { SkASSERT(invPath.isInverseFillType()); - if (auto result = Op(SkPath::Rect(bounds), invPath, kIntersect_SkPathOp)) { - *outPath = *result; - return true; - } - return false; + return Op(SkPath::Rect(bounds), invPath, kIntersect_SkPathOp, outPath); } sk_sp<SkDevice> SkPDFDevice::createDevice(const CreateInfo& cinfo, const SkPaint* layerPaint) { @@ -305,7 +298,7 @@ sk_sp<SkDevice> SkPDFDevice::createDevice(const CreateInfo& cinfo, const SkPaint // printer resolution. // TODO: It may be possible to express some filters natively using PDF - // to improve quality and file size (skbug.com/40034150) + // to improve quality and file size (https://bug.skia.org/3043) if ((layerPaint && (layerPaint->getImageFilter() || layerPaint->getColorFilter())) || (cinfo.fInfo.colorSpace() && !cinfo.fInfo.colorSpace()->isSRGB())) { // need to return a raster device, which we will detect in drawDevice() @@ -434,8 +427,8 @@ void SkPDFDevice::drawAnnotation(const SkRect& rect, const char key[], SkData* v return; } if (!strcmp(SkAnnotationKeys::Define_Named_Dest_Key(), key)) { - SkPoint p = this->localToDevice().mapPoint({rect.x(), rect.y()}); - p = pageXform.mapPoint(p); + SkPoint p = this->localToDevice().mapXY(rect.x(), rect.y()); + pageXform.mapPoints(&p, 1); auto pg = fDocument->currentPage(); fDocument->fNamedDestinations.push_back(SkPDFNamedDestination{sk_ref_sp(value), p, pg}); } @@ -443,10 +436,9 @@ void SkPDFDevice::drawAnnotation(const SkRect& rect, const char key[], SkData* v } // Convert to path to handle non-90-degree rotations. SkPath path = SkPath::Rect(rect).makeTransform(this->localToDevice()); - SkPath clip = SkClipStack_AsPath(this->cs()); - if (auto result = Op(clip, path, kIntersect_SkPathOp)) { - path = *result; - } + SkPath clip; + SkClipStack_AsPath(this->cs(), &clip); + Op(clip, path, kIntersect_SkPathOp, &path); // PDF wants a rectangle only. SkRect transformedRect = pageXform.mapRect(path.getBounds()); if (transformedRect.isEmpty()) { @@ -468,27 +460,29 @@ void SkPDFDevice::drawAnnotation(const SkRect& rect, const char key[], SkData* v } void SkPDFDevice::drawPaint(const SkPaint& srcPaint) { - if (this->hasEmptyClip()) { + SkMatrix inverse; + if (!this->localToDevice().invert(&inverse)) { return; } - // Clip is in device space. Transform shader into device space. SkRect bbox = this->cs().bounds(this->bounds()); + inverse.mapRect(&bbox); bbox.roundOut(&bbox); + if (this->hasEmptyClip()) { + return; + } SkPaint newPaint = srcPaint; newPaint.setStyle(SkPaint::kFill_Style); - if (newPaint.getShader()) { - newPaint.setShader(newPaint.getShader()->makeWithLocalMatrix(this->localToDevice())); - } - this->internalDrawPath(this->cs(), SkMatrix::I(), SkPath::Rect(bbox), newPaint, true); + this->drawRect(bbox, newPaint); } void SkPDFDevice::drawPoints(SkCanvas::PointMode mode, - SkSpan<const SkPoint> points, + size_t count, + const SkPoint* points, const SkPaint& srcPaint) { if (this->hasEmptyClip()) { return; } - if (points.size() == 0) { + if (count == 0) { return; } SkTCopyOnFirstWrite<SkPaint> paint(clean_paint(srcPaint)); @@ -499,11 +493,11 @@ void SkPDFDevice::drawPoints(SkCanvas::PointMode mode, set_style(&paint, SkPaint::kStroke_Style); } - // skcpu::Draw::drawPoints converts to multiple calls to fDevice->drawPath. + // SkDraw::drawPoints converts to multiple calls to fDevice->drawPath. // We only use this when there's a path effect or perspective because of the overhead // of multiple calls to setUpContentEntry it causes. if (paint->getPathEffect() || this->localToDevice().hasPerspective()) { - draw_points(mode, points, *paint, this->devClipBounds(), this); + draw_points(mode, count, points, *paint, this->devClipBounds(), this); return; } @@ -515,8 +509,8 @@ void SkPDFDevice::drawPoints(SkCanvas::PointMode mode, set_style(&paint, SkPaint::kFill_Style); SkScalar strokeWidth = paint->getStrokeWidth(); SkScalar halfStroke = SkScalarHalf(strokeWidth); - for (auto&& pt : points) { - SkRect r = SkRect::MakeXYWH(pt.fX, pt.fY, 0, 0); + for (size_t i = 0; i < count; i++) { + SkRect r = SkRect::MakeXYWH(points[i].fX, points[i].fY, 0, 0); r.inset(-halfStroke, -halfStroke); this->drawRect(r, *paint); } @@ -541,11 +535,10 @@ void SkPDFDevice::drawPoints(SkCanvas::PointMode mode, // The points do not already have localToDevice applied. pageXform.preConcat(this->localToDevice()); - for (auto&& userPoint : points) { + for (auto&& userPoint : SkSpan(points, count)) { fMarkManager.accumulate(pageXform.mapPoint(userPoint)); } } - const size_t count = points.size(); switch (mode) { case SkCanvas::kPolygon_PointMode: SkPDFUtils::MoveTo(points[0].fX, points[0].fY, contentStream); @@ -597,24 +590,19 @@ void SkPDFDevice::internalDrawPathWithFilter(const SkClipStack& clipStack, const SkPath& origPath, const SkPaint& origPaint) { SkASSERT(origPaint.getMaskFilter()); - SkPathBuilder builder; + SkPath path(origPath); SkTCopyOnFirstWrite<SkPaint> paint(origPaint); - SkStrokeRec::InitStyle initStyle = skpathutils::FillPathWithPaint(origPath, *paint, &builder) + SkStrokeRec::InitStyle initStyle = skpathutils::FillPathWithPaint(path, *paint, &path) ? SkStrokeRec::kFill_InitStyle : SkStrokeRec::kHairline_InitStyle; - builder.transform(ctm); - SkPathRaw pathRaw = SkPathPriv::Raw(builder); + path.transform(ctm, &path); SkIRect bounds = clipStack.bounds(this->bounds()).roundOut(); SkMaskBuilder sourceMask; - if (!skcpu::DrawToMask(pathRaw, - bounds, - paint->getMaskFilter(), - &SkMatrix::I(), - &sourceMask, - SkMaskBuilder::kComputeBoundsAndRenderImage_CreateMode, - initStyle)) { + if (!SkDraw::DrawToMask(path, bounds, paint->getMaskFilter(), &SkMatrix::I(), + &sourceMask, SkMaskBuilder::kComputeBoundsAndRenderImage_CreateMode, + initStyle)) { return; } SkAutoMaskFreeImage srcAutoMaskFreeImage(sourceMask.image()); @@ -643,7 +631,7 @@ void SkPDFDevice::internalDrawPathWithFilter(const SkClipStack& clipStack, maskDevice->makeFormXObjectFromDevice(dstMaskBounds, true), false, SkPDFGraphicState::kLuminosity_SMaskMode, fDocument), content.stream()); SkPDFUtils::AppendRectangle(SkRect::Make(dstMaskBounds), content.stream()); - SkPDFUtils::PaintPath(SkPaint::kFill_Style, pathRaw.fillType(), content.stream()); + SkPDFUtils::PaintPath(SkPaint::kFill_Style, path.getFillType(), content.stream()); this->clearMaskOnGraphicState(content.stream()); } @@ -685,8 +673,12 @@ void SkPDFDevice::internalDrawPath(const SkClipStack& clipStack, if (clipStack.isEmpty(this->bounds())) { return; } - SkPathBuilder builder; - if (skpathutils::FillPathWithPaint(*pathPtr, *paint, &builder)) { + if (!pathIsMutable) { + modifiedPath = origPath; + pathPtr = &modifiedPath; + pathIsMutable = true; + } + if (skpathutils::FillPathWithPaint(*pathPtr, *paint, pathPtr)) { set_style(&paint, SkPaint::kFill_Style); } else { set_style(&paint, SkPaint::kStroke_Style); @@ -694,9 +686,6 @@ void SkPDFDevice::internalDrawPath(const SkClipStack& clipStack, paint.writable()->setStrokeWidth(0); } } - modifiedPath = builder.detach(); - pathPtr = &modifiedPath; - pathIsMutable = true; paint.writable()->setPathEffect(nullptr); } @@ -709,7 +698,7 @@ void SkPDFDevice::internalDrawPath(const SkClipStack& clipStack, pathPtr = &modifiedPath; pathIsMutable = true; } - *pathPtr = pathPtr->makeTransform(matrix); + pathPtr->transform(matrix); if (paint->getShader()) { transform_shader(paint.writable(), matrix); } @@ -870,26 +859,26 @@ static bool contains(const SkRect& r, SkPoint p) { void SkPDFDevice::drawGlyphRunAsPath( const sktext::GlyphRun& glyphRun, SkPoint offset, const SkPaint& runPaint) { const SkFont& font = glyphRun.font(); + SkPath path; struct Rec { - SkPathBuilder fBuilder; + SkPath* fPath; SkPoint fOffset; const SkPoint* fPos; - } rec = {{}, offset, glyphRun.positions().data()}; + } rec = {&path, offset, glyphRun.positions().data()}; - font.getPaths(glyphRun.glyphsIDs(), + font.getPaths(glyphRun.glyphsIDs().data(), glyphRun.glyphsIDs().size(), [](const SkPath* path, const SkMatrix& mx, void* ctx) { Rec* rec = reinterpret_cast<Rec*>(ctx); if (path) { SkMatrix total = mx; total.postTranslate(rec->fPos->fX + rec->fOffset.fX, rec->fPos->fY + rec->fOffset.fY); - rec->fBuilder.addPath(*path, total); + rec->fPath->addPath(*path, total); } rec->fPos += 1; // move to the next glyph's position }, &rec); - this->internalDrawPath(this->cs(), this->localToDevice(), - rec.fBuilder.detach(), runPaint, true); + this->internalDrawPath(this->cs(), this->localToDevice(), path, runPaint, true); SkFont transparentFont = glyphRun.font(); transparentFont.setEmbolden(false); // Stop Recursion @@ -1201,11 +1190,8 @@ std::unique_ptr<SkStreamAsset> SkPDFDevice::content() { * in the first place. */ bool SkPDFDevice::handleInversePath(const SkPath& origPath, - const SkPaint& srcPaint, + const SkPaint& paint, bool pathIsMutable) { - // Assume the caller has already applied the path effect. - SkASSERT(!srcPaint.getPathEffect()); - if (!origPath.isInverseFillType()) { return false; } @@ -1214,50 +1200,58 @@ bool SkPDFDevice::handleInversePath(const SkPath& origPath, return false; } - SkTCopyOnFirstWrite<SkPaint> paint(srcPaint); SkPath modifiedPath; - const SkPath* pathPtr = &origPath; + SkPath* pathPtr = const_cast<SkPath*>(&origPath); + SkPaint noInversePaint(paint); // Merge stroking operations into final path. - if (SkPaint::kStroke_Style == paint->getStyle() || - SkPaint::kStrokeAndFill_Style == paint->getStyle()) - { - SkPathBuilder builder; - bool doFillPath = skpathutils::FillPathWithPaint(origPath, *paint, &builder); - modifiedPath = builder.detach(); - pathPtr = &modifiedPath; - + if (SkPaint::kStroke_Style == paint.getStyle() || + SkPaint::kStrokeAndFill_Style == paint.getStyle()) { + bool doFillPath = skpathutils::FillPathWithPaint(origPath, paint, &modifiedPath); if (doFillPath) { - SkPaint* modifiedPaint = paint.writable(); - modifiedPaint->setStyle(SkPaint::kFill_Style); - modifiedPaint->setStrokeWidth(0); + noInversePaint.setStyle(SkPaint::kFill_Style); + noInversePaint.setStrokeWidth(0); + pathPtr = &modifiedPath; } else { - // Hairline strokes are rendered non-inverted. + // To be consistent with the raster output, hairline strokes + // are rendered as non-inverted. modifiedPath.toggleInverseFillType(); - this->internalDrawPath(this->cs(), this->localToDevice(), modifiedPath, *paint, true); + this->internalDrawPath(this->cs(), this->localToDevice(), modifiedPath, paint, true); return true; } } - // Clip is in device space. Transform path and shader into device space. - SkRect bounds = this->cs().bounds(this->bounds()); - modifiedPath = pathPtr->makeTransform(this->localToDevice()); - if (!calculate_inverse_path(bounds, modifiedPath, &modifiedPath)) { + // Get bounds of clip in current transform space + // (clip bounds are given in device space). + SkMatrix transformInverse; + SkMatrix totalMatrix = this->localToDevice(); + + if (!totalMatrix.invert(&transformInverse)) { return false; } - if (paint->getShader()) { - paint.writable()->setShader(paint->getShader()->makeWithLocalMatrix(this->localToDevice())); + SkRect bounds = this->cs().bounds(this->bounds()); + transformInverse.mapRect(&bounds); + + // Extend the bounds by the line width (plus some padding) + // so the edge doesn't cause a visible stroke. + bounds.outset(paint.getStrokeWidth() + SK_Scalar1, + paint.getStrokeWidth() + SK_Scalar1); + + if (!calculate_inverse_path(bounds, *pathPtr, &modifiedPath)) { + return false; } - this->internalDrawPath(this->cs(), SkMatrix::I(), modifiedPath, *paint, true); + + this->internalDrawPath(this->cs(), this->localToDevice(), modifiedPath, noInversePaint, true); return true; } SkPDFIndirectReference SkPDFDevice::makeFormXObjectFromDevice(SkIRect bounds, bool alpha) { SkMatrix inverseTransform = SkMatrix::I(); - if (auto inv = fInitialTransform.invert()) { - inverseTransform = *inv; - } else { - SkDEBUGFAIL("Layer initial transform should be invertible."); + if (!fInitialTransform.isIdentity()) { + if (!fInitialTransform.invert(&inverseTransform)) { + SkDEBUGFAIL("Layer initial transform should be invertible."); + inverseTransform.reset(); + } } const char* colorSpace = alpha ? "DeviceGray" : nullptr; @@ -1616,15 +1610,10 @@ void SkPDFDevice::internalDrawImageRect(SkKeyedImage imageSubset, return; } - SkKeyedImage originalImage = imageSubset; - bool canUseOriginal = true; - bool didSubset = false; - // First, figure out the src->dst transform and subset the image if needed. SkIRect bounds = imageSubset.image()->bounds(); SkRect srcRect = src ? *src : SkRect::Make(bounds); - SkMatrix transform = SkMatrix::RectToRectOrIdentity(srcRect, dst); - SkMatrix originalTransform = transform; + SkMatrix transform = SkMatrix::RectToRect(srcRect, dst); if (src && *src != SkRect::Make(bounds)) { if (!srcRect.intersect(SkRect::Make(bounds))) { return; @@ -1634,7 +1623,6 @@ void SkPDFDevice::internalDrawImageRect(SkKeyedImage imageSubset, SkIntToScalar(bounds.y())); if (bounds != imageSubset.image()->bounds()) { imageSubset = imageSubset.subset(bounds); - didSubset = true; } if (!imageSubset) { return; @@ -1669,7 +1657,6 @@ void SkPDFDevice::internalDrawImageRect(SkKeyedImage imageSubset, paint.writable()->setShader(nullptr); } imageSubset = SkKeyedImage(surface->makeImageSnapshot()); - canUseOriginal = false; SkASSERT(!imageSubset.image()->isAlphaOnly()); } @@ -1727,7 +1714,15 @@ void SkPDFDevice::internalDrawImageRect(SkKeyedImage imageSubset, return; } transform.postConcat(ctm); - originalTransform.postConcat(ctm); + + bool needToRestore = false; + if (src && !is_integral(*src)) { + // Need sub-pixel clipping to fix https://bug.skia.org/4374 + this->cs().save(); + this->cs().clipRect(dst, ctm, SkClipOp::kIntersect, true); + needToRestore = true; + } + SK_AT_SCOPE_EXIT(if (needToRestore) { this->cs().restore(); }); SkMatrix matrix = transform; @@ -1783,43 +1778,11 @@ void SkPDFDevice::internalDrawImageRect(SkKeyedImage imageSubset, matrix.postTranslate(deltaX, deltaY); imageSubset = SkKeyedImage(surface->makeImageSnapshot()); - canUseOriginal = false; if (!imageSubset) { return; } } - if (SkColorFilter* colorFilter = paint->getColorFilter()) { - sk_sp<SkImage> img = color_filter(imageSubset.image().get(), colorFilter); - imageSubset = SkKeyedImage(std::move(img)); - canUseOriginal = false; - if (!imageSubset) { - return; - } - // TODO(halcanary): de-dupe this by caching filtered images. - // (maybe in the resource cache?) - } - - bool useCroppedOriginal = false; - if (didSubset && canUseOriginal) { - size_t originalSize = SkPDFSerializeImageSize( - originalImage.image().get(), fDocument, fDocument->metadata().fEncodingQuality); - size_t subsetSize = SkPDFSerializeImageSize( - imageSubset.image().get(), fDocument, fDocument->metadata().fEncodingQuality); - if (originalSize <= subsetSize) { - matrix = originalTransform; - imageSubset = originalImage; - useCroppedOriginal = true; - } - } - - // Need sub-pixel clipping to fix skbug.com/40035524 - SkClipStack::AutoRestore ar(&this->cs(), false); - if ((src && !is_integral(*src)) || useCroppedOriginal) { - this->cs().save(); - this->cs().clipRect(dst, ctm, SkClipOp::kIntersect, true); - } - SkMatrix scaled; // Adjust for origin flip. scaled.setScale(SK_Scalar1, -SK_Scalar1); @@ -1841,6 +1804,16 @@ void SkPDFDevice::internalDrawImageRect(SkKeyedImage imageSubset, return; } + if (SkColorFilter* colorFilter = paint->getColorFilter()) { + sk_sp<SkImage> img = color_filter(imageSubset.image().get(), colorFilter); + imageSubset = SkKeyedImage(std::move(img)); + if (!imageSubset) { + return; + } + // TODO(halcanary): de-dupe this by caching filtered images. + // (maybe in the resource cache?) + } + SkBitmapKey key = imageSubset.key(); SkPDFIndirectReference* pdfimagePtr = fDocument->fPDFBitmapMap.find(key); SkPDFIndirectReference pdfimage = pdfimagePtr ? *pdfimagePtr : SkPDFIndirectReference(); @@ -1884,8 +1857,8 @@ void SkPDFDevice::drawDevice(SkDevice* device, const SkSamplingOptions& sampling if (!content) { return; } - SkPath shape = SkPath::Rect(SkRect::Make(device->imageInfo().dimensions())) - .makeTransform(matrix); + SkPath shape = SkPath::Rect(SkRect::Make(device->imageInfo().dimensions())); + shape.transform(matrix); if (content.needShape()) { content.setShape(shape); } diff --git a/gfx/skia/skia/src/pdf/SkPDFDevice.h b/gfx/skia/skia/src/pdf/SkPDFDevice.h @@ -8,13 +8,11 @@ #ifndef SkPDFDevice_DEFINED #define SkPDFDevice_DEFINED -#include "include/core/SkCPURecorder.h" #include "include/core/SkCanvas.h" #include "include/core/SkMatrix.h" #include "include/core/SkRefCnt.h" #include "include/core/SkSamplingOptions.h" #include "include/core/SkScalar.h" -#include "include/core/SkSpan.h" #include "include/core/SkStream.h" #include "src/core/SkClipStack.h" #include "src/core/SkClipStackDevice.h" @@ -24,6 +22,7 @@ #include "src/pdf/SkPDFTag.h" #include "src/pdf/SkPDFTypes.h" +#include <cstddef> #include <memory> class SkBitmap; @@ -32,10 +31,9 @@ class SkData; class SkDevice; class SkImage; class SkMesh; +class SkPDFDocument; class SkPaint; class SkPath; -class SkPDFDocument; -class SkRecorder; class SkRRect; class SkSpecialImage; class SkSurface; @@ -86,7 +84,9 @@ public: * operations, and are handling any looping from the paint. */ void drawPaint(const SkPaint& paint) override; - void drawPoints(SkCanvas::PointMode, SkSpan<const SkPoint>, const SkPaint&) override; + void drawPoints(SkCanvas::PointMode mode, + size_t count, const SkPoint[], + const SkPaint& paint) override; void drawRect(const SkRect& r, const SkPaint& paint) override; void drawOval(const SkRect& oval, const SkPaint& paint) override; void drawRRect(const SkRRect& rr, const SkPaint& paint) override; @@ -124,11 +124,6 @@ public: const SkMatrix& initialTransform() const { return fInitialTransform; } - SkRecorder* baseRecorder() const override { - // TODO(kjlubick) the creation of this should likely involve a CPU context. - return skcpu::Recorder::TODO(); - } - private: // TODO(vandebo): push most of SkPDFDevice's state into a core object in // order to get the right access levels without using friend. diff --git a/gfx/skia/skia/src/pdf/SkPDFDocument.cpp b/gfx/skia/skia/src/pdf/SkPDFDocument.cpp @@ -57,7 +57,7 @@ const char* SkPDFGetElemIdKey() { } static SkString ToValidUtf8String(const SkData& d) { - if (d.empty()) { + if (d.size() == 0) { SkDEBUGFAIL("Not a valid string, data length is zero."); return SkString(); } @@ -184,7 +184,7 @@ static SkPDFIndirectReference generate_page_tree( std::vector<PageTreeNode> result; static constexpr size_t kMaxNodeSize = 8; const size_t n = vec.size(); - SkASSERT(!vec.empty()); + SkASSERT(n >= 1); const size_t result_len = (n - 1) / kMaxNodeSize + 1; SkASSERT(result_len >= 1); SkASSERT(n == 1 || result_len < n); diff --git a/gfx/skia/skia/src/pdf/SkPDFDocumentPriv.h b/gfx/skia/skia/src/pdf/SkPDFDocumentPriv.h @@ -149,7 +149,6 @@ public: std::unique_ptr<SkPDFArray> getAnnotations(); - // Every reference returned by this method must be passed to `emit` exactly once. SkPDFIndirectReference reserveRef() { return SkPDFIndirectReference{fNextObjectNumber++}; } // Returns a tag to prepend to a PostScript name of a subset font. Includes the '+'. diff --git a/gfx/skia/skia/src/pdf/SkPDFFont.cpp b/gfx/skia/skia/src/pdf/SkPDFFont.cpp @@ -31,7 +31,6 @@ #include "include/core/SkRefCnt.h" #include "include/core/SkScalar.h" #include "include/core/SkSize.h" -#include "include/core/SkSpan.h" #include "include/core/SkStream.h" #include "include/core/SkString.h" #include "include/core/SkTypeface.h" @@ -67,7 +66,6 @@ #include <cstddef> #include <initializer_list> #include <memory> -#include <optional> #include <utility> using namespace skia_private; @@ -123,13 +121,19 @@ static bool scale_paint(SkPaint& paint, SkScalar fontToEMScale) { } } if (SkPathEffectBase* peb = as_PEB(paint.getPathEffect())) { - if (auto dashInfo = peb->asADash()) { - SkSpan<const SkScalar> src = dashInfo->fIntervals; - AutoSTArray<4, SkScalar> dst(src.size()); - for (size_t i = 0; i < src.size(); ++i) { - dst[i] = src[i] * fontToEMScale; + AutoSTMalloc<4, SkScalar> intervals; + SkPathEffectBase::DashInfo dashInfo(intervals, 4, 0); + if (peb->asADash(&dashInfo) == SkPathEffectBase::DashType::kDash) { + if (dashInfo.fCount > 4) { + intervals.realloc(dashInfo.fCount); + peb->asADash(&dashInfo); } - paint.setPathEffect(SkDashPathEffect::Make(dst, dashInfo->fPhase * fontToEMScale)); + for (int32_t i = 0; i < dashInfo.fCount; ++i) { + dashInfo.fIntervals[i] *= fontToEMScale; + } + dashInfo.fPhase *= fontToEMScale; + paint.setPathEffect( + SkDashPathEffect::Make(dashInfo.fIntervals, dashInfo.fCount, dashInfo.fPhase)); } else { return false; } @@ -279,7 +283,7 @@ const SkAdvancedTypefaceMetrics* SkPDFFont::GetMetrics(const SkTypeface& typefac for (char c : {'i', 'I', '!', '1'}) { SkGlyphID g = font.unicharToGlyph(c); SkRect bounds; - font.getBounds({&g, 1}, {&bounds, 1}, nullptr); + font.getBounds(&g, 1, &bounds, nullptr); stemV = std::min(stemV, SkToS16(SkScalarRoundToInt(bounds.width()))); } metrics->fStemV = stemV; @@ -290,7 +294,7 @@ const SkAdvancedTypefaceMetrics* SkPDFFont::GetMetrics(const SkTypeface& typefac for (char c : {'M', 'X'}) { SkGlyphID g = font.unicharToGlyph(c); SkRect bounds; - font.getBounds({&g, 1}, {&bounds, 1}, nullptr); + font.getBounds(&g, 1, &bounds, nullptr); capHeight += bounds.height(); } metrics->fCapHeight = SkToS16(SkScalarRoundToInt(capHeight / 2)); @@ -309,7 +313,7 @@ const std::vector<SkUnichar>& SkPDFFont::GetUnicodeMap(const SkTypeface& typefac return *ptr; } std::vector<SkUnichar> buffer(typeface.countGlyphs()); - typeface.getGlyphToUnicodeMap(buffer); + typeface.getGlyphToUnicodeMap(buffer.data()); return *canon->fToUnicodeMap.set(id, std::move(buffer)); } @@ -797,7 +801,7 @@ static void emit_subset_type3(const SkPDFFont& pdfFont, SkPDFDocument* doc) { content.writeText("/X"); content.write(characterName.c_str(), characterName.size()); content.writeText(" Do\n"); - SkPDFIndirectReference image = SkPDFSerializeImage(pimg.fImage.get(), doc, 101); + SkPDFIndirectReference image = SkPDFSerializeImage(pimg.fImage.get(), doc); xobjects->insertRef(SkStringPrintf("Xg%X", gID), image); } else { // TODO: For A1, put ImageMask on the PDF image and draw the image? diff --git a/gfx/skia/skia/src/pdf/SkPDFGradientShader.cpp b/gfx/skia/skia/src/pdf/SkPDFGradientShader.cpp @@ -7,13 +7,11 @@ #include "src/pdf/SkPDFGradientShader.h" -#include "include/core/SkAlphaType.h" #include "include/core/SkPaint.h" #include "include/core/SkPathTypes.h" #include "include/core/SkSpan.h" #include "include/core/SkStream.h" #include "include/core/SkTileMode.h" -#include "include/effects/SkGradientShader.h" #include "include/private/base/SkTemplates.h" #include "include/private/base/SkTo.h" #include "src/core/SkChecksum.h" @@ -67,47 +65,48 @@ static void unit_to_points_matrix(const SkPoint pts[2], SkMatrix* matrix) { matrix->postTranslate(pts[0].fX, pts[0].fY); } -static bool is_premul(const SkShaderBase::GradientInfo& info) { - return SkToBool(info.fGradientFlags & SkGradientShader::kInterpolateColorsInPremul_Flag); -} - -enum NumComponents { - Three = 3, - Four = 4, - Max = Four, -}; +static const int kColorComponents = 3; +typedef uint8_t ColorTuple[kColorComponents]; /* Assumes t - startOffset is on the stack and does a linear interpolation on t between startOffset and endOffset from prevColor to curColor (for each color - component), leaving the result in component order on the stack. - @param range endOffset - startOffset - @param beginColor The previous color. - @param endColor The current color. - @param numComponents The number of components (3 or 4 if alpha is needed). - @param result The result ps function. + component), leaving the result in component order on the stack. It assumes + there are always 3 components per color. + @param range endOffset - startOffset + @param beginColor The previous color. + @param endColor The current color. + @param result The result ps function. */ -static void interpolate_color_code(SkScalar range, NumComponents numComponents, - SkColor4f prevColor, SkColor4f curColor, +static void interpolate_color_code(SkScalar range, SkColor beginColor, SkColor endColor, SkDynamicMemoryWStream* result) { SkASSERT(range != SkIntToScalar(0)); /* Linearly interpolate from the previous color to the current. - Take the components 0..1 and determine the multipliers for interpolation. + Scale the colors from 0..255 to 0..1 and determine the multipliers for interpolation. C{r,g,b}(t, section) = t - offset_(section-1) + t * Multiplier{r,g,b}. */ + ColorTuple curColor = { SkTo<uint8_t>(SkColorGetR(endColor)), + SkTo<uint8_t>(SkColorGetG(endColor)), + SkTo<uint8_t>(SkColorGetB(endColor)) }; + + ColorTuple prevColor = { SkTo<uint8_t>(SkColorGetR(beginColor)), + SkTo<uint8_t>(SkColorGetG(beginColor)), + SkTo<uint8_t>(SkColorGetB(beginColor)) }; + // Figure out how to scale each color component. - SkScalar multiplier[NumComponents::Max]; - for (int i = 0; i < numComponents; i++) { - multiplier[i] = (curColor[i] - prevColor[i]) / range; + SkScalar multiplier[kColorComponents]; + for (int i = 0; i < kColorComponents; i++) { + static const SkScalar kColorScale = SkScalarInvert(255); + multiplier[i] = kColorScale * (curColor[i] - prevColor[i]) / range; } // Calculate when we no longer need to keep a copy of the input parameter t. // If the last component to use t is i, then dupInput[0..i - 1] = true // and dupInput[i .. components] = false. - bool dupInput[NumComponents::Max]; - dupInput[numComponents - 1] = false; - for (int i = numComponents - 2; i >= 0; i--) { + bool dupInput[kColorComponents]; + dupInput[kColorComponents - 1] = false; + for (int i = kColorComponents - 2; i >= 0; i--) { dupInput[i] = dupInput[i + 1] || multiplier[i + 1] != 0; } @@ -115,7 +114,7 @@ static void interpolate_color_code(SkScalar range, NumComponents numComponents, result->writeText("pop "); } - for (int i = 0; i < numComponents; i++) { + for (int i = 0; i < kColorComponents; i++) { // If the next components needs t and this component will consume a // copy, make another copy. if (dupInput[i] && multiplier[i] != 0) { @@ -123,7 +122,7 @@ static void interpolate_color_code(SkScalar range, NumComponents numComponents, } if (multiplier[i] == 0) { - SkPDFUtils::AppendColorComponentF(prevColor[i], result); + SkPDFUtils::AppendColorComponent(prevColor[i], result); result->writeText(" "); } else { if (multiplier[i] != 1) { @@ -131,7 +130,7 @@ static void interpolate_color_code(SkScalar range, NumComponents numComponents, result->writeText(" mul "); } if (prevColor[i] != 0) { - SkPDFUtils::AppendColorComponentF(prevColor[i], result); + SkPDFUtils::AppendColorComponent(prevColor[i], result); result->writeText(" add "); } } @@ -142,28 +141,7 @@ static void interpolate_color_code(SkScalar range, NumComponents numComponents, } } -// Convert { r, g, b, a } to a == 0 ? {0 0 0} : { r/a, g/a, b/a } -static void unpremul(const SkShaderBase::GradientInfo& info, SkDynamicMemoryWStream* function) { - // Preview Version 11.0 (1069.7.1) aborts the function if the predicate is like - // "dup 0 eq" or any other use of "eq" with "a". - function->writeText("dup abs 0.00001 lt" - "{ pop pop pop pop 0 0 0 }" - "{" - " dup" // r g b a a - " 3 1 roll" // r g a b a - " div" // r g a b/a - " 4 1 roll" // b/a r g a - " dup" // b/a r g a a - " 3 1 roll" // b/a r a g a - " div" // b/a r a g/a - " 4 1 roll" // g/a b/a r a - " div" // g/a b/a r/a - " 3 1 roll" // r/a g/a b/a - "} ifelse\n"); -} - static void write_gradient_ranges(const SkShaderBase::GradientInfo& info, SkSpan<size_t> rangeEnds, - NumComponents numComponents, bool top, bool first, SkDynamicMemoryWStream* result) { SkASSERT(!rangeEnds.empty()); @@ -193,16 +171,16 @@ static void write_gradient_ranges(const SkShaderBase::GradientInfo& info, SkSpan SkScalar rangeBegin = info.fColorOffsets[rangeBeginIndex]; SkPDFUtils::AppendScalar(rangeBegin, result); result->writeText(" sub "); // consume t, put t - startOffset on the stack. - interpolate_color_code(rangeEnd - rangeBegin, numComponents, + interpolate_color_code(rangeEnd - rangeBegin, info.fColors[rangeBeginIndex], info.fColors[rangeEndIndex], result); result->writeText("\n"); } else { size_t loCount = rangeEnds.size() / 2; SkSpan<size_t> loSpan = rangeEnds.subspan(0, loCount); - write_gradient_ranges(info, loSpan, numComponents, false, true, result); + write_gradient_ranges(info, loSpan, false, true, result); SkSpan<size_t> hiSpan = rangeEnds.subspan(loCount, rangeEnds.size() - loCount); - write_gradient_ranges(info, hiSpan, numComponents, false, false, result); + write_gradient_ranges(info, hiSpan, false, false, result); } if (top) { @@ -271,21 +249,14 @@ static void gradient_function_code(const SkShaderBase::GradientInfo& info, // After finding a hit the stack is [r g b 0]. // The 0 is consumed just before returning. - const bool premul = is_premul(info); - NumComponents numComponents = premul ? NumComponents::Four : NumComponents::Three; - // The initial range has no previous and contains a solid color. // Any t <= 0 will be handled by this initial range, so later t == 0 indicates a hit was found. result->writeText("dup 0 le {pop "); - SkPDFUtils::AppendColorComponentF(info.fColors[0].fR, result); + SkPDFUtils::AppendColorComponent(SkColorGetR(info.fColors[0]), result); result->writeText(" "); - SkPDFUtils::AppendColorComponentF(info.fColors[0].fG, result); + SkPDFUtils::AppendColorComponent(SkColorGetG(info.fColors[0]), result); result->writeText(" "); - SkPDFUtils::AppendColorComponentF(info.fColors[0].fB, result); - if (numComponents == NumComponents::Four) { - result->writeText(" "); - SkPDFUtils::AppendColorComponentF(info.fColors[0].fA, result); - } + SkPDFUtils::AppendColorComponent(SkColorGetB(info.fColors[0]), result); result->writeText(" 0} if\n"); // Optimize out ranges which don't make any visual difference. @@ -294,12 +265,8 @@ static void gradient_function_code(const SkShaderBase::GradientInfo& info, for (int i = 1; i < info.fColorCount; ++i) { // Ignoring the alpha, is this range the same solid color as the next range? // This optimizes gradients where sometimes only the color or only the alpha is changing. - auto eqIgnoringAlpha = [&](SkColor4f a, SkColor4f b) { - if (premul) { - return a == b; - } else { - return a.makeOpaque() == b.makeOpaque(); - } + auto eqIgnoringAlpha = [](SkColor a, SkColor b) { + return SkColorSetA(a, 0x00) == SkColorSetA(b, 0x00); }; bool constantColorBothSides = eqIgnoringAlpha(info.fColors[i-1], info.fColors[i]) &&// This range is a solid color. @@ -316,41 +283,32 @@ static void gradient_function_code(const SkShaderBase::GradientInfo& info, } // If a cap on depth is needed, loop here. - write_gradient_ranges(info, SkSpan(rangeEnds.get(), rangeEndsCount), - numComponents, true, true, result); + write_gradient_ranges(info, SkSpan(rangeEnds.get(), rangeEndsCount), true, true, result); // Clamp the final color. result->writeText("0 gt {"); - SkPDFUtils::AppendColorComponentF(info.fColors[info.fColorCount - 1].fR, result); + SkPDFUtils::AppendColorComponent(SkColorGetR(info.fColors[info.fColorCount - 1]), result); result->writeText(" "); - SkPDFUtils::AppendColorComponentF(info.fColors[info.fColorCount - 1].fG, result); + SkPDFUtils::AppendColorComponent(SkColorGetG(info.fColors[info.fColorCount - 1]), result); result->writeText(" "); - SkPDFUtils::AppendColorComponentF(info.fColors[info.fColorCount - 1].fB, result); - if (numComponents == NumComponents::Four) { - result->writeText(" "); - SkPDFUtils::AppendColorComponentF(info.fColors[info.fColorCount - 1].fA, result); - } + SkPDFUtils::AppendColorComponent(SkColorGetB(info.fColors[info.fColorCount - 1]), result); result->writeText("} if\n"); - - if (premul) { - unpremul(info, result); - } } -static std::unique_ptr<SkPDFDict> createInterpolationFunction(const SkColor4f& color1, - const SkColor4f& color2) { +static std::unique_ptr<SkPDFDict> createInterpolationFunction(const ColorTuple& color1, + const ColorTuple& color2) { auto retval = SkPDFMakeDict(); auto c0 = SkPDFMakeArray(); - c0->appendColorComponentF(color1.fR); - c0->appendColorComponentF(color1.fG); - c0->appendColorComponentF(color1.fB); + c0->appendColorComponent(color1[0]); + c0->appendColorComponent(color1[1]); + c0->appendColorComponent(color1[2]); retval->insertObject("C0", std::move(c0)); auto c1 = SkPDFMakeArray(); - c1->appendColorComponentF(color2.fR); - c1->appendColorComponentF(color2.fG); - c1->appendColorComponentF(color2.fB); + c1->appendColorComponent(color2[0]); + c1->appendColorComponent(color2[1]); + c1->appendColorComponent(color2[2]); retval->insertObject("C1", std::move(c1)); retval->insertObject("Domain", SkPDFMakeArray(0, 1)); @@ -366,7 +324,7 @@ static std::unique_ptr<SkPDFDict> gradientStitchCode(const SkShaderBase::Gradien // normalize color stops int colorCount = info.fColorCount; - std::vector<SkColor4f> colors(info.fColors, info.fColors + colorCount); + std::vector<SkColor> colors(info.fColors, info.fColors + colorCount); std::vector<SkScalar> colorOffsets(info.fColorOffsets, info.fColorOffsets + colorCount); int i = 1; @@ -396,11 +354,18 @@ static std::unique_ptr<SkPDFDict> gradientStitchCode(const SkShaderBase::Gradien colorOffsets[i - 1] -= 0.00001f; } - // no need for a stitch function if there are only 2 stops. - if (colorCount == 2) { - return createInterpolationFunction(colors[0], colors[1]); + AutoSTMalloc<4, ColorTuple> colorDataAlloc(colorCount); + ColorTuple *colorData = colorDataAlloc.get(); + for (int idx = 0; idx < colorCount; idx++) { + colorData[idx][0] = SkColorGetR(colors[idx]); + colorData[idx][1] = SkColorGetG(colors[idx]); + colorData[idx][2] = SkColorGetB(colors[idx]); } + // no need for a stitch function if there are only 2 stops. + if (colorCount == 2) + return createInterpolationFunction(colorData[0], colorData[1]); + auto encode = SkPDFMakeArray(); auto bounds = SkPDFMakeArray(); auto functions = SkPDFMakeArray(); @@ -416,7 +381,7 @@ static std::unique_ptr<SkPDFDict> gradientStitchCode(const SkShaderBase::Gradien encode->appendScalar(0); encode->appendScalar(1.0f); - functions->appendObject(createInterpolationFunction(colors[idx-1], colors[idx])); + functions->appendObject(createInterpolationFunction(colorData[idx-1], colorData[idx])); } retval->insertObject("Encode", std::move(encode)); @@ -747,8 +712,7 @@ static SkPDFIndirectReference make_function_shader(SkPDFDocument* doc, state.fType == SkShaderBase::GradientType::kConical) && (info.fTileMode == SkTileMode::kClamp || info.fTileMode == SkTileMode::kDecal) && - !finalMatrix.hasPerspective() && - !is_premul(info); + !finalMatrix.hasPerspective(); enum class ShadingType : int32_t { Function = 1, @@ -873,14 +837,14 @@ static SkPDFIndirectReference make_function_shader(SkPDFDocument* doc, // The two point radial gradient further references state.fInfo // in translating from x, y coordinates to the t parameter. So, we have // to transform the points and radii according to the calculated matrix. - auto inverseMapperMatrix = mapperMatrix.invert(); - if (!inverseMapperMatrix) { + SkShaderBase::GradientInfo infoCopy = info; + SkMatrix inverseMapperMatrix; + if (!mapperMatrix.invert(&inverseMapperMatrix)) { return SkPDFIndirectReference(); } - SkShaderBase::GradientInfo infoCopy = info; - inverseMapperMatrix->mapPoints(infoCopy.fPoint); - infoCopy.fRadius[0] = inverseMapperMatrix->mapRadius(info.fRadius[0]); - infoCopy.fRadius[1] = inverseMapperMatrix->mapRadius(info.fRadius[1]); + inverseMapperMatrix.mapPoints(infoCopy.fPoint, 2); + infoCopy.fRadius[0] = inverseMapperMatrix.mapRadius(info.fRadius[0]); + infoCopy.fRadius[1] = inverseMapperMatrix.mapRadius(info.fRadius[1]); twoPointConicalCode(infoCopy, perspectiveInverseOnly, &functionCode); } break; case SkShaderBase::GradientType::kSweep: @@ -911,7 +875,7 @@ static SkPDFIndirectReference make_function_shader(SkPDFDocument* doc, static SkPDFIndirectReference find_pdf_shader(SkPDFDocument* doc, SkPDFGradientShader::Key key, - bool makeAlphaShader); + bool keyHasAlpha); static std::unique_ptr<SkPDFDict> get_gradient_resource_dict(SkPDFIndirectReference functionShader, SkPDFIndirectReference gState) { @@ -948,7 +912,7 @@ static std::unique_ptr<SkStreamAsset> create_pattern_fill_content(int gsIndex, static bool gradient_has_alpha(const SkPDFGradientShader::Key& key) { SkASSERT(key.fType != SkShaderBase::GradientType::kNone); for (int i = 0; i < key.fInfo.fColorCount; i++) { - if (!key.fInfo.fColors[i].isOpaque()) { + if ((SkAlpha)SkColorGetA(key.fInfo.fColors[i]) != SK_AlphaOPAQUE) { return true; } } @@ -960,7 +924,7 @@ static SkPDFGradientShader::Key clone_key(const SkPDFGradientShader::Key& k) { SkPDFGradientShader::Key clone = { k.fType, k.fInfo, // change pointers later. - std::unique_ptr<SkColor4f[]>(new SkColor4f[k.fInfo.fColorCount]), + std::unique_ptr<SkColor[]>(new SkColor[k.fInfo.fColorCount]), std::unique_ptr<SkScalar[]>(new SkScalar[k.fInfo.fColorCount]), k.fCanvasTransform, k.fShaderTransform, @@ -979,10 +943,9 @@ static SkPDFIndirectReference create_smask_graphic_state(SkPDFDocument* doc, SkASSERT(state.fType != SkShaderBase::GradientType::kNone); SkPDFGradientShader::Key luminosityState = clone_key(state); for (int i = 0; i < luminosityState.fInfo.fColorCount; i++) { - float alpha = luminosityState.fInfo.fColors[i].fA; - luminosityState.fInfo.fColors[i] = SkColor4f{alpha, alpha, alpha, 1.0f}; + SkAlpha alpha = SkColorGetA(luminosityState.fInfo.fColors[i]); + luminosityState.fInfo.fColors[i] = SkColorSetARGB(255, alpha, alpha, alpha); } - luminosityState.fInfo.fGradientFlags &= ~SkGradientShader::kInterpolateColorsInPremul_Flag; luminosityState.fHash = hash(luminosityState); SkASSERT(!gradient_has_alpha(luminosityState)); @@ -1005,15 +968,12 @@ static SkPDFIndirectReference make_alpha_function_shader(SkPDFDocument* doc, const SkPDFGradientShader::Key& state) { SkASSERT(state.fType != SkShaderBase::GradientType::kNone); SkPDFGradientShader::Key opaqueState = clone_key(state); - const bool keepAlpha = is_premul(opaqueState.fInfo); - if (!keepAlpha) { - for (int i = 0; i < opaqueState.fInfo.fColorCount; i++) { - opaqueState.fInfo.fColors[i].fA = 1.0f; - } - opaqueState.fHash = hash(opaqueState); - - SkASSERT(!gradient_has_alpha(opaqueState)); + for (int i = 0; i < opaqueState.fInfo.fColorCount; i++) { + opaqueState.fInfo.fColors[i] = SkColorSetA(opaqueState.fInfo.fColors[i], SK_AlphaOPAQUE); } + opaqueState.fHash = hash(opaqueState); + + SkASSERT(!gradient_has_alpha(opaqueState)); SkRect bbox = SkRect::Make(state.fBBox); SkPDFIndirectReference colorShader = find_pdf_shader(doc, std::move(opaqueState), false); if (!colorShader) { @@ -1047,37 +1007,25 @@ static SkPDFGradientShader::Key make_key(const SkShader* shader, key.fType = as_SB(shader)->asGradient(&key.fInfo); SkASSERT(SkShaderBase::GradientType::kNone != key.fType); SkASSERT(key.fInfo.fColorCount > 0); - key.fColors = std::make_unique<SkColor4f[]>(key.fInfo.fColorCount); - key.fStops = std::make_unique<SkScalar[]>(key.fInfo.fColorCount); + key.fColors.reset(new SkColor[key.fInfo.fColorCount]); + key.fStops.reset(new SkScalar[key.fInfo.fColorCount]); key.fInfo.fColors = key.fColors.get(); key.fInfo.fColorOffsets = key.fStops.get(); as_SB(shader)->asGradient(&key.fInfo); - if (is_premul(key.fInfo)) { - bool changedByPremul = false; - for (auto&& c : SkSpan(key.fInfo.fColors, key.fInfo.fColorCount)) { - if (c.fA != 1.0) { - changedByPremul = true; - } - SkRGBA4f<kPremul_SkAlphaType> pm = c.premul(); - c = SkColor4f{pm.fR, pm.fG, pm.fB, pm.fA}; - } - if (!changedByPremul) { - key.fInfo.fGradientFlags &= ~SkGradientShader::kInterpolateColorsInPremul_Flag; - } - } key.fHash = hash(key); return key; } static SkPDFIndirectReference find_pdf_shader(SkPDFDocument* doc, SkPDFGradientShader::Key key, - bool makeAlphaShader) { + bool keyHasAlpha) { + SkASSERT(gradient_has_alpha(key) == keyHasAlpha); auto& gradientPatternMap = doc->fGradientPatternMap; if (SkPDFIndirectReference* ptr = gradientPatternMap.find(key)) { return *ptr; } SkPDFIndirectReference pdfShader; - if (makeAlphaShader) { + if (keyHasAlpha) { pdfShader = make_alpha_function_shader(doc, key); } else { pdfShader = make_function_shader(doc, key); @@ -1087,12 +1035,12 @@ static SkPDFIndirectReference find_pdf_shader(SkPDFDocument* doc, } SkPDFIndirectReference SkPDFGradientShader::Make(SkPDFDocument* doc, - SkShader* shader, - const SkMatrix& canvasTransform, - const SkIRect& bbox) { + SkShader* shader, + const SkMatrix& canvasTransform, + const SkIRect& bbox) { SkASSERT(shader); SkASSERT(as_SB(shader)->asGradient() != SkShaderBase::GradientType::kNone); SkPDFGradientShader::Key key = make_key(shader, canvasTransform, bbox); - const bool makeAlphaShader = gradient_has_alpha(key); - return find_pdf_shader(doc, std::move(key), makeAlphaShader); + bool alpha = gradient_has_alpha(key); + return find_pdf_shader(doc, std::move(key), alpha); } diff --git a/gfx/skia/skia/src/pdf/SkPDFGradientShader.h b/gfx/skia/skia/src/pdf/SkPDFGradientShader.h @@ -33,7 +33,7 @@ SkPDFIndirectReference Make(SkPDFDocument* doc, struct Key { SkShaderBase::GradientType fType; SkShaderBase::GradientInfo fInfo; - std::unique_ptr<SkColor4f[]> fColors; + std::unique_ptr<SkColor[]> fColors; std::unique_ptr<SkScalar[]> fStops; SkMatrix fCanvasTransform; SkMatrix fShaderTransform; diff --git a/gfx/skia/skia/src/pdf/SkPDFGraphicStackState.cpp b/gfx/skia/skia/src/pdf/SkPDFGraphicStackState.cpp @@ -75,7 +75,8 @@ static void apply_clip(const SkClipStack& stack, const SkRect& outerBounds, F fn SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart); SkRect bounds = outerBounds; while (const SkClipStack::Element* element = iter.next()) { - SkPath operand = element->asDeviceSpacePath(); + SkPath operand; + element->asDeviceSpacePath(&operand); SkPathOp op; switch (element->getOp()) { case SkClipOp::kDifference: op = kDifference_SkPathOp; break; @@ -85,9 +86,7 @@ static void apply_clip(const SkClipStack& stack, const SkRect& outerBounds, F fn operand.isInverseFillType() || !kHuge.contains(operand.getBounds())) { - if (auto result = Op(SkPath::Rect(bounds), operand, op)) { - operand = *result; - } + Op(SkPath::Rect(bounds), operand, op, &operand); } SkASSERT(!operand.isInverseFillType()); fn(operand); @@ -125,9 +124,10 @@ static void append_clip(const SkClipStack& clipStack, } if (is_complex_clip(clipStack)) { - if (auto clipPath = Op(SkClipStack_AsPath(clipStack), SkPath::Rect(outsetBounds), - kIntersect_SkPathOp)) { - append_clip_path(*clipPath, wStream); + SkPath clipPath; + SkClipStack_AsPath(clipStack, &clipPath); + if (Op(clipPath, SkPath::Rect(outsetBounds), kIntersect_SkPathOp, &clipPath)) { + append_clip_path(clipPath, wStream); } // If Op() fails (pathological case; e.g. input values are // extremely large or NaN), emit no clip at all. diff --git a/gfx/skia/skia/src/pdf/SkPDFMakeToUnicodeCmap.cpp b/gfx/skia/skia/src/pdf/SkPDFMakeToUnicodeCmap.cpp @@ -225,9 +225,7 @@ void SkPDFAppendCmapSections(const SkUnichar* glyphToUnicode, for (int i = firstGlyphID - glyphOffset; i < limit + 1; ++i) { SkGlyphID gid = i + glyphOffset; - bool inSubset = i < limit && - (subset == nullptr || subset->has(gid)) && - !glyphToUnicodeEx.find(gid); + bool inSubset = i < limit && (subset == nullptr || subset->has(gid)); if (!rangeEmpty) { // PDF spec requires bfrange not changing the higher byte, // e.g. <1035> <10FF> <2222> is ok, but @@ -257,7 +255,8 @@ void SkPDFAppendCmapSections(const SkUnichar* glyphToUnicode, } } - // The spec requires all bfchar entries for a font must come before bfrange entries. + // The spec requires all bfchar entries for a font must come before bfrange + // entries. append_bfchar_section(bfcharEntries, multiByteGlyphs, cmap); append_bfchar_section_ex(glyphToUnicodeEx, multiByteGlyphs, firstGlyphID, lastGlyphID, cmap); append_bfrange_section(bfrangeEntries, multiByteGlyphs, cmap); diff --git a/gfx/skia/skia/src/pdf/SkPDFTag.cpp b/gfx/skia/skia/src/pdf/SkPDFTag.cpp @@ -501,18 +501,11 @@ struct Entry { Content fContent; int fHeaderLevel; + SkPDFIndirectReference fRef; SkPDFIndirectReference fStructureRef; - SkPDFIndirectReference fRef = {}; std::vector<Entry> fChildren = {}; size_t fDescendentsEmitted = 0; - void setAllRefs(SkPDFDocument* const doc, SkPDFIndirectReference ref) { - fRef = ref; - for (auto&& child : fChildren) { - child.setAllRefs(doc, doc->reserveRef()); - } - } - void emitDescendents(SkPDFDocument* const doc) { fDescendentsEmitted = fChildren.size(); for (size_t i = 0; i < fChildren.size(); ++i) { @@ -585,7 +578,7 @@ void make(SkPDFDocument* const doc, SkPDFStructElem* const structElem, STArray<7 } Entry::Content content = create_header_content(structElem); if (!content.fText.isEmpty()) { - Entry e{std::move(content), level, structElem->fRef}; + Entry e{std::move(content), level, doc->reserveRef(), structElem->fRef}; stack.push_back(&stack.back()->fChildren.emplace_back(std::move(e))); return; } @@ -703,10 +696,9 @@ SkPDFIndirectReference SkPDFStructTree::makeOutline(SkPDFDocument* doc) const { return SkPDFIndirectReference(); } - SkPDFIndirectReference outlineRef; + SkPDFIndirectReference outlineRef = doc->reserveRef(); SkPDFDict outline("Outlines"); if (fOutline == SkPDF::Metadata::Outline::StructureElements) { - outlineRef = doc->reserveRef(); SkPDFIndirectReference entryRef = doc->reserveRef(); SkPDFIndirectReference none; structelem_outline::Entry entry = structelem_outline::emit(doc, fRoot, outlineRef, @@ -716,14 +708,12 @@ SkPDFIndirectReference SkPDFStructTree::makeOutline(SkPDFDocument* doc) const { outline.insertInt("Count", entry.fDescendantCount); } else { STArray<7, header_outline::Entry*> stack; - header_outline::Entry top{{SkString(), Location()}, 0, {}}; + header_outline::Entry top{{SkString(), Location()}, 0, outlineRef, {}}; stack.push_back(&top); header_outline::make(doc, fRoot, stack); if (top.fChildren.empty()) { return SkPDFIndirectReference(); } - outlineRef = doc->reserveRef(); - top.setAllRefs(doc, outlineRef); top.emitDescendents(doc); outline.insertRef("First", top.fChildren.front().fRef); outline.insertRef("Last", top.fChildren.back().fRef); diff --git a/gfx/skia/skia/src/pdf/SkPDFTypes.cpp b/gfx/skia/skia/src/pdf/SkPDFTypes.cpp @@ -411,10 +411,6 @@ void SkPDFArray::appendColorComponent(uint8_t value) { this->append(SkPDFUnion::ColorComponent(value)); } -void SkPDFArray::appendColorComponentF(float value) { - this->append(SkPDFUnion::ColorComponentF(value)); -} - void SkPDFArray::appendBool(bool value) { this->append(SkPDFUnion::Bool(value)); } diff --git a/gfx/skia/skia/src/pdf/SkPDFTypes.h b/gfx/skia/skia/src/pdf/SkPDFTypes.h @@ -105,7 +105,6 @@ public: */ void appendInt(int32_t); void appendColorComponent(uint8_t); - void appendColorComponentF(float); void appendBool(bool); void appendScalar(SkScalar); void appendName(const char[]); diff --git a/gfx/skia/skia/src/pdf/SkPDFUtils.cpp b/gfx/skia/skia/src/pdf/SkPDFUtils.cpp @@ -108,9 +108,9 @@ static void append_cubic(SkScalar ctl1X, SkScalar ctl1Y, content->writeText(cmd.c_str()); } -static void append_quad(SkSpan<const SkPoint> quad, SkWStream* content) { +static void append_quad(const SkPoint quad[], SkWStream* content) { SkPoint cubic[4]; - SkConvertQuadToCubic(quad.data(), cubic); + SkConvertQuadToCubic(quad, cubic); append_cubic(cubic[1].fX, cubic[1].fY, cubic[2].fX, cubic[2].fY, cubic[3].fX, cubic[3].fY, content); } @@ -164,19 +164,20 @@ void SkPDFUtils::EmitPath(const SkPath& path, SkPaint::Style paintStyle, //} SkPoint lastMovePt = SkPoint::Make(0,0); SkDynamicMemoryWStream currentSegment; - + SkPoint args[4]; SkPath::Iter iter(path, false); - while (auto rec = iter.next()) { + for (SkPath::Verb verb = iter.next(args); + verb != SkPath::kDone_Verb; + verb = iter.next(args)) { // args gets all the points, even the implicit first point. - SkSpan<const SkPoint> args = rec->fPoints; - switch (rec->fVerb) { - case SkPathVerb::kMove: + switch (verb) { + case SkPath::kMove_Verb: MoveTo(args[0].fX, args[0].fY, &currentSegment); lastMovePt = args[0]; fillState = kEmpty_SkipFillState; break; - case SkPathVerb::kLine: - if (!doConsumeDegerates || !SkPathPriv::AllPointsEq(args)) { + case SkPath::kLine_Verb: + if (!doConsumeDegerates || !SkPathPriv::AllPointsEq(args, 2)) { AppendLine(args[1].fX, args[1].fY, &currentSegment); if ((fillState == kEmpty_SkipFillState) && (args[0] != lastMovePt)) { fillState = kSingleLine_SkipFillState; @@ -185,34 +186,37 @@ void SkPDFUtils::EmitPath(const SkPath& path, SkPaint::Style paintStyle, fillState = kNonSingleLine_SkipFillState; } break; - case SkPathVerb::kQuad: - if (!doConsumeDegerates || !SkPathPriv::AllPointsEq(args)) { + case SkPath::kQuad_Verb: + if (!doConsumeDegerates || !SkPathPriv::AllPointsEq(args, 3)) { append_quad(args, &currentSegment); fillState = kNonSingleLine_SkipFillState; } break; - case SkPathVerb::kConic: - if (!doConsumeDegerates || !SkPathPriv::AllPointsEq(args)) { + case SkPath::kConic_Verb: + if (!doConsumeDegerates || !SkPathPriv::AllPointsEq(args, 3)) { SkAutoConicToQuads converter; - const SkPoint* quads = converter.computeQuads(args, rec->conicWeight(), tolerance); + const SkPoint* quads = converter.computeQuads(args, iter.conicWeight(), tolerance); for (int i = 0; i < converter.countQuads(); ++i) { - append_quad({&quads[i * 2], 3}, &currentSegment); + append_quad(&quads[i * 2], &currentSegment); } fillState = kNonSingleLine_SkipFillState; } break; - case SkPathVerb::kCubic: - if (!doConsumeDegerates || !SkPathPriv::AllPointsEq(args)) { + case SkPath::kCubic_Verb: + if (!doConsumeDegerates || !SkPathPriv::AllPointsEq(args, 4)) { append_cubic(args[1].fX, args[1].fY, args[2].fX, args[2].fY, args[3].fX, args[3].fY, &currentSegment); fillState = kNonSingleLine_SkipFillState; } break; - case SkPathVerb::kClose: + case SkPath::kClose_Verb: ClosePath(&currentSegment); currentSegment.writeToStream(content); currentSegment.reset(); break; + default: + SkASSERT(false); + break; } } if (currentSegment.bytesWritten() > 0) { @@ -289,7 +293,7 @@ static constexpr int int_pow(int base, unsigned exp, int acc = 1) { } -size_t SkPDFUtils::ColorToDecimalF(float value, char (&result)[kFloatColorDecimalCount + 2]) { +size_t SkPDFUtils::ColorToDecimalF(float value, char result[kFloatColorDecimalCount + 2]) { static constexpr int kFactor = int_pow(10, kFloatColorDecimalCount); int x = sk_float_round2int(value * kFactor); if (x >= kFactor || x <= 0) { // clamp to 0-1 @@ -312,11 +316,12 @@ size_t SkPDFUtils::ColorToDecimal(uint8_t value, char result[5]) { } bool SkPDFUtils::InverseTransformBBox(const SkMatrix& matrix, SkRect* bbox) { - if (auto inverse = matrix.invert()) { - inverse->mapRect(bbox); - return true; + SkMatrix inverse; + if (!matrix.invert(&inverse)) { + return false; } - return false; + inverse.mapRect(bbox); + return true; } void SkPDFUtils::PopulateTilingPatternDict(SkPDFDict* pattern, diff --git a/gfx/skia/skia/src/pdf/SkPDFUtils.h b/gfx/skia/skia/src/pdf/SkPDFUtils.h @@ -9,7 +9,6 @@ #include "include/core/SkMatrix.h" #include "include/core/SkPaint.h" -#include "include/core/SkPathTypes.h" #include "include/core/SkRefCnt.h" #include "include/core/SkScalar.h" #include "include/core/SkStream.h" @@ -31,6 +30,7 @@ class SkPDFDict; class SkPath; class SkShader; enum class SkBlendMode; +enum class SkPathFillType; struct SkRect; namespace SkPDF { struct DateTime; } @@ -83,7 +83,7 @@ void ApplyPattern(int objectIndex, SkWStream* content); size_t ColorToDecimal(uint8_t value, char result[5]); static constexpr unsigned kFloatColorDecimalCount = 4; -size_t ColorToDecimalF(float value, char (&result)[kFloatColorDecimalCount + 2]); +size_t ColorToDecimalF(float value, char result[kFloatColorDecimalCount + 2]); inline void AppendColorComponent(uint8_t value, SkWStream* wStream) { char buffer[5]; size_t len = SkPDFUtils::ColorToDecimal(value, buffer); diff --git a/gfx/skia/skia/src/ports/SkFontConfigInterface_direct.cpp b/gfx/skia/skia/src/ports/SkFontConfigInterface_direct.cpp @@ -27,7 +27,7 @@ namespace { // FontConfig was thread antagonistic until 2.10.91 with known thread safety issues until 2.13.93. // Before that, lock with a global mutex. -// See skbug.com/40032593 and cl/339089311 for background. +// See https://bug.skia.org/1497 and cl/339089311 for background. static SkMutex& f_c_mutex() { static SkMutex& mutex = *(new SkMutex); return mutex; @@ -393,11 +393,6 @@ static SkScalar map_ranges(SkScalar val, MapRanges const ranges[], int rangesCou #define FC_WEIGHT_DEMILIGHT 65 #endif -// Available since FontConfig 2.15. -#ifndef FC_FONT_WRAPPER -#define FC_FONT_WRAPPER "fontwrapper" -#endif - static SkFontStyle skfontstyle_from_fcpattern(FcPattern* pattern) { typedef SkFontStyle SkFS; @@ -519,8 +514,9 @@ bool SkFontConfigInterfaceDirect::isAccessible(const char* filename) { bool SkFontConfigInterfaceDirect::isValidPattern(FcPattern* pattern) { #ifdef SK_FONT_CONFIG_INTERFACE_ONLY_ALLOW_SFNT_FONTS const char* font_format = get_string(pattern, FC_FONTFORMAT); - if (!font_format || (0 != strcmp(font_format, kFontFormatTrueType) && - 0 != strcmp(font_format, kFontFormatCFF))) + if (font_format + && 0 != strcmp(font_format, kFontFormatTrueType) + && 0 != strcmp(font_format, kFontFormatCFF)) { return false; } @@ -616,10 +612,6 @@ bool SkFontConfigInterfaceDirect::matchFamilyName(const char familyName[], FcPatternAddBool(pattern, FC_SCALABLE, FcTrue); - // Prefer SFNT fonts in order to reduce the chance of having to reject the font - // in isValidPattern(). - FcPatternAddString(pattern, FC_FONT_WRAPPER, reinterpret_cast<const FcChar8*>("SFNT")); - FcConfigSubstitute(fc, pattern, FcMatchPattern); FcDefaultSubstitute(pattern); diff --git a/gfx/skia/skia/src/ports/SkFontHost_FreeType.cpp b/gfx/skia/skia/src/ports/SkFontHost_FreeType.cpp @@ -13,12 +13,10 @@ #include "include/core/SkFontMetrics.h" #include "include/core/SkGraphics.h" #include "include/core/SkPath.h" -#include "include/core/SkPathBuilder.h" #include "include/core/SkPictureRecorder.h" #include "include/core/SkScalar.h" #include "include/core/SkStream.h" #include "include/core/SkString.h" -#include "include/ports/SkFontScanner_FreeType.h" #include "include/private/base/SkMalloc.h" #include "include/private/base/SkMutex.h" #include "include/private/base/SkTPin.h" @@ -363,9 +361,6 @@ void SkTypeface_FreeType::FaceRec::setupPalette(const SkFontData& data) { if (FT_Palette_Select(fFace.get(), basePaletteIndex, &ftPalette)) { return; } - if (!ftPalette) { - return; - } fFTPaletteEntryCount = paletteData.num_palette_entries; for (int i = 0; i < data.getPaletteOverrideCount(); ++i) { @@ -476,7 +471,7 @@ public: protected: GlyphMetrics generateMetrics(const SkGlyph&, SkArenaAlloc*) override; void generateImage(const SkGlyph&, void*) override; - std::optional<GeneratedPath> generatePath(const SkGlyph& glyph) override; + bool generatePath(const SkGlyph& glyph, SkPath* path, bool* modified) override; sk_sp<SkDrawable> generateDrawable(const SkGlyph&) override; void generateFontMetrics(SkFontMetrics*) override; @@ -642,27 +637,24 @@ std::unique_ptr<SkAdvancedTypefaceMetrics> SkTypeface_FreeType::onGetAdvancedMet return info; } -void SkTypeface_FreeType::getGlyphToUnicodeMap(SkSpan<SkUnichar> dstArray) const { +void SkTypeface_FreeType::getGlyphToUnicodeMap(SkUnichar* dstArray) const { AutoFTAccess fta(this); FT_Face face = fta.face(); if (!face) { return; } - const size_t numGlyphs = std::min(dstArray.size(), (size_t)face->num_glyphs); - if (numGlyphs == 0) { - return; - } - sk_bzero(dstArray.data(), dstArray.size_bytes()); + FT_Long numGlyphs = face->num_glyphs; + if (!dstArray) { SkASSERT(numGlyphs == 0); } + sk_bzero(dstArray, sizeof(SkUnichar) * numGlyphs); FT_UInt glyphIndex; SkUnichar charCode = FT_Get_First_Char(face, &glyphIndex); while (glyphIndex) { - if (glyphIndex < numGlyphs) { - // Use the first character that maps to this glyphID. https://crbug.com/359065 - if (0 == dstArray[glyphIndex]) { - dstArray[glyphIndex] = charCode; - } + SkASSERT(glyphIndex < SkToUInt(numGlyphs)); + // Use the first character that maps to this glyphID. https://crbug.com/359065 + if (0 == dstArray[glyphIndex]) { + dstArray[glyphIndex] = charCode; } charCode = FT_Get_Next_Char(face, charCode, &glyphIndex); } @@ -749,7 +741,7 @@ std::unique_ptr<SkScalerContext> SkTypeface_FreeType::onCreateScalerContextAsPro * cannot. */ static int GetVariationDesignPosition(FT_Face face, - SkSpan<SkFontArguments::VariationPosition::Coordinate> coordinates) + SkFontArguments::VariationPosition::Coordinate coordinates[], int coordinateCount) { if (!(face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS)) { return 0; @@ -761,7 +753,7 @@ static int GetVariationDesignPosition(FT_Face face, } UniqueVoidPtr autoFreeVariations(variations); - if (coordinates.size() < variations->num_axis) { + if (!coordinates || coordinateCount < SkToInt(variations->num_axis)) { return variations->num_axis; } @@ -792,7 +784,7 @@ std::unique_ptr<SkFontData> SkTypeface_FreeType::cloneFontData(const SkFontArgum int axisCount = axisDefinitions.size(); AutoSTMalloc<4, SkFontArguments::VariationPosition::Coordinate> currentPosition(axisCount); - int currentAxisCount = GetVariationDesignPosition(face, {currentPosition, axisCount}); + int currentAxisCount = GetVariationDesignPosition(face, currentPosition, axisCount); SkString name; AutoSTMalloc<4, SkFixed> axisValues(axisCount); @@ -872,19 +864,18 @@ int SkTypeface_FreeType::onGetUPEM() const { return GetUnitsPerEm(face); } -bool SkTypeface_FreeType::onGetKerningPairAdjustments(SkSpan<const SkGlyphID> glyphs, - SkSpan<int32_t> adjustments) const { - SkASSERT(glyphs.size() == adjustments.size() + 1); - +bool SkTypeface_FreeType::onGetKerningPairAdjustments(const SkGlyphID glyphs[], + int count, int32_t adjustments[]) const { AutoFTAccess fta(this); FT_Face face = fta.face(); if (!face || !FT_HAS_KERNING(face)) { return false; } - for (size_t i = 0; i < adjustments.size(); ++i) { + for (int i = 0; i < count - 1; ++i) { FT_Vector delta; - FT_Error err = FT_Get_Kerning(face, glyphs[i], glyphs[i+1], FT_KERNING_UNSCALED, &delta); + FT_Error err = FT_Get_Kerning(face, glyphs[i], glyphs[i+1], + FT_KERNING_UNSCALED, &delta); if (err) { return false; } @@ -1215,7 +1206,7 @@ SkScalerContext::GlyphMetrics SkScalerContext_FreeType::generateMetrics(const Sk FT_Bool haveLayers = false; #ifdef FT_COLOR_H - // See https://skbug.com/40044044, if the face isn't marked scalable then paths cannot be loaded. + // See https://skbug.com/12945, if the face isn't marked scalable then paths cannot be loaded. if (FT_IS_SCALABLE(fFace)) { SkRect bounds = SkRect::MakeEmpty(); #ifdef TT_SUPPORT_COLRV1 @@ -1505,14 +1496,16 @@ sk_sp<SkDrawable> SkScalerContext_FreeType::generateDrawable(const SkGlyph& glyp return nullptr; } -std::optional<SkScalerContext::GeneratedPath> -SkScalerContext_FreeType::generatePath(const SkGlyph& glyph) { +bool SkScalerContext_FreeType::generatePath(const SkGlyph& glyph, SkPath* path, bool* modified) { + SkASSERT(path); + SkAutoMutexExclusive ac(f_t_mutex()); SkGlyphID glyphID = glyph.getGlyphID(); // FT_IS_SCALABLE is documented to mean the face contains outline glyphs. if (!FT_IS_SCALABLE(fFace) || this->setupSize()) { - return {}; + path->reset(); + return false; } uint32_t flags = fLoadGlyphFlags; @@ -1521,14 +1514,14 @@ SkScalerContext_FreeType::generatePath(const SkGlyph& glyph) { FT_Error err = FT_Load_Glyph(fFace, glyphID, flags); if (err != 0 || fFace->glyph->format != FT_GLYPH_FORMAT_OUTLINE) { - return {}; + path->reset(); + return false; } + *modified |= emboldenIfNeeded(fFace, fFace->glyph, glyphID); - bool modified = emboldenIfNeeded(fFace, fFace->glyph, glyphID); - - SkPathBuilder builder; - if (!fUtils.generateGlyphPath(fFace, &builder)) { - return {}; + if (!fUtils.generateGlyphPath(fFace, path)) { + path->reset(); + return false; } // The path's origin from FreeType is always the horizontal layout origin. @@ -1538,10 +1531,10 @@ SkScalerContext_FreeType::generatePath(const SkGlyph& glyph) { vector.x = fFace->glyph->metrics.vertBearingX - fFace->glyph->metrics.horiBearingX; vector.y = -fFace->glyph->metrics.vertBearingY - fFace->glyph->metrics.horiBearingY; FT_Vector_Transform(&vector, &fMatrix22); - builder.offset(SkFDot6ToScalar(vector.x), -SkFDot6ToScalar(vector.y)); - modified = true; + path->offset(SkFDot6ToScalar(vector.x), -SkFDot6ToScalar(vector.y)); + *modified = true; } - return {{builder.detach(), modified}}; + return true; } void SkScalerContext_FreeType::generateFontMetrics(SkFontMetrics* metrics) { @@ -1746,17 +1739,13 @@ SkTypeface_FreeType::~SkTypeface_FreeType() { // Just made up, so we don't end up storing 1000s of entries constexpr int kMaxC2GCacheCount = 512; -void SkTypeface_FreeType::onCharsToGlyphs(SkSpan<const SkUnichar> uni, - SkSpan<SkGlyphID> glyphs) const { +void SkTypeface_FreeType::onCharsToGlyphs(const SkUnichar uni[], int count, + SkGlyphID glyphs[]) const { // Try the cache first, *before* accessing freetype lib/face, as that // can be very slow. If we do need to compute a new glyphID, then // access those freetype objects and continue the loop. - SkASSERT(uni.size() == glyphs.size()); - - const size_t count = uni.size(); - - size_t i; + int i; { // Optimistically use a shared lock. SkAutoSharedMutexShared ama(fC2GCacheMutex); @@ -1778,7 +1767,7 @@ void SkTypeface_FreeType::onCharsToGlyphs(SkSpan<const SkUnichar> uni, AutoFTAccess fta(this); FT_Face face = fta.face(); if (!face) { - sk_bzero(glyphs.data(), glyphs.size_bytes()); + sk_bzero(glyphs, count * sizeof(glyphs[0])); return; } @@ -1829,18 +1818,18 @@ bool SkTypeface_FreeType::onGlyphMaskNeedsCurrentColor() const { } int SkTypeface_FreeType::onGetVariationDesignPosition( - SkSpan<SkFontArguments::VariationPosition::Coordinate> coordinates) const + SkFontArguments::VariationPosition::Coordinate coordinates[], int coordinateCount) const { AutoFTAccess fta(this); FT_Face face = fta.face(); if (!face) { return -1; } - return GetVariationDesignPosition(face, coordinates); + return GetVariationDesignPosition(face, coordinates, coordinateCount); } int SkTypeface_FreeType::onGetVariationDesignParameters( - SkSpan<SkFontParameters::Variation::Axis> parameters) const + SkFontParameters::Variation::Axis parameters[], int parameterCount) const { AutoFTAccess fta(this); FT_Face face = fta.face(); @@ -1858,7 +1847,7 @@ int SkTypeface_FreeType::onGetVariationDesignParameters( } UniqueVoidPtr autoFreeVariations(variations); - if (parameters.size() < variations->num_axis) { + if (!parameters || parameterCount < SkToInt(variations->num_axis)) { return variations->num_axis; } @@ -1876,7 +1865,7 @@ int SkTypeface_FreeType::onGetVariationDesignParameters( return variations->num_axis; } -int SkTypeface_FreeType::onGetTableTags(SkSpan<SkFontTableTag> tags) const { +int SkTypeface_FreeType::onGetTableTags(SkFontTableTag tags[]) const { AutoFTAccess fta(this); FT_Face face = fta.face(); if (!face) { @@ -1892,15 +1881,16 @@ int SkTypeface_FreeType::onGetTableTags(SkSpan<SkFontTableTag> tags) const { return 0; } - const size_t count = std::min<size_t>(tableCount, tags.size()); - for (size_t tableIndex = 0; tableIndex < count; ++tableIndex) { - FT_ULong tableTag; - FT_ULong tablelength; - error = FT_Sfnt_Table_Info(face, tableIndex, &tableTag, &tablelength); - if (error) { - return 0; + if (tags) { + for (FT_ULong tableIndex = 0; tableIndex < tableCount; ++tableIndex) { + FT_ULong tableTag; + FT_ULong tablelength; + error = FT_Sfnt_Table_Info(face, tableIndex, &tableTag, &tablelength); + if (error) { + return 0; + } + tags[tableIndex] = static_cast<SkFontTableTag>(tableTag); } - tags[tableIndex] = static_cast<SkFontTableTag>(tableTag); } return tableCount; } @@ -2237,8 +2227,7 @@ bool SkFontScanner_FreeType::scanInstance(SkStreamAsset* stream, if (position) { position->reset(numAxes); auto coordinates = position->data(); - if (GetVariationDesignPosition(face.get(), - {coordinates, numAxes}) != (int)numAxes) { + if (GetVariationDesignPosition(face.get(), coordinates, numAxes) != (int)numAxes) { return false; } } @@ -2323,7 +2312,6 @@ SkTypeface::FactoryId SkFontScanner_FreeType::getFactoryId() const { static constexpr SkFourByteTag wghtTag = SkSetFourByteTag('w', 'g', 'h', 't'); static constexpr SkFourByteTag wdthTag = SkSetFourByteTag('w', 'd', 't', 'h'); static constexpr SkFourByteTag slntTag = SkSetFourByteTag('s', 'l', 'n', 't'); - static constexpr SkFourByteTag italTag = SkSetFourByteTag('i', 't', 'a', 'l'); int weight = SkFontStyle::kNormal_Weight; int width = SkFontStyle::kNormal_Width; SkFontStyle::Slant slant = SkFontStyle::kUpright_Slant; @@ -2333,8 +2321,6 @@ SkTypeface::FactoryId SkFontScanner_FreeType::getFactoryId() const { slant = style->slant(); } - std::optional<SkFixed> slnt_value; - std::optional<SkFixed> ital_value; for (int i = 0; i < axisDefinitions.size(); ++i) { const SkFontParameters::Variation::Axis& axisDefinition = axisDefinitions[i]; const SkScalar axisMin = axisDefinition.min; @@ -2403,35 +2389,20 @@ SkTypeface::FactoryId SkFontScanner_FreeType::getFactoryId() const { width = SkFontDescriptor::SkFontStyleWidthForWidthAxisValue(wdthValue); } } - if (axisDefinition.tag == slntTag) { - slnt_value = axisValues[i]; - } - if (axisDefinition.tag == italTag) { - ital_value = axisValues[i]; + 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 + // the designer considers to be upright for that font design." + if (axisValues[i] == 0) { + slant = SkFontStyle::kUpright_Slant; + } else { + slant = SkFontStyle::kOblique_Slant; + } } } // TODO: warn on defaulted axis? } - // Value > 0 => +, value == 0 => 0, no value => _ - // slnt\ital _ 0 + - // _ init !ital ital - // 0 !oblq uprt ital - // + oblq oblq ital - if (ital_value && ital_value.value() != 0) { - slant = SkFontStyle::kItalic_Slant; - } else if (slnt_value && slnt_value.value() != 0) { - slant = SkFontStyle::kOblique_Slant; - } else if (ital_value && ital_value.value() == 0.0 && slnt_value && slnt_value.value() == 0.0) { - slant = SkFontStyle::kUpright_Slant; - } else if (ital_value && ital_value.value() == 0.0 && !slnt_value) { - if (slant == SkFontStyle::kItalic_Slant) { - slant = SkFontStyle::kUpright_Slant; - } - } else if (!ital_value && slnt_value && slnt_value.value() == 0.0) { - if (slant == SkFontStyle::kOblique_Slant) { - slant = SkFontStyle::kUpright_Slant; - } - } if (style) { *style = SkFontStyle(weight, width, slant); diff --git a/gfx/skia/skia/src/ports/SkFontHost_FreeType_common.cpp b/gfx/skia/skia/src/ports/SkFontHost_FreeType_common.cpp @@ -16,8 +16,8 @@ #include "include/core/SkImage.h" #include "include/core/SkOpenTypeSVGDecoder.h" #include "include/core/SkPath.h" -#include "include/core/SkPathBuilder.h" #include "include/effects/SkGradientShader.h" +#include "include/pathops/SkPathOps.h" #include "include/private/base/SkTo.h" #include "src/core/SkColorData.h" #include "src/core/SkFDot6.h" @@ -25,7 +25,6 @@ #include "src/core/SkTHash.h" #include <algorithm> -#include <optional> #include <utility> #include <vector> @@ -507,8 +506,7 @@ struct OpaquePaintHasher { using VisitedSet = THashSet<FT_OpaquePaint, OpaquePaintHasher>; -std::optional<SkPath> generateFacePathCOLRv1(FT_Face face, SkGlyphID glyphID, - const SkMatrix* = nullptr); +bool generateFacePathCOLRv1(FT_Face face, SkGlyphID glyphID, SkPath* path); inline float SkColrV1AlphaToFloat(uint16_t alpha) { return (alpha / float(1 << 14)); } @@ -1090,16 +1088,21 @@ bool colrv1_draw_paint(SkCanvas* canvas, const FT_COLR_Paint& colrPaint) { switch (colrPaint.format) { case FT_COLR_PAINTFORMAT_GLYPH: { - auto path = generateFacePathCOLRv1(face, colrPaint.u.glyph.glyphID); - if (!path) { + FT_UInt glyphID = colrPaint.u.glyph.glyphID; + SkPath path; + /* TODO: Currently this call retrieves the path at units_per_em size. If we want to get + * correct hinting for the scaled size under the transforms at this point in the color + * glyph graph, we need to extract at least the requested glyph width and height and + * pass that to the path generation. */ + if (!generateFacePathCOLRv1(face, glyphID, &path)) { return false; } if constexpr (kSkShowTextBlitCoverage) { SkPaint highlight_paint; highlight_paint.setColor(0x33FF0000); - canvas->drawRect(path->getBounds(), highlight_paint); + canvas->drawRect(path.getBounds(), highlight_paint); } - canvas->clipPath(*path, true /* doAntiAlias */); + canvas->clipPath(path, true /* doAntiAlias */); return true; } case FT_COLR_PAINTFORMAT_SOLID: @@ -1142,16 +1145,21 @@ bool colrv1_draw_glyph_with_path(SkCanvas* canvas, return false; } - auto path = generateFacePathCOLRv1(face, glyphPaint.u.glyph.glyphID); - if (!path) { + FT_UInt glyphID = glyphPaint.u.glyph.glyphID; + SkPath path; + /* TODO: Currently this call retrieves the path at units_per_em size. If we want to get + * correct hinting for the scaled size under the transforms at this point in the color + * glyph graph, we need to extract at least the requested glyph width and height and + * pass that to the path generation. */ + if (!generateFacePathCOLRv1(face, glyphID, &path)) { return false; } if constexpr (kSkShowTextBlitCoverage) { SkPaint highlightPaint; highlightPaint.setColor(0x33FF0000); - canvas->drawRect(path->getBounds(), highlightPaint); + canvas->drawRect(path.getBounds(), highlightPaint); } - canvas->drawPath(*path, skiaFillPaint); + canvas->drawPath(path, skiaFillPaint); return true; } @@ -1468,11 +1476,13 @@ bool colrv1_traverse_paint_bounds(SkMatrix* ctm, return true; } case FT_COLR_PAINTFORMAT_GLYPH: { - auto path = generateFacePathCOLRv1(face, paint.u.glyph.glyphID, ctm); - if (!path) { + FT_UInt glyphID = paint.u.glyph.glyphID; + SkPath path; + if (!generateFacePathCOLRv1(face, glyphID, &path)) { return false; } - bounds->join(path->getBounds()); + path.transform(*ctm); + bounds->join(path.getBounds()); return true; } case FT_COLR_PAINTFORMAT_COLR_GLYPH: { @@ -1594,9 +1604,9 @@ bool SkScalerContextFTUtils::drawCOLRv0Glyph(FT_Face face, const SkGlyph& glyph, } else { paint.setColor(palette[layerColorIndex]); } - SkPathBuilder builder; - if (this->generateFacePath(face, layerGlyphIndex, flags, &builder)) { - canvas->drawPath(builder.detach(), paint); + SkPath path; + if (this->generateFacePath(face, layerGlyphIndex, flags, &path)) { + canvas->drawPath(path, paint); } } SkASSERTF(haveLayers, "Could not get COLRv0 layers from '%s'.", face->family_name); @@ -1907,14 +1917,14 @@ void SkScalerContextFTUtils::generateGlyphImage(FT_Face face, const SkGlyph& gly namespace { class SkFTGeometrySink { - SkPathBuilder* fBuilder; + SkPath* fPath; bool fStarted; FT_Vector fCurrent; void goingTo(const FT_Vector* pt) { if (!fStarted) { fStarted = true; - fBuilder->moveTo(SkFDot6ToScalar(fCurrent.x), -SkFDot6ToScalar(fCurrent.y)); + fPath->moveTo(SkFDot6ToScalar(fCurrent.x), -SkFDot6ToScalar(fCurrent.y)); } fCurrent = *pt; } @@ -1926,7 +1936,7 @@ class SkFTGeometrySink { static int Move(const FT_Vector* pt, void* ctx) { SkFTGeometrySink& self = *(SkFTGeometrySink*)ctx; if (self.fStarted) { - self.fBuilder->close(); + self.fPath->close(); self.fStarted = false; } self.fCurrent = *pt; @@ -1937,7 +1947,7 @@ class SkFTGeometrySink { SkFTGeometrySink& self = *(SkFTGeometrySink*)ctx; if (self.currentIsNot(pt)) { self.goingTo(pt); - self.fBuilder->lineTo(SkFDot6ToScalar(pt->x), -SkFDot6ToScalar(pt->y)); + self.fPath->lineTo(SkFDot6ToScalar(pt->x), -SkFDot6ToScalar(pt->y)); } return 0; } @@ -1946,8 +1956,8 @@ class SkFTGeometrySink { SkFTGeometrySink& self = *(SkFTGeometrySink*)ctx; if (self.currentIsNot(pt0) || self.currentIsNot(pt1)) { self.goingTo(pt1); - self.fBuilder->quadTo(SkFDot6ToScalar(pt0->x), -SkFDot6ToScalar(pt0->y), - SkFDot6ToScalar(pt1->x), -SkFDot6ToScalar(pt1->y)); + self.fPath->quadTo(SkFDot6ToScalar(pt0->x), -SkFDot6ToScalar(pt0->y), + SkFDot6ToScalar(pt1->x), -SkFDot6ToScalar(pt1->y)); } return 0; } @@ -1956,15 +1966,15 @@ class SkFTGeometrySink { SkFTGeometrySink& self = *(SkFTGeometrySink*)ctx; if (self.currentIsNot(pt0) || self.currentIsNot(pt1) || self.currentIsNot(pt2)) { self.goingTo(pt2); - self.fBuilder->cubicTo(SkFDot6ToScalar(pt0->x), -SkFDot6ToScalar(pt0->y), - SkFDot6ToScalar(pt1->x), -SkFDot6ToScalar(pt1->y), - SkFDot6ToScalar(pt2->x), -SkFDot6ToScalar(pt2->y)); + self.fPath->cubicTo(SkFDot6ToScalar(pt0->x), -SkFDot6ToScalar(pt0->y), + SkFDot6ToScalar(pt1->x), -SkFDot6ToScalar(pt1->y), + SkFDot6ToScalar(pt2->x), -SkFDot6ToScalar(pt2->y)); } return 0; } public: - SkFTGeometrySink(SkPathBuilder* builder) : fBuilder{builder}, fStarted{false}, fCurrent{0,0} {} + SkFTGeometrySink(SkPath* path) : fPath{path}, fStarted{false}, fCurrent{0,0} {} inline static constexpr const FT_Outline_Funcs Funcs{ /*move_to =*/ SkFTGeometrySink::Move, @@ -1976,35 +1986,33 @@ public: }; }; -bool generateGlyphPathStatic(FT_Face face, SkPathBuilder* builder) { - SkFTGeometrySink sink{builder}; +bool generateGlyphPathStatic(FT_Face face, SkPath* path) { + SkFTGeometrySink sink{path}; if (face->glyph->format != FT_GLYPH_FORMAT_OUTLINE || FT_Outline_Decompose(&face->glyph->outline, &SkFTGeometrySink::Funcs, &sink)) { + path->reset(); return false; } - builder->close(); + path->close(); return true; } bool generateFacePathStatic(FT_Face face, SkGlyphID glyphID, - SkScalerContextFTUtils::LoadGlyphFlags flags, SkPathBuilder* builder) { + SkScalerContextFTUtils::LoadGlyphFlags flags, SkPath* path){ flags |= FT_LOAD_BITMAP_METRICS_ONLY; // Don't decode any bitmaps. flags |= FT_LOAD_NO_BITMAP; // Ignore embedded bitmaps. flags &= ~FT_LOAD_RENDER; // Don't scan convert. flags &= ~FT_LOAD_COLOR; // Ignore SVG. if (FT_Load_Glyph(face, glyphID, flags)) { + path->reset(); return false; } - return generateGlyphPathStatic(face, builder); + return generateGlyphPathStatic(face, path); } #ifdef TT_SUPPORT_COLRV1 -/* TODO: Currently this call retrieves the path at units_per_em size. If we want to get - * correct hinting for the scaled size under the transforms at this point in the color - * glyph graph, we need to extract at least the requested glyph width and height and - * pass that to the path generation. */ -std::optional<SkPath> generateFacePathCOLRv1(FT_Face face, SkGlyphID glyphID, const SkMatrix* mx) { +bool generateFacePathCOLRv1(FT_Face face, SkGlyphID glyphID, SkPath* path) { uint32_t flags = 0; flags |= FT_LOAD_BITMAP_METRICS_ONLY; // Don't decode any bitmaps. flags |= FT_LOAD_NO_BITMAP; // Ignore embedded bitmaps. @@ -2026,62 +2034,66 @@ std::optional<SkPath> generateFacePathCOLRv1(FT_Face face, SkGlyphID glyphID, co }()); if (!unscaledFtSize) { - return {}; + return false; } FT_Size oldSize = face->size; - SkPathBuilder builder; - auto tryGeneratePath = [face, &unscaledFtSize, glyphID, flags, &builder]() { + auto tryGeneratePath = [face, &unscaledFtSize, glyphID, flags, path]() { FT_Error err = 0; err = FT_Activate_Size(unscaledFtSize.get()); if (err != 0) { - return false; + return false; } err = FT_Set_Char_Size(face, SkIntToFDot6(face->units_per_EM), - SkIntToFDot6(face->units_per_EM), 72, 72); + SkIntToFDot6(face->units_per_EM), 72, 72); if (err != 0) { return false; } err = FT_Load_Glyph(face, glyphID, flags); if (err != 0) { + path->reset(); + return false; + } + + if (!generateGlyphPathStatic(face, path)) { + path->reset(); return false; } - return generateGlyphPathStatic(face, &builder); + return true; }; bool pathGenerationResult = tryGeneratePath(); FT_Activate_Size(oldSize); - if (pathGenerationResult) { - if (mx) { - builder.transform(*mx); - } - return builder.detach(); - } else { - return {}; - } + return pathGenerationResult; } #endif } // namespace -bool SkScalerContextFTUtils::generateGlyphPath(FT_Face face, SkPathBuilder* builder) const { - // We used to try simplifying overlapping contours (flags & FT_OUTLINE_OVERLAP) - // at this stage, but that was not 100% reliable, and other font backends - // (e.g. CoreText) do not attempt this, so we removed it. - - return generateGlyphPathStatic(face, builder); +bool SkScalerContextFTUtils::generateGlyphPath(FT_Face face, SkPath* path) const { + if (!generateGlyphPathStatic(face, path)) { + return false; + } + if (face->glyph->outline.flags & FT_OUTLINE_OVERLAP) { + Simplify(*path, path); + // Simplify will return an even-odd path. + // A stroke+fill (for fake bold) may be incorrect for even-odd. + // https://github.com/flutter/flutter/issues/112546 + AsWinding(*path, path); + } + return true; } bool SkScalerContextFTUtils::generateFacePath(FT_Face face, SkGlyphID glyphID, LoadGlyphFlags flags, - SkPathBuilder* builder) const { - return generateFacePathStatic(face, glyphID, flags, builder); + SkPath* path) const { + return generateFacePathStatic(face, glyphID, flags, path); } #ifdef TT_SUPPORT_COLRV1 diff --git a/gfx/skia/skia/src/ports/SkFontHost_FreeType_common.h b/gfx/skia/skia/src/ports/SkFontHost_FreeType_common.h @@ -16,7 +16,6 @@ #include "src/core/SkScalerContext.h" class SkCanvas; -class SkPathBuilder; // These are forward declared to avoid pimpl but also hide the FreeType implementation. typedef struct FT_FaceRec_* FT_Face; @@ -56,7 +55,7 @@ struct SkScalerContextFTUtils { SkSpan<SkColor> palette, SkCanvas*) const; void generateGlyphImage(FT_Face, const SkGlyph&, void*, const SkMatrix& bitmapTransform, const SkMaskGamma::PreBlend&) const; - bool generateGlyphPath(FT_Face, SkPathBuilder*) const; + bool generateGlyphPath(FT_Face, SkPath*) const; /** Computes a bounding box for a COLRv1 glyph. * @@ -67,7 +66,7 @@ struct SkScalerContextFTUtils { static bool computeColrV1GlyphBoundingBox(FT_Face, SkGlyphID, SkRect* bounds); private: - bool generateFacePath(FT_Face, SkGlyphID, LoadGlyphFlags, SkPathBuilder*) const; + bool generateFacePath(FT_Face, SkGlyphID, LoadGlyphFlags, SkPath*) const; }; #endif // SKFONTHOST_FREETYPE_COMMON_H_ diff --git a/gfx/skia/skia/src/ports/SkFontHost_cairo.cpp b/gfx/skia/skia/src/ports/SkFontHost_cairo.cpp @@ -13,7 +13,6 @@ #include "include/core/SkFontMetrics.h" #include "include/core/SkFontTypes.h" #include "include/core/SkPath.h" -#include "include/core/SkPathBuilder.h" #include "include/core/SkStream.h" #include "src/core/SkScalerContext.h" #include "src/core/SkTypefaceCache.h" @@ -104,7 +103,8 @@ public: SkScalerContext_CairoFT(SkTypeface& typeface, const SkScalerContextEffects& effects, const SkDescriptor* desc, FT_Face face, - void* faceContext, FT_LcdFilter lcdFilter); + void* faceContext, SkPixelGeometry pixelGeometry, + FT_LcdFilter lcdFilter); virtual ~SkScalerContext_CairoFT() { mozilla_ForgetSharedFTFaceLockOwner(fFTFaceContext, this); @@ -125,7 +125,7 @@ public: protected: GlyphMetrics generateMetrics(const SkGlyph& glyph, SkArenaAlloc* arena) override; void generateImage(const SkGlyph& glyph, void* imageBuffer) override; - std::optional<GeneratedPath> generatePath(const SkGlyph& glyph) override; + bool generatePath(const SkGlyph& glyph, SkPath* path, bool* modified) override; void generateFontMetrics(SkFontMetrics* metrics) override; private: @@ -186,7 +186,7 @@ public: { SkScalerContext_CairoFT* ctx = new SkScalerContext_CairoFT( *const_cast<SkCairoFTTypeface*>(this), effects, desc, - fFTFace, fFTFaceContext, fLcdFilter); + fFTFace, fFTFaceContext, fPixelGeometry, fLcdFilter); std::unique_ptr<SkScalerContext> result(ctx); if (!ctx->isValid()) { return nullptr; @@ -213,10 +213,10 @@ public: SkDEBUGCODE(SkDebugf("SkCairoFTTypeface::onGetFontDescriptor unimplemented\n")); } - void onCharsToGlyphs(SkSpan<const SkUnichar> chars, SkSpan<SkGlyphID> glyphs) const override + void onCharsToGlyphs(const SkUnichar* chars, int count, SkGlyphID glyphs[]) const override { mozilla_LockSharedFTFace(fFTFaceContext, nullptr); - for (int i = 0; i < chars.size(); ++i) { + for (int i = 0; i < count; ++i) { glyphs[i] = SkToU16(FT_Get_Char_Index(fFTFace, chars[i])); } mozilla_UnlockSharedFTFace(fFTFaceContext); @@ -250,7 +250,7 @@ public: return false; } - int onGetTableTags(SkSpan<SkFontTableTag>) const override + int onGetTableTags(SkFontTableTag*) const override { return 0; } @@ -262,14 +262,16 @@ public: void getPostScriptGlyphNames(SkString*) const override {} - void getGlyphToUnicodeMap(SkSpan<SkUnichar>) const override {} + void getGlyphToUnicodeMap(SkUnichar*) const override {} - int onGetVariationDesignPosition(SkSpan<SkFontArguments::VariationPosition::Coordinate>) const override + int onGetVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[], + int coordinateCount) const override { return 0; } - int onGetVariationDesignParameters(SkSpan<SkFontParameters::Variation::Axis> parameters) const override + int onGetVariationDesignParameters(SkFontParameters::Variation::Axis parameters[], + int parameterCount) const override { return 0; } @@ -278,10 +280,12 @@ public: return sk_ref_sp(this); } - SkCairoFTTypeface(FT_Face face, void* faceContext, FT_LcdFilter lcdFilter) + SkCairoFTTypeface(FT_Face face, void* faceContext, + SkPixelGeometry pixelGeometry, FT_LcdFilter lcdFilter) : SkTypeface(SkFontStyle::Normal()) , fFTFace(face) , fFTFaceContext(faceContext) + , fPixelGeometry(pixelGeometry) , fLcdFilter(lcdFilter) { mozilla_AddRefSharedFTFace(fFTFaceContext); @@ -307,6 +311,7 @@ private: FT_Face fFTFace; void* fFTFaceContext; + SkPixelGeometry fPixelGeometry; FT_LcdFilter fLcdFilter; }; @@ -315,12 +320,13 @@ static bool FindByFTFaceContext(SkTypeface* typeface, void* context) { } SkTypeface* SkCreateTypefaceFromCairoFTFont(FT_Face face, void* faceContext, + SkPixelGeometry pixelGeometry, uint8_t lcdFilter) { sk_sp<SkTypeface> typeface = SkTypefaceCache::FindByProcAndRef(FindByFTFaceContext, faceContext); if (!typeface) { - typeface = sk_make_sp<SkCairoFTTypeface>(face, faceContext, + typeface = sk_make_sp<SkCairoFTTypeface>(face, faceContext, pixelGeometry, (FT_LcdFilter)lcdFilter); SkTypefaceCache::Add(typeface); } @@ -331,13 +337,14 @@ SkTypeface* SkCreateTypefaceFromCairoFTFont(FT_Face face, void* faceContext, SkScalerContext_CairoFT::SkScalerContext_CairoFT( SkTypeface& typeface, const SkScalerContextEffects& effects, const SkDescriptor* desc, FT_Face face, void* faceContext, - FT_LcdFilter lcdFilter) + SkPixelGeometry pixelGeometry, FT_LcdFilter lcdFilter) : SkScalerContext(typeface, effects, desc) , fFTFace(face) , fFTFaceContext(faceContext) , fLcdFilter(lcdFilter) { - SkMatrix matrix = fRec.getSingleMatrix(); + SkMatrix matrix; + fRec.getSingleMatrix(&matrix); computeShapeMatrix(matrix); @@ -351,6 +358,24 @@ SkScalerContext_CairoFT::SkScalerContext_CairoFT( } loadFlags |= FT_LOAD_MONOCHROME; } else { + if (isLCD(fRec)) { + switch (pixelGeometry) { + case kRGB_H_SkPixelGeometry: + default: + break; + case kRGB_V_SkPixelGeometry: + fRec.fFlags |= SkScalerContext::kLCD_Vertical_Flag; + break; + case kBGR_H_SkPixelGeometry: + fRec.fFlags |= SkScalerContext::kLCD_BGROrder_Flag; + break; + case kBGR_V_SkPixelGeometry: + fRec.fFlags |= SkScalerContext::kLCD_Vertical_Flag | + SkScalerContext::kLCD_BGROrder_Flag; + break; + } + } + switch (fRec.getHinting()) { case SkFontHinting::kNone: loadFlags |= FT_LOAD_NO_HINTING; @@ -542,15 +567,17 @@ SkScalerContext::GlyphMetrics SkScalerContext_CairoFT::generateMetrics(const SkG if (fFTFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA) { mx.maskFormat = SkMask::kARGB32_Format; - } else if (isLCD(fRec)) { - mx.maskFormat = SkMask::kA8_Format; + } + + if (isLCD(fRec)) { + fRec.fMaskFormat = SkMask::kA8_Format; } if (fHaveShape) { // Ensure filtering is preserved when the bitmap is transformed. // Otherwise, the result will look horrifically aliased. - if (mx.maskFormat == SkMask::kBW_Format) { - mx.maskFormat = SkMask::kA8_Format; + if (fRec.fMaskFormat == SkMask::kBW_Format) { + fRec.fMaskFormat = SkMask::kA8_Format; } // Apply the shape matrix to the glyph's bounding box. @@ -626,10 +653,12 @@ void SkScalerContext_CairoFT::generateImage(const SkGlyph& glyph, void* imageBuf } } -std::optional<SkScalerContext::GeneratedPath> SkScalerContext_CairoFT::generatePath(const SkGlyph& glyph) +bool SkScalerContext_CairoFT::generatePath(const SkGlyph& glyph, SkPath* path, bool* modified) { AutoLockFTFace faceLock(this); + SkASSERT(path); + SkGlyphID glyphID = glyph.getGlyphID(); uint32_t flags = fLoadGlyphFlags; @@ -639,16 +668,13 @@ std::optional<SkScalerContext::GeneratedPath> SkScalerContext_CairoFT::generateP FT_Error err = mozilla_LoadFTGlyph(fFTFace, glyphID, flags); if (err != 0) { - return {}; + path->reset(); + return false; } - bool modified = prepareGlyph(fFTFace->glyph); + *modified |= prepareGlyph(fFTFace->glyph); - SkPathBuilder builder; - if (!fUtils.generateGlyphPath(fFTFace, &builder)) { - return {}; - } - return {{builder.detach(), modified}}; + return fUtils.generateGlyphPath(fFTFace, path); } void SkScalerContext_CairoFT::generateFontMetrics(SkFontMetrics* metrics) diff --git a/gfx/skia/skia/src/ports/SkFontHost_win.cpp b/gfx/skia/skia/src/ports/SkFontHost_win.cpp @@ -12,7 +12,6 @@ #include "include/core/SkFontMetrics.h" #include "include/core/SkFontTypes.h" #include "include/core/SkPath.h" -#include "include/core/SkPathBuilder.h" #include "include/core/SkStream.h" #include "include/core/SkString.h" #include "include/ports/SkTypeface_win.h" @@ -287,10 +286,10 @@ protected: std::unique_ptr<SkScalerContext> onCreateScalerContext(const SkScalerContextEffects&, const SkDescriptor*) const override; void onFilterRec(SkScalerContextRec*) const override; - void getGlyphToUnicodeMap(SkSpan<SkUnichar>) const override; + void getGlyphToUnicodeMap(SkUnichar*) const override; std::unique_ptr<SkAdvancedTypefaceMetrics> onGetAdvancedMetrics() const override; void onGetFontDescriptor(SkFontDescriptor*, bool*) const override; - void onCharsToGlyphs(SkSpan<const SkUnichar>, SkSpan<SkGlyphID>) const override; + void onCharsToGlyphs(const SkUnichar* chars, int count, SkGlyphID glyphs[]) const override; int onCountGlyphs() const override; void getPostScriptGlyphNames(SkString*) const override; int onGetUPEM() const override; @@ -298,16 +297,17 @@ protected: bool onGetPostScriptName(SkString*) const override { return false; } SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override; bool onGlyphMaskNeedsCurrentColor() const override { return false; } - int onGetVariationDesignPosition( - SkSpan<SkFontArguments::VariationPosition::Coordinate>) const override + int onGetVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[], + int coordinateCount) const override { return -1; } - int onGetVariationDesignParameters(SkSpan<SkFontParameters::Variation::Axis>) const override + int onGetVariationDesignParameters(SkFontParameters::Variation::Axis parameters[], + int parameterCount) const override { return -1; } - int onGetTableTags(SkSpan<SkFontTableTag>) const override; + int onGetTableTags(SkFontTableTag tags[]) const override; size_t onGetTableData(SkFontTableTag, size_t offset, size_t length, void* data) const override; sk_sp<SkData> onCopyTableData(SkFontTableTag) const override; }; @@ -412,8 +412,8 @@ void SkLOGFONTFromTypeface(const SkTypeface* face, LOGFONT* lf) { // require parsing the TTF cmap table (platform 4, encoding 12) directly instead // of calling GetFontUnicodeRange(). static void populate_glyph_to_unicode(HDC fontHdc, const unsigned glyphCount, - SkSpan<SkUnichar> glyphToUnicode) { - sk_bzero(glyphToUnicode.data(), glyphToUnicode.size_bytes()); + SkUnichar* glyphToUnicode) { + sk_bzero(glyphToUnicode, sizeof(SkUnichar) * glyphCount); DWORD glyphSetBufferSize = GetFontUnicodeRanges(fontHdc, nullptr); if (!glyphSetBufferSize) { return; @@ -591,7 +591,7 @@ public: protected: GlyphMetrics generateMetrics(const SkGlyph&, SkArenaAlloc*) override; void generateImage(const SkGlyph&, void* imageBuffer) override; - std::optional<GeneratedPath> generatePath(const SkGlyph&) override; + bool generatePath(const SkGlyph& glyph, SkPath* path, bool* modified) override; void generateFontMetrics(SkFontMetrics*) override; private: @@ -629,7 +629,6 @@ private: enum Type { kTrueType_Type, kBitmap_Type, kLine_Type } fType; - bool fGenerateFromPath; TEXTMETRIC fTM; }; @@ -664,7 +663,6 @@ SkScalerContext_GDI::SkScalerContext_GDI(LogFontTypeface& rawTypeface, , fSavefont(nullptr) , fFont(nullptr) , fSC(nullptr) - , fGenerateFromPath(false) { LogFontTypeface* typeface = static_cast<LogFontTypeface*>(this->getTypeface()); @@ -760,7 +758,7 @@ SkScalerContext_GDI::SkScalerContext_GDI(LogFontTypeface& rawTypeface, fMat22.eM22 = SkFloatToFIXED(xform.eM22); if (needToRenderWithSkia(fRec)) { - fGenerateFromPath = true; + this->forceGenerateImageFromPath(); } // Create a hires matrix if we need linear metrics. @@ -913,17 +911,15 @@ SkScalerContext::GlyphMetrics SkScalerContext_GDI::generateMetrics(const SkGlyph sk_bzero(&gm, sizeof(gm)); status = GetGlyphOutlineW(fDDC, glyphId, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, nullptr, &fHighResMat22); if (GDI_ERROR != status) { - mx.advance = fHiResMatrix.mapPoint({SkIntToScalar(gm.gmCellIncX), - SkIntToScalar(gm.gmCellIncY)}); + mx.advance = fHiResMatrix.mapXY(SkIntToScalar(gm.gmCellIncX), + SkIntToScalar(gm.gmCellIncY)); } } else if (!isAxisAligned(this->fRec)) { status = GetGlyphOutlineW(fDDC, glyphId, GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, nullptr, &fGsA); if (GDI_ERROR != status) { - mx.advance = fG_inv.mapPoint({SkIntToScalar(gm.gmCellIncX), SkIntToScalar(gm.gmCellIncY)}); + mx.advance = fG_inv.mapXY(SkIntToScalar(gm.gmCellIncX), SkIntToScalar(gm.gmCellIncY)); } } - - mx.computeFromPath = fGenerateFromPath; return mx; } @@ -1108,11 +1104,6 @@ void SkScalerContext_GDI::RGBToLcd16( void SkScalerContext_GDI::generateImage(const SkGlyph& glyph, void* imageBuffer) { SkASSERT(fDDC); - if (fGenerateFromPath && glyph.path()) { - this->generateImageFromPath(glyph, imageBuffer); - return; - } - const bool isBW = SkMask::kBW_Format == fRec.fMaskFormat; const bool isAA = !isLCD(fRec); @@ -1338,15 +1329,15 @@ private: }; class SkGDIGeometrySink { - SkPathBuilder* fBuilder; + SkPath* fPath; bool fStarted = false; POINTFX fCurrent; void goingTo(const POINTFX pt) { if (!fStarted) { fStarted = true; - fBuilder->moveTo( SkFIXEDToScalar(fCurrent.x), - -SkFIXEDToScalar(fCurrent.y)); + fPath->moveTo( SkFIXEDToScalar(fCurrent.x), + -SkFIXEDToScalar(fCurrent.y)); } fCurrent = pt; } @@ -1357,7 +1348,7 @@ class SkGDIGeometrySink { } public: - SkGDIGeometrySink(SkPathBuilder* builder) : fBuilder(builder) {} + SkGDIGeometrySink(SkPath* path) : fPath(path) {} void process(const uint8_t* glyphbuf, DWORD total_size); /** It is possible for the hinted and unhinted versions of the same path to have @@ -1390,7 +1381,7 @@ void SkGDIGeometrySink::process(const uint8_t* glyphbuf, DWORD total_size) { POINTFX pnt_b = apfx[i]; if (this->currentIsNot(pnt_b)) { this->goingTo(pnt_b); - fBuilder->lineTo( SkFIXEDToScalar(pnt_b.x), + fPath->lineTo( SkFIXEDToScalar(pnt_b.x), -SkFIXEDToScalar(pnt_b.y)); } } @@ -1411,10 +1402,10 @@ void SkGDIGeometrySink::process(const uint8_t* glyphbuf, DWORD total_size) { if (this->currentIsNot(pnt_b) || this->currentIsNot(pnt_c)) { this->goingTo(pnt_c); - fBuilder->quadTo( SkFIXEDToScalar(pnt_b.x), - -SkFIXEDToScalar(pnt_b.y), - SkFIXEDToScalar(pnt_c.x), - -SkFIXEDToScalar(pnt_c.y)); + fPath->quadTo( SkFIXEDToScalar(pnt_b.x), + -SkFIXEDToScalar(pnt_b.y), + SkFIXEDToScalar(pnt_c.x), + -SkFIXEDToScalar(pnt_c.y)); } } } @@ -1424,7 +1415,7 @@ void SkGDIGeometrySink::process(const uint8_t* glyphbuf, DWORD total_size) { } cur_glyph += th->cb; if (this->fStarted) { - fBuilder->close(); + fPath->close(); } } } @@ -1462,8 +1453,8 @@ bool SkGDIGeometrySink::process(const uint8_t* glyphbuf, DWORD total_size, POINTFX pnt_b = {apfx[i].x, hintedPoint->y}; if (this->currentIsNot(pnt_b)) { this->goingTo(pnt_b); - fBuilder->lineTo( SkFIXEDToScalar(pnt_b.x), - -SkFIXEDToScalar(pnt_b.y)); + fPath->lineTo( SkFIXEDToScalar(pnt_b.x), + -SkFIXEDToScalar(pnt_b.y)); } } } @@ -1495,10 +1486,10 @@ bool SkGDIGeometrySink::process(const uint8_t* glyphbuf, DWORD total_size, if (this->currentIsNot(pnt_b) || this->currentIsNot(pnt_c)) { this->goingTo(pnt_c); - fBuilder->quadTo( SkFIXEDToScalar(pnt_b.x), - -SkFIXEDToScalar(pnt_b.y), - SkFIXEDToScalar(pnt_c.x), - -SkFIXEDToScalar(pnt_c.y)); + fPath->quadTo( SkFIXEDToScalar(pnt_b.x), + -SkFIXEDToScalar(pnt_b.y), + SkFIXEDToScalar(pnt_c.x), + -SkFIXEDToScalar(pnt_c.y)); } } } @@ -1508,7 +1499,7 @@ bool SkGDIGeometrySink::process(const uint8_t* glyphbuf, DWORD total_size, } cur_glyph += th->cb; if (this->fStarted) { - fBuilder->close(); + fPath->close(); } } return true; @@ -1553,10 +1544,12 @@ DWORD SkScalerContext_GDI::getGDIGlyphPath(SkGlyphID glyph, UINT flags, return total_size; } -std::optional<SkScalerContext::GeneratedPath> -SkScalerContext_GDI::generatePath(const SkGlyph& glyph) { +bool SkScalerContext_GDI::generatePath(const SkGlyph& glyph, SkPath* path, bool* modified) { + SkASSERT(path); SkASSERT(fDDC); + path->reset(); + SkGlyphID glyphID = glyph.getGlyphID(); // Out of all the fonts on a typical Windows box, @@ -1575,12 +1568,11 @@ SkScalerContext_GDI::generatePath(const SkGlyph& glyph) { AutoSTMalloc<BUFFERSIZE, uint8_t> glyphbuf(BUFFERSIZE); DWORD total_size = getGDIGlyphPath(glyphID, format, &glyphbuf); if (0 == total_size) { - return {}; + return false; } - SkPathBuilder builder; if (fRec.getHinting() != SkFontHinting::kSlight) { - SkGDIGeometrySink sink(&builder); + SkGDIGeometrySink sink(path); sink.process(glyphbuf, total_size); } else { AutoSTMalloc<BUFFERSIZE, uint8_t> hintedGlyphbuf(BUFFERSIZE); @@ -1588,20 +1580,20 @@ SkScalerContext_GDI::generatePath(const SkGlyph& glyph) { DWORD hinted_total_size = getGDIGlyphPath(glyphID, GGO_NATIVE | GGO_GLYPH_INDEX, &hintedGlyphbuf); if (0 == hinted_total_size) { - return {}; + return false; } - SkGDIGeometrySink sinkXBufYIter(&builder); + SkGDIGeometrySink sinkXBufYIter(path); if (!sinkXBufYIter.process(glyphbuf, total_size, GDIGlyphbufferPointIter(hintedGlyphbuf, hinted_total_size))) { // Both path and sinkXBufYIter are in the state they were in at the time of failure. - builder.reset(); - SkGDIGeometrySink sink(&builder); + path->reset(); + SkGDIGeometrySink sink(path); sink.process(glyphbuf, total_size); } } - return {{builder.detach(), false}}; + return true; } static void logfont_for_name(const char* familyName, LOGFONT* lf) { @@ -1640,10 +1632,10 @@ void LogFontTypeface::onGetFontDescriptor(SkFontDescriptor* desc, *isLocalStream = this->fSerializeAsStream; } -void LogFontTypeface::getGlyphToUnicodeMap(SkSpan<SkUnichar> dstArray) const { +void LogFontTypeface::getGlyphToUnicodeMap(SkUnichar* dstArray) const { SkAutoHDC hdc(fLogFont); unsigned int glyphCount = calculateGlyphCount(hdc, fLogFont); - populate_glyph_to_unicode(hdc, std::min<unsigned>(glyphCount, dstArray.size()), dstArray); + populate_glyph_to_unicode(hdc, glyphCount, dstArray); } std::unique_ptr<SkAdvancedTypefaceMetrics> LogFontTypeface::onGetAdvancedMetrics() const { @@ -1796,7 +1788,7 @@ static HRESULT create_unique_font_name(char* buffer, size_t bufferSize) { Introduces a font to GDI. On failure will return nullptr. The returned handle should eventually be passed to RemoveFontMemResourceEx. */ -static HANDLE activate_font(const SkData* fontData) { +static HANDLE activate_font(SkData* fontData) { DWORD numFonts = 0; //AddFontMemResourceEx just copies the data, but does not specify const. HANDLE fontHandle = AddFontMemResourceEx(const_cast<void*>(fontData->data()), @@ -1965,9 +1957,9 @@ static uint16_t nonBmpCharToGlyph(HDC hdc, SCRIPT_CACHE* scriptCache, const WCHA return index; } -void LogFontTypeface::onCharsToGlyphs(SkSpan<const SkUnichar> uni, SkSpan<SkGlyphID> glyphs) const { - SkASSERT(uni.size() == glyphs.size()); - +void LogFontTypeface::onCharsToGlyphs(const SkUnichar* uni, int glyphCount, + SkGlyphID glyphs[]) const +{ SkAutoHDC hdc(fLogFont); TEXTMETRIC tm; @@ -1983,8 +1975,7 @@ void LogFontTypeface::onCharsToGlyphs(SkSpan<const SkUnichar> uni, SkSpan<SkGlyp static const int scratchCount = 256; WCHAR scratch[scratchCount]; int glyphIndex = 0; - const int glyphCount = SkToInt(glyphs.size()); - const uint32_t* utf32 = reinterpret_cast<const uint32_t*>(uni.data()); + const uint32_t* utf32 = reinterpret_cast<const uint32_t*>(uni); while (glyphIndex < glyphCount) { // Try a run of bmp. int glyphsLeft = std::min(glyphCount - glyphIndex, scratchCount); @@ -2035,23 +2026,22 @@ SkTypeface::LocalizedStrings* LogFontTypeface::onCreateFamilyNameIterator() cons return nameIter.release(); } -int LogFontTypeface::onGetTableTags(SkSpan<SkFontTableTag> tags) const { +int LogFontTypeface::onGetTableTags(SkFontTableTag tags[]) const { SkSFNTHeader header; if (sizeof(header) != this->onGetTableData(0, 0, sizeof(header), &header)) { return 0; } - size_t numTables = SkEndian_SwapBE16(header.numTables); + int numTables = SkEndian_SwapBE16(header.numTables); - if (!tags.empty()) { + if (tags) { size_t size = numTables * sizeof(SkSFNTHeader::TableDirectoryEntry); AutoSTMalloc<0x20, SkSFNTHeader::TableDirectoryEntry> dir(numTables); if (size != this->onGetTableData(0, sizeof(header), size, dir.get())) { return 0; } - const size_t n = std::min(numTables, tags.size()); - for (size_t i = 0; i < n; ++i) { + for (int i = 0; i < numTables; ++i) { tags[i] = SkEndian_SwapBE32(dir[i].tag); } } diff --git a/gfx/skia/skia/src/ports/SkFontMgr_FontConfigInterface.cpp b/gfx/skia/skia/src/ports/SkFontMgr_FontConfigInterface.cpp @@ -12,11 +12,13 @@ #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" #include "src/core/SkTypefaceCache.h" #include "src/ports/SkFontConfigTypeface.h" +#include "src/ports/SkTypeface_FreeType.h" #include <new> @@ -197,7 +199,7 @@ protected: return nullptr; } - // Check if a typeface with this FontIdentity is already in the typeface cache. + // Check if a typeface with this FontIdentity is already in the FontIdentity cache. face = fTFCache.findByProcAndRef(find_by_FontIdentity, &identity); if (!face) { sk_sp<SkTypeface> realTypeface = fScanner->MakeFromStream( @@ -205,10 +207,8 @@ protected: SkFontArguments().setCollectionIndex(identity.fTTCIndex)); face.reset(SkTypeface_FCI::Create(std::move(realTypeface), fFCI, identity, std::move(outFamilyName), outStyle, false)); - if (face) { - // Add this typeface to the typeface cache. - fTFCache.add(face); - } + // Add this FontIdentity to the FontIdentity cache. + fTFCache.add(face); } // Add this request to the request cache. fCache.add(face, request.release()); @@ -242,7 +242,7 @@ protected: return nullptr; // don't accept too large fonts (>= 1GB) for safety. } - return fScanner->MakeFromStream(std::move(stream), args); + return SkTypeface_FreeType::MakeFromStream(std::move(stream), args); } sk_sp<SkTypeface> onMakeFromFile(const char path[], int ttcIndex) const override { @@ -261,3 +261,8 @@ SK_API sk_sp<SkFontMgr> SkFontMgr_New_FCI(sk_sp<SkFontConfigInterface> fci, SkASSERT(fci); return sk_make_sp<SkFontMgr_FCI>(std::move(fci), std::move(scanner)); } + +SK_API sk_sp<SkFontMgr> SkFontMgr_New_FCI(sk_sp<SkFontConfigInterface> fci) { + SkASSERT(fci); + return sk_make_sp<SkFontMgr_FCI>(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 @@ -9,13 +9,13 @@ #include "include/core/SkData.h" #include "include/core/SkFontMgr.h" -#include "include/core/SkFontScanner.h" #include "include/core/SkFontStyle.h" #include "include/core/SkPaint.h" #include "include/core/SkRefCnt.h" #include "include/core/SkStream.h" #include "include/core/SkString.h" #include "include/ports/SkFontMgr_android.h" +#include "include/ports/SkFontScanner_FreeType.h" #include "include/private/base/SkFixed.h" #include "include/private/base/SkTArray.h" #include "include/private/base/SkTDArray.h" @@ -36,9 +36,6 @@ using namespace skia_private; class SkData; namespace { - -[[maybe_unused]] static inline const constexpr bool kSkFontMgrVerbose = false; - class SkTypeface_AndroidSystem : public SkTypeface_proxy { public: static sk_sp<SkTypeface_AndroidSystem> Make(sk_sp<SkTypeface> realTypeface, @@ -54,6 +51,7 @@ public: } const SkString fFamilyName; + const STArray<4, SkFixed, true> fAxes; const STArray<4, SkLanguage, true> fLang; const FontVariant fVariantStyle; @@ -240,7 +238,7 @@ public: SkFontMgr_Android(const SkFontMgr_Android_CustomFonts* custom, std::unique_ptr<SkFontScanner> scanner) : fScanner(std::move(scanner)) { - std::vector<std::unique_ptr<FontFamily>> families; + SkTDArray<FontFamily*> families; if (custom && SkFontMgr_Android_CustomFonts::kPreferSystem != custom->fSystemFontUse) { SkString base(custom->fBasePath); SkFontMgr_Android_Parser::GetCustomFontFamilies( @@ -258,6 +256,10 @@ public: } this->buildNameToFamilyMap(families, custom ? custom->fIsolated : false); this->findDefaultStyleSet(); + for (FontFamily* p : families) { + delete p; + } + families.reset(); } protected: @@ -308,22 +310,16 @@ protected: return sset->matchStyle(style); } - enum class NameType { Self, Fallback }; static sk_sp<SkTypeface_AndroidSystem> find_family_style_character( const SkString& familyName, - const TArray<NameToFamily, true>& nameToFamilyMap, - NameType nameType, + const TArray<NameToFamily, true>& fallbackNameToFamilyMap, const SkFontStyle& style, bool elegant, const SkString& langTag, SkUnichar character) { - for (auto&& nameToFamily : nameToFamilyMap) { - SkFontStyleSet_Android* family = nameToFamily.styleSet; - if (!familyName.isEmpty()) { - const SkString& name = nameType == NameType::Self ? nameToFamily.name - : family->fFallbackFor; - if (familyName != name) { - continue; - } + for (int i = 0; i < fallbackNameToFamilyMap.size(); ++i) { + SkFontStyleSet_Android* family = fallbackNameToFamilyMap[i].styleSet; + if (familyName != family->fFallbackFor) { + continue; } sk_sp<SkTypeface_AndroidSystem> face(family->matchAStyle(style)); @@ -355,7 +351,7 @@ protected: // The variant 'default' means 'compact and elegant'. // As a result, it is not possible to know the variant context from the font alone. // TODO: add 'is_elegant' and 'is_compact' bits to 'style' request. - sk_sp<SkTypeface_AndroidSystem> matchingTypeface; + SkString familyNameString(familyName); for (const SkString& currentFamilyName : { familyNameString, SkString() }) { // The first time match anything elegant, second time anything not elegant. @@ -363,15 +359,10 @@ protected: for (int bcp47Index = bcp47Count; bcp47Index --> 0;) { SkLanguage lang(bcp47[bcp47Index]); while (!lang.getTag().isEmpty()) { - matchingTypeface = find_family_style_character( - currentFamilyName, fNameToFamilyMap, NameType::Self, - style, SkToBool(elegant), lang.getTag(), character); - if (matchingTypeface) { - return matchingTypeface; - } - matchingTypeface = find_family_style_character( - currentFamilyName, fFallbackNameToFamilyMap, NameType::Fallback, - style, SkToBool(elegant), lang.getTag(), character); + sk_sp<SkTypeface_AndroidSystem> matchingTypeface = + find_family_style_character(currentFamilyName, fFallbackNameToFamilyMap, + style, SkToBool(elegant), + lang.getTag(), character); if (matchingTypeface) { return matchingTypeface; } @@ -379,15 +370,10 @@ protected: lang = lang.getParent(); } } - matchingTypeface = find_family_style_character( - currentFamilyName, fNameToFamilyMap, NameType::Self, - style, SkToBool(elegant), SkString(), character); - if (matchingTypeface) { - return matchingTypeface; - } - matchingTypeface = find_family_style_character( - currentFamilyName, fFallbackNameToFamilyMap, NameType::Fallback, - style, SkToBool(elegant), SkString(), character); + sk_sp<SkTypeface_AndroidSystem> matchingTypeface = + find_family_style_character(currentFamilyName, fFallbackNameToFamilyMap, + style, SkToBool(elegant), + SkString(), character); if (matchingTypeface) { return matchingTypeface; } @@ -466,11 +452,10 @@ private: } fStyleSets.emplace_back(std::move(newSet)); } - void buildNameToFamilyMap(std::vector<std::unique_ptr<FontFamily>>& families, - const bool isolated) { + void buildNameToFamilyMap(const SkTDArray<FontFamily*>& families, const bool isolated) { StreamForPathCache streamForPath; int familyIndex = 0; - for (std::unique_ptr<FontFamily>& family : families) { + for (FontFamily* family : families) { addFamily(*family, isolated, familyIndex++, streamForPath); for (const auto& [unused, fallbackFamily] : family->fallbackFamilies) { addFamily(*fallbackFamily, isolated, familyIndex++, streamForPath); @@ -502,18 +487,19 @@ static char const * const gSystemFontUseStrings[] = { } // namespace -sk_sp<SkFontMgr> SkFontMgr_New_Android(const SkFontMgr_Android_CustomFonts* custom, - std::unique_ptr<SkFontScanner> scanner) { +sk_sp<SkFontMgr> SkFontMgr_New_Android(const SkFontMgr_Android_CustomFonts* custom) { + return SkFontMgr_New_Android(custom, SkFontScanner_Make_FreeType()); +} + +sk_sp<SkFontMgr> SkFontMgr_New_Android(const SkFontMgr_Android_CustomFonts* custom, std::unique_ptr<SkFontScanner> scanner) { if (custom) { SkASSERT(0 <= custom->fSystemFontUse); SkASSERT(custom->fSystemFontUse < std::size(gSystemFontUseStrings)); - if constexpr (kSkFontMgrVerbose) { - SkDEBUGF("SystemFontUse: %s BasePath: %s Fonts: %s FallbackFonts: %s\n", - gSystemFontUseStrings[custom->fSystemFontUse], - custom->fBasePath, - custom->fFontsXml, - custom->fFallbackFontsXml); - } + SkDEBUGF("SystemFontUse: %s BasePath: %s Fonts: %s FallbackFonts: %s\n", + gSystemFontUseStrings[custom->fSystemFontUse], + custom->fBasePath, + custom->fFontsXml, + custom->fFallbackFontsXml); } return sk_make_sp<SkFontMgr_Android>(custom, std::move(scanner)); } diff --git a/gfx/skia/skia/src/ports/SkFontMgr_android_ndk.cpp b/gfx/skia/skia/src/ports/SkFontMgr_android_ndk.cpp @@ -5,40 +5,22 @@ * found in the LICENSE file. */ -#include "include/core/SkFontArguments.h" #include "include/core/SkFontMgr.h" -#include "include/core/SkFontScanner.h" #include "include/core/SkStream.h" #include "include/core/SkTypeface.h" #include "include/core/SkTypes.h" #include "include/ports/SkFontMgr_android_ndk.h" -#include "include/private/base/SkAssert.h" -#include "include/private/base/SkFeatures.h" -#include "include/private/base/SkFloatingPoint.h" +#include "include/ports/SkFontScanner_FreeType.h" #include "include/private/base/SkTArray.h" #include "include/private/base/SkTemplates.h" -#include "src/base/SkSharedMutex.h" -#include "src/base/SkTSort.h" +#include "src/base/SkTSearch.h" #include "src/base/SkUTF.h" -#include "src/core/SkChecksum.h" #include "src/core/SkFontDescriptor.h" -#include "src/core/SkLRUCache.h" +#include "src/core/SkOSFile.h" #include "src/core/SkTHash.h" -#include "src/ports/SkFontMgr_android_parser.h" #include "src/ports/SkTypeface_proxy.h" -#include <unicode/uchar.h> -#include <unicode/ustring.h> - -#if defined(SK_BUILD_FOR_ANDROID) #include <android/api-level.h> -#else -#define __ANDROID_API__ 0 -#define __ANDROID_API_Q__ 29 -#define __ANDROID_API_R__ 30 -#define __ANDROID_API_S__ 31 -int android_get_device_api_level() { return __ANDROID_API__; }; -#endif using namespace skia_private; @@ -65,10 +47,9 @@ using namespace skia_private; * As a result, there is no correct way to use locale information from the Android 10 NDK. So this * font manager only works with Android 11 (R, API 30) and above. */ -#define SK_ANDROID_NDK_FONT_API_EXISTS __ANDROID_API_Q__ -#define SK_ANDROID_NDK_FONT_API_LOCALE_WORKS __ANDROID_API_R__ +#define SK_FONTMGR_ANDROID_NDK_API_LEVEL __ANDROID_API_R__ -#if __ANDROID_API__ >= SK_ANDROID_NDK_FONT_API_EXISTS +#if __ANDROID_API__ >= SK_FONTMGR_ANDROID_NDK_API_LEVEL #include <android/font.h> #include <android/font_matcher.h> #include <android/system_fonts.h> @@ -76,7 +57,6 @@ using namespace skia_private; #include <cinttypes> #include <memory> -#include <vector> #include <dlfcn.h> @@ -87,58 +67,6 @@ namespace { [[maybe_unused]] static inline const constexpr bool kSkFontMgrVerbose = false; -namespace variation { -using Coordinate = SkFontArguments::VariationPosition::Coordinate; -using Storage = AutoSTArray<4, Coordinate>; - -static constexpr SkFourByteTag wghtTag = SkSetFourByteTag('w','g','h','t'); -static constexpr SkFourByteTag wdthTag = SkSetFourByteTag('w','d','t','h'); -static constexpr SkFourByteTag slntTag = SkSetFourByteTag('s','l','n','t'); -static constexpr SkFourByteTag italTag = SkSetFourByteTag('i','t','a','l'); - -static bool coordinateLess(const Coordinate& a, const Coordinate& b) { - return a.axis != b.axis ? a.axis < b.axis : a.value < b.value; -}; - -static bool coordinateEqual(const Coordinate& a, const Coordinate& b) { - return a.axis == b.axis && a.value == b.value; -}; - -static SkSpan<Coordinate> Get(const SkTypeface& typeface, Storage& storage) { - if (storage.size() < Storage::kCount) { - storage.reset(Storage::kCount); - } - int numAxes = typeface.getVariationDesignPosition(SkSpan(storage)); - if (SkToInt(storage.size()) < numAxes) { - storage.reset(numAxes); - numAxes = typeface.getVariationDesignPosition(SkSpan(storage)); - } - if (numAxes < 0) { - numAxes = 0; - } - - return SkSpan(storage.data(), numAxes); -} - -/* Normalize the values. NaN and -0.0 => 0. - * Should normalize before sorting or comparing. - */ -static SkSpan<Coordinate> Normalize(SkSpan<Coordinate> variation) { - for (auto&& coord : variation) { - if (coord.value == 0 || SkIsNaN(coord.value)) { - coord.value = 0.0f; - } - } - return variation; -} - -static SkSpan<Coordinate> Sort(SkSpan<Coordinate> variation) { - SkTQSort(variation.begin(), variation.end(), variation::coordinateLess); - return variation; -} - -} // namespace variation - struct AndroidFontAPI { ASystemFontIterator* (*ASystemFontIterator_open)(); void (*ASystemFontIterator_close)(ASystemFontIterator*); @@ -153,62 +81,57 @@ struct AndroidFontAPI { size_t (*AFont_getAxisCount)(const AFont*); uint32_t (*AFont_getAxisTag)(const AFont*, uint32_t axisIndex); float (*AFont_getAxisValue)(const AFont*, uint32_t axisIndex); +}; -#if __ANDROID_API__ >= SK_ANDROID_NDK_FONT_API_EXISTS - - static std::optional<AndroidFontAPI> Make() { - static AndroidFontAPI api { - ::ASystemFontIterator_open, - ::ASystemFontIterator_close, - ::ASystemFontIterator_next, - - ::AFont_close, - ::AFont_getFontFilePath, - ::AFont_getWeight, - ::AFont_isItalic, - ::AFont_getLocale, - ::AFont_getCollectionIndex, - ::AFont_getAxisCount, - ::AFont_getAxisTag, - ::AFont_getAxisValue, - }; - if (android_get_device_api_level() < SK_ANDROID_NDK_FONT_API_LOCALE_WORKS) { - return std::nullopt; - } - if constexpr (kSkFontMgrVerbose) { SkDebugf("SKIA: GetAndroidFontAPI direct\n"); } - return api; - } +#if __ANDROID_API__ >= SK_FONTMGR_ANDROID_NDK_API_LEVEL + +static const AndroidFontAPI* GetAndroidFontAPI() { + static AndroidFontAPI androidFontAPI { + ASystemFontIterator_open, + ASystemFontIterator_close, + ASystemFontIterator_next, + + AFont_close, + AFont_getFontFilePath, + AFont_getWeight, + AFont_isItalic, + AFont_getLocale, + AFont_getCollectionIndex, + AFont_getAxisCount, + AFont_getAxisTag, + AFont_getAxisValue, + }; + if constexpr (kSkFontMgrVerbose) { SkDebugf("SKIA: GetAndroidFontAPI direct\n"); } + return &androidFontAPI; +} #else -private: - AndroidFontAPI() {} - std::unique_ptr<void, SkFunctionObject<dlclose>> self; -public: - AndroidFontAPI(const AndroidFontAPI&) = delete; - AndroidFontAPI& operator=(const AndroidFontAPI&) = delete; - AndroidFontAPI(AndroidFontAPI&&) = default; - AndroidFontAPI& operator=(AndroidFontAPI&&) = default; - - static std::optional<AndroidFontAPI> Make() { - if (android_get_device_api_level() < SK_ANDROID_NDK_FONT_API_LOCALE_WORKS) { - return std::nullopt; +static const AndroidFontAPI* GetAndroidFontAPI() { + struct OptionalAndroidFontAPI : AndroidFontAPI { + bool valid = false; + }; + static OptionalAndroidFontAPI androidFontAPI = [](){ + using DLHandle = std::unique_ptr<void, SkFunctionObject<dlclose>>; + OptionalAndroidFontAPI api; + + if (android_get_device_api_level() < SK_FONTMGR_ANDROID_NDK_API_LEVEL) { + return api; } - AndroidFontAPI api; - api.self.reset(dlopen("libandroid.so", RTLD_LAZY | RTLD_LOCAL)); - if (!api.self) { - return std::nullopt; + DLHandle self(dlopen("libandroid.so", RTLD_LAZY | RTLD_LOCAL)); + if (!self) { + return api; } #define SK_DLSYM_ANDROID_FONT_API(NAME) \ do { \ - *(void**)(&api.NAME) = dlsym(api.self.get(), #NAME); \ + *(void**)(&api.NAME) = dlsym(self.get(), #NAME); \ if (!api.NAME) { \ if constexpr (kSkFontMgrVerbose) { \ SkDebugf("SKIA: Failed to load: " #NAME "\n");\ } \ - return std::nullopt; \ + return api; \ } \ } while (0) @@ -225,208 +148,17 @@ public: SK_DLSYM_ANDROID_FONT_API(AFont_getAxisCount); SK_DLSYM_ANDROID_FONT_API(AFont_getAxisTag); SK_DLSYM_ANDROID_FONT_API(AFont_getAxisValue); + #undef SK_DLSYM_ANDROID_FONT_API - if constexpr (kSkFontMgrVerbose) { SkDebugf("SKIA: GetAndroidFontAPI dlsym\n"); } + api.valid = true; return api; - } - -#endif + }(); + if constexpr (kSkFontMgrVerbose) { SkDebugf("SKIA: GetAndroidFontAPI dlsym\n"); } + return androidFontAPI.valid ? &androidFontAPI : nullptr; }; -struct AndroidIcuAPI { - /* The result is case folded UTF-8. This is normalized case, it isn't upper or lower case. */ - SkString casefold(SkSpan<const char> s) const { - return fHasICU ? this->icuCaseFold(s) : this->cToWLower(s); - } - -private: - int32_t (*u_strFoldCase)(UChar *dest, int32_t destCapacity, const UChar *src, int32_t srcLength, - uint32_t options, UErrorCode *pErrorCode) = nullptr; - UChar* (*u_strFromUTF8)(UChar *dest, int32_t destCapacity, int32_t *pDestLength, - const char *src, int32_t srcLength, UErrorCode *pErrorCode) = nullptr; - char * (*u_strToUTF8)(char *dest, int32_t destCapacity, int32_t *pDestLength, - const UChar *src, int32_t srcLength, UErrorCode *pErrorCode) = nullptr; - bool fHasICU = false; - - SkString cToWLower(SkSpan<const char> s) const { - // Find length of result - size_t retLen = 0; - bool isDifferent = false; - const char* src = s.begin(); - while (src != s.end()) { - SkUnichar uni = SkUTF::NextUTF8(&src, s.end()); - if (uni < 0) { - return SkString(s.data(), s.size()); - } - // On Android 2.3 (API 9) and later wchar_t is 32 bit, UTF-32 like. - // towlower only provides simple case folding, but this should be fine for Android 11. - wint_t wlow = towlower(uni); - char buffer[SkUTF::kMaxBytesInUTF8Sequence]; - size_t buflen = SkUTF::ToUTF8(wlow, buffer); - if (buflen == 0) { - return SkString(s.data(), s.size()); - } - isDifferent |= wlow != SkToU32(uni); - retLen += buflen; - } - - // No change needed - if (!isDifferent) { - if constexpr (kSkFontMgrVerbose) { SkDebugf("SKIA: TL \"%s\" unchanged\n", s.data()); } - return SkString(s.data(), s.size()); - } - - // Create result - SkString ret(retLen); - src = s.begin(); - char* dst = ret.begin(); - while (src != s.end()) { - SkUnichar uni = SkUTF::NextUTF8(&src, s.end()); - wint_t wlow = towlower(uni); - char buffer[SkUTF::kMaxBytesInUTF8Sequence]; - size_t buflen = SkUTF::ToUTF8(wlow, buffer); - memcpy(dst, buffer, buflen); - dst += buflen; - } - - if constexpr (kSkFontMgrVerbose) { - SkDebugf("SKIA: TL \"%s\" to \"%s\"\n", s.data(), ret.c_str()); - } - return ret; - } - - SkString icuCaseFold(SkSpan<const char> s) const { - if (!SkTFitsIn<int32_t>(s.size())) { - return SkString(s.data(), s.size()); - } - - using Storage = AutoSTMalloc<32, UChar>; - UErrorCode error = U_ZERO_ERROR; - - // Convert to UTF-16 - int32_t uStrSize = 0; - Storage uStr(Storage::kCount); - this->u_strFromUTF8(uStr, Storage::kCount, &uStrSize, s.data(), s.size(), &error); - if (error == U_BUFFER_OVERFLOW_ERROR) { - error = U_ZERO_ERROR; - } - if (U_FAILURE(error)) { - if constexpr (kSkFontMgrVerbose) { SkDebugf("SKIA: CF UTF-16 length\n"); } - return SkString(s.data(), s.size()); - } - if (SkTo<int32_t>(Storage::kCount) < uStrSize) { - uStr.reset(uStrSize); - int32_t uStrSizePrev = uStrSize; - this->u_strFromUTF8(uStr, uStrSize, &uStrSize, s.data(), s.size(), &error); - if (U_FAILURE(error) || uStrSizePrev < uStrSize) { - if constexpr (kSkFontMgrVerbose) { SkDebugf("SKIA: CF UTF-16 wrong length\n"); } - return SkString(s.data(), s.size()); - } - } - - // Case fold - Storage uStrFolded(Storage::kCount); - int32_t uStrFoldedSize = this->u_strFoldCase(uStrFolded, Storage::kCount, uStr, uStrSize, - U_FOLD_CASE_EXCLUDE_SPECIAL_I, &error); - if (error == U_BUFFER_OVERFLOW_ERROR) { - error = U_ZERO_ERROR; - } - if (U_FAILURE(error)) { - if constexpr (kSkFontMgrVerbose) { SkDebugf("SKIA: CF folded length\n"); } - return SkString(s.data(), s.size()); - } - if (SkTo<int32_t>(Storage::kCount) < uStrFoldedSize) { - uStrFolded.reset(uStrFoldedSize); - int32_t uStrFoldedSizePrev = uStrFoldedSize; - uStrFoldedSize = this->u_strFoldCase(uStrFolded, uStrFoldedSize, uStr, uStrSize, - U_FOLD_CASE_EXCLUDE_SPECIAL_I, &error); - if (U_FAILURE(error) || uStrFoldedSizePrev < uStrFoldedSize) { - if constexpr (kSkFontMgrVerbose) { SkDebugf("SKIA: CF folded wrong length\n"); } - return SkString(s.data(), s.size()); - } - } - - // Convert to UTF-8 - SkString ret; - int32_t retSize = 0; - this->u_strToUTF8(nullptr, 0, &retSize, uStrFolded, uStrFoldedSize, &error); - if (error == U_BUFFER_OVERFLOW_ERROR) { - error = U_ZERO_ERROR; - } - if (U_FAILURE(error)) { - if constexpr (kSkFontMgrVerbose) { SkDebugf("SKIA: CF UTF-8 length\n"); } - return SkString(s.data(), s.size()); - } - ret.resize(retSize); - int32_t retSizePrev = retSize; - this->u_strToUTF8(ret.data(), retSize, &retSize, uStrFolded, uStrFoldedSize, &error); - if (U_FAILURE(error) || retSizePrev != retSize) { - if constexpr (kSkFontMgrVerbose) { SkDebugf("SKIA: CF UTF-8 wrong length\n"); } - return SkString(s.data(), s.size()); - } - - // Return result - if constexpr (kSkFontMgrVerbose) { - SkDebugf("SKIA: CF \"%s\" to \"%s\"\n", s.data(), ret.c_str()); - } - return ret; - } - -#if __ANDROID_API__ >= __ANDROID_API_S__ -public: - AndroidIcuAPI() - : u_strFoldCase(::u_strFoldCase) - , u_strFromUTF8(::u_strFromUTF8) - , u_strToUTF8(::u_strToUTF8) - , fHasICU(true) - { - if constexpr (kSkFontMgrVerbose) { SkDebugf("SKIA: GetAndroidIcuAPI direct\n"); } - } - -#else - -private: - std::unique_ptr<void, SkFunctionObject<dlclose>> fSelf; -public: - AndroidIcuAPI(const AndroidIcuAPI&) = delete; - AndroidIcuAPI& operator=(const AndroidIcuAPI&) = delete; - AndroidIcuAPI(AndroidIcuAPI&&) = default; - AndroidIcuAPI& operator=(AndroidIcuAPI&&) = default; - - AndroidIcuAPI() { - fSelf.reset(dlopen("libicu.so", RTLD_LAZY | RTLD_LOCAL)); - if (!fSelf) { - return; - } - -#define SK_DLSYM_ANDROID_ICU_API(NAME) \ - do { \ - *(void**)(&NAME) = dlsym(fSelf.get(), #NAME); \ - if (!NAME) { \ - if constexpr (kSkFontMgrVerbose) { \ - SkDebugf("SKIA: Failed to load: " #NAME "\n");\ - } \ - return; \ - } \ - } while (0) - - SK_DLSYM_ANDROID_ICU_API(u_strFoldCase); - SK_DLSYM_ANDROID_ICU_API(u_strFromUTF8); - SK_DLSYM_ANDROID_ICU_API(u_strToUTF8); -#undef SK_DLSYM_ANDROID_ICU_API - - fHasICU = true; - if constexpr (kSkFontMgrVerbose) { SkDebugf("SKIA: GetAndroidIcuAPI dlsym\n"); } - } #endif -}; - -// bcp47 is ascii only and only does 1:1 replacements for case folding. -static void normalizeAsciiCase(SkSpan<char> s) { - std::transform(s.begin(), s.end(), s.begin(), - [](char c){ return (c < 'A' || 'Z' < c) ? c : c + 'a' - 'A'; }); -} struct SkAFont { SkAFont(const AndroidFontAPI& api, AFont* font) : fAPI(api), fFont(font) {} @@ -476,133 +208,63 @@ private: ASystemFontIterator* const fIterator; }; -class SkALanguage { +class SkLanguage { public: - SkALanguage() {} - SkALanguage(const char* tag) : SkALanguage(SkSpan<const char>(tag, tag ? strlen(tag) : 0)) {} - SkALanguage(SkSpan<const char> tag) { - fLanguage = consumeSubtag(tag); - if (fLanguage.equals("und", 3)) { - fLanguage = SkString(); - } - fScript = consumeSubtag(tag); - fRegion = consumeSubtag(tag); - } - SkALanguage(const SkALanguage&) = default; - SkALanguage& operator=(const SkALanguage& b) = default; - - SkALanguage lessSpecific() const { - SkALanguage lessSpecific(*this); - if (!lessSpecific.fRegion.isEmpty()) { - lessSpecific.fRegion = SkString(); - return lessSpecific; - } - if (!lessSpecific.fScript.isEmpty()) { - lessSpecific.fScript = SkString(); - return lessSpecific; - } - - if (!lessSpecific.fLanguage.isEmpty()) { - lessSpecific.fLanguage = SkString(); - return lessSpecific; + SkLanguage() { } + SkLanguage(const SkString& tag) : fTag(tag) { } + SkLanguage(const char* tag) : fTag(tag) { } + SkLanguage(const char* tag, size_t len) : fTag(tag, len) { } + SkLanguage(const SkLanguage&) = default; + SkLanguage& operator=(const SkLanguage& b) = default; + + /** Gets a BCP 47 language identifier for this SkLanguage. + @return a BCP 47 language identifier representing this language + */ + const SkString& getTag() const { return fTag; } + + /** Performs BCP 47 fallback to return an SkLanguage one step more general. + @return an SkLanguage one step more general + */ + SkLanguage getParent() const { + SkASSERT(!fTag.isEmpty()); + const char* tag = fTag.c_str(); + + // strip off the rightmost "-.*" + const char* parentTagEnd = strrchr(tag, '-'); + if (parentTagEnd == nullptr) { + return SkLanguage(); } - return lessSpecific; + size_t parentTagLen = parentTagEnd - tag; + return SkLanguage(tag, parentTagLen); } - bool isEmpty() const { - return fLanguage.isEmpty() && fScript.isEmpty() && fRegion.isEmpty(); + bool operator==(const SkLanguage& b) const { + return fTag == b.fTag; } - - bool startsWith(const SkALanguage& that) { - return (that.fLanguage.isEmpty() || fLanguage == that.fLanguage) && - (that.fScript.isEmpty() || fScript == that.fScript) && - (that.fRegion.isEmpty() || fRegion == that.fRegion); + bool operator!=(const SkLanguage& b) const { + return fTag != b.fTag; } - const SkString& getLanguage() const { return fLanguage; } - const SkString& getScript() const { return fScript; } - const SkString& getRegion() const { return fRegion; } - - using sk_is_trivially_relocatable = std::true_type; private: - SkString consumeSubtag(SkSpan<const char>& tag) { - if (tag.size() == 0) { - return SkString(); - } - const char* subtagEnd = static_cast<const char*>(memchr(tag.data(), '-', tag.size())); - if (!subtagEnd) { - SkString ret(tag.data(), tag.size()); - tag = SkSpan<const char>(); - return ret; - } - size_t subtagLength = subtagEnd - tag.data(); - SkString ret(tag.data(), subtagLength); - normalizeAsciiCase({ret.begin(), ret.size()}); - tag = tag.subspan(subtagLength + 1); - return ret; - } - SkString fLanguage; - SkString fScript; - SkString fRegion; - // variants, extensions, and privateuses are ignored as no known font configuration uses them. - static_assert(::sk_is_trivially_relocatable<decltype(fLanguage)>::value); - static_assert(::sk_is_trivially_relocatable<decltype(fScript)>::value); - static_assert(::sk_is_trivially_relocatable<decltype(fRegion)>::value); + //! BCP 47 language identifier + SkString fTag; + static_assert(::sk_is_trivially_relocatable<decltype(fTag)>::value); }; class SkTypeface_AndroidNDK : public SkTypeface_proxy { public: - struct AutoAxis { - static constexpr struct KnownAxis { - SkFourByteTag tag; - int flag; - } kKnownAxis[] = { - { variation::wghtTag, 1 << 0 }, - { variation::wdthTag, 1 << 1 }, - { variation::slntTag, 1 << 2 }, - { variation::italTag, 1 << 3 }, - }; - - AutoAxis() = default; - AutoAxis(const AutoAxis&) = default; - AutoAxis& operator=(const AutoAxis&) = default; - AutoAxis(SkSpan<const SkFontArguments::VariationPosition::Coordinate> pos) { - for (auto&& coord : pos) { - for (auto&& axis : kKnownAxis) { - if (coord.axis == axis.tag) { - fFlags |= axis.flag; - } - } - } - } - - void remove(const AutoAxis& that) { - fFlags &= ~that.fFlags; - } - - bool weight() const { return SkToBool(fFlags & kKnownAxis[0].flag); } - bool width() const { return SkToBool(fFlags & kKnownAxis[1].flag); } - bool slant() const { return SkToBool(fFlags & kKnownAxis[2].flag); } - bool italic() const { return SkToBool(fFlags & kKnownAxis[3].flag); } - bool none() const { return fFlags == 0; } - uint8_t fFlags = 0; - }; - static sk_sp<SkTypeface_AndroidNDK> Make(sk_sp<SkTypeface> realTypeface, + static sk_sp<SkTypeface_AndroidNDK> Make(sk_sp<SkTypeface> realTypeface, const SkFontStyle& style, bool isFixedPitch, const SkString& familyName, - TArray<SkString>&& extraFamilyNames, - TArray<SkALanguage>&& lang, - const AutoAxis& autoAxis) { + TArray<SkLanguage>&& lang) { SkASSERT(realTypeface); return sk_sp<SkTypeface_AndroidNDK>(new SkTypeface_AndroidNDK(std::move(realTypeface), style, isFixedPitch, familyName, - std::move(extraFamilyNames), - std::move(lang), - autoAxis)); + std::move(lang))); } private: @@ -610,14 +272,10 @@ private: const SkFontStyle& style, bool isFixedPitch, const SkString& familyName, - TArray<SkString>&& extraFamilyNames, - TArray<SkALanguage>&& lang, - const AutoAxis& autoAxis) + TArray<SkLanguage>&& lang) : SkTypeface_proxy(std::move(realTypeface), style, isFixedPitch) , fFamilyName(familyName) - , fExtraFamilyNames(std::move(extraFamilyNames)) , fLang(std::move(lang)) - , fAutoAxis(autoAxis) { } void onGetFamilyName(SkString* familyName) const override { @@ -638,16 +296,12 @@ private: if (proxy == nullptr) { return nullptr; } - SkFontStyle style = proxy->fontStyle(); - bool fixedPitch = proxy->isFixedPitch(); return SkTypeface_AndroidNDK::Make( std::move(proxy), - style, - fixedPitch, + this->fontStyle(), + this->isFixedPitch(), fFamilyName, - TArray<SkString>(fExtraFamilyNames), - TArray<SkALanguage>(), - AutoAxis()); + TArray<SkLanguage>()); } SkFontStyle onGetFontStyle() const override { @@ -658,236 +312,14 @@ private: return SkTypeface::onGetFixedPitch(); } - SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override { - class ALocalizedStrings : public SkTypeface::LocalizedStrings { - public: - ALocalizedStrings(sk_sp<SkTypeface_AndroidNDK> typeface, - sk_sp<SkTypeface::LocalizedStrings> base) - : fTypeface(std::move(typeface)) - , fBase(std::move(base)) - , fExtraFamilyName(nullptr) {} - private: - sk_sp<SkTypeface_AndroidNDK> fTypeface; - sk_sp<SkTypeface::LocalizedStrings> fBase; - const SkString* fExtraFamilyName; - - bool next(LocalizedString* localizedString) override { - if (!fExtraFamilyName) { - if (fBase->next(localizedString)) { - return true; - } - fExtraFamilyName = fTypeface->fExtraFamilyNames.begin(); - } - if (fExtraFamilyName == fTypeface->fExtraFamilyNames.end()) { - return false; - } - *localizedString = {*fExtraFamilyName, SkString()}; - ++fExtraFamilyName; - return true; - } - }; - sk_sp<SkTypeface::LocalizedStrings> base(SkTypeface_proxy::onCreateFamilyNameIterator()); - return new ALocalizedStrings(sk_ref_sp(this), std::move(base)); - } - public: const SkString fFamilyName; - const TArray<SkString> fExtraFamilyNames; - const STArray<4, SkALanguage> fLang; - const AutoAxis fAutoAxis; -}; - -class TypefaceCache : public SkRefCnt { -public: - TypefaceCache() : fRequests(64), fMatches(64) {} - ~TypefaceCache() override {} - - class Request { - public: - Request(SkTypefaceID id, SkFontStyle style) : fId(id), fStyle(style) { - SkGoodHash hasher; - fHash = hasher(fId); - fHash ^= hasher(fStyle); - } - bool operator==(const Request& that) const { - return fId == that.fId && fStyle == that.fStyle; - } - struct Hash { uint32_t operator()(const Request& a) { return a.fHash; } }; - private: - const SkTypefaceID fId; - const SkFontStyle fStyle; - uint32_t fHash; - }; - sk_sp<SkTypeface> find(const Request& request) { - SkAutoSharedMutexShared lock(fMutex); - sk_sp<SkTypeface>* typeface = fRequests.find(request); - if (typeface) { - return *typeface; - } - return nullptr; - } - void add(const Request& request, sk_sp<SkTypeface> typeface) { - SkAutoSharedMutexExclusive lock(fMutex); - fRequests.insert_or_update(request, std::move(typeface)); - } - - class Match { - public: - /* variation is expected to be normalized and sorted. */ - Match(SkTypefaceID id, variation::Storage&& variation) - : fId(id) - , fVariation(std::move(variation)) - { - SkGoodHash hasher; - fHash = hasher(id); - for (auto&& coord : fVariation) { - fHash ^= hasher(coord.axis); - fHash ^= hasher(FloatBits(coord.value)); - } - } - Match(const Match&) = delete; - Match& operator=(const Match&) = delete; - Match(Match&& that) : fId(std::move(that.fId)) - , fVariation(std::move(that.fVariation)) - , fHash(std::move(that.fHash)) {} - Match& operator=(Match&&) = delete; - bool operator==(const Match& that) const { - return fId == that.fId && - fVariation.size() == that.fVariation.size() && - std::equal(fVariation.begin(), fVariation.end(), - that.fVariation.begin(), variation::coordinateEqual); - } - struct Hash { uint32_t operator()(const Match& a) { return a.fHash; } }; - private: - static uint32_t FloatBits(float f) { - static_assert(sizeof(uint32_t) == sizeof(float)); - uint32_t bits; - std::memcpy(&bits, &f, sizeof(uint32_t)); - return bits; - } - const SkTypefaceID fId; - variation::Storage fVariation; - uint32_t fHash; - }; - sk_sp<SkTypeface> find(const Match& match) { - SkAutoSharedMutexShared lock(fMutex); - sk_sp<SkTypeface>* typeface = fMatches.find(match); - if (typeface) { - return *typeface; - } - return nullptr; - } - void add(Match&& match, sk_sp<SkTypeface> typeface) { - SkAutoSharedMutexExclusive lock(fMutex); - fMatches.insert_or_update(std::move(match), typeface); - } -private: - SkLRUCache<Request, sk_sp<SkTypeface>, Request::Hash> fRequests; - SkLRUCache<Match, sk_sp<SkTypeface>, Match::Hash> fMatches; - SkSharedMutex fMutex; + const STArray<4, SkLanguage> fLang; }; -sk_sp<SkTypeface> adjustForStyle(sk_sp<SkTypeface_AndroidNDK>&& typeface, SkFontStyle style, - TypefaceCache& cache) { - if (!typeface) { - return std::move(typeface); - } - - SkFontStyle typefaceStyle = typeface->fontStyle(); - if (typefaceStyle == style || typeface->fAutoAxis.none()) { - return std::move(typeface); - } - - SkFontArguments::VariationPosition::Coordinate coord[4]; - int numCoords = 0; - if (typefaceStyle.weight() != style.weight() && typeface->fAutoAxis.weight()) { - coord[numCoords++] = {variation::wghtTag, static_cast<float>(style.weight())}; - } - if (typefaceStyle.width() != style.width() && typeface->fAutoAxis.width()) { - coord[numCoords++] = {variation::wdthTag, - SkFontDescriptor::SkFontWidthAxisValueForStyleWidth(style.width())}; - } - if (typefaceStyle.slant() != style.slant()) { - switch (style.slant()) { - case SkFontStyle::Slant::kItalic_Slant: - if (typeface->fAutoAxis.italic()) { - coord[numCoords++] = {variation::italTag, 1.0}; - } - break; - case SkFontStyle::Slant::kOblique_Slant: - if (typeface->fAutoAxis.slant()) { - coord[numCoords++] = {variation::slntTag, -7.0}; - } - break; - case SkFontStyle::Slant::kUpright_Slant: - if (typeface->fAutoAxis.italic()) { - coord[numCoords++] = {variation::italTag, 0.0}; - } - if (typeface->fAutoAxis.slant()) { - coord[numCoords++] = {variation::slntTag, 0.0}; - } - break; - } - } - if (numCoords == 0) { - return std::move(typeface); - } - - TypefaceCache::Request request(typeface->uniqueID(), style); - if (sk_sp<SkTypeface> cachedTypeface = cache.find(request)) { - if constexpr (kSkFontMgrVerbose) { - SkString familyName; - typeface->getFamilyName(&familyName); - SkFontStyle s = cachedTypeface->fontStyle(); - SkDebugf("Cached request of \"%s\" weight:%d width: %d slant %d\n", - familyName.c_str(), s.weight(), s.width(), s.slant()); - } - return cachedTypeface; - } - - sk_sp<SkTypeface> newTypeface = typeface->makeClone( - SkFontArguments().setVariationDesignPosition({coord, numCoords})); - if (!newTypeface) { - if constexpr (kSkFontMgrVerbose) { - SkString familyName; - typeface->getFamilyName(&familyName); - SkDebugf("Failed to clone \"%s\"\n", familyName.c_str()); - } - return std::move(typeface); - } - - variation::Storage variationStorage; - SkSpan<variation::Coordinate> newVariation = variation::Get(*newTypeface, variationStorage); - variation::Sort(variation::Normalize(newVariation)); - variationStorage.trimTo(newVariation.size()); - TypefaceCache::Match match(typeface->uniqueID(), std::move(variationStorage)); - if (sk_sp<SkTypeface> cachedTypeface = cache.find(match)) { - if constexpr (kSkFontMgrVerbose) { - SkString familyName; - typeface->getFamilyName(&familyName); - SkFontStyle s = cachedTypeface->fontStyle(); - SkDebugf("Cached match of \"%s\" weight:%d width: %d slant %d\n", - familyName.c_str(), s.weight(), s.width(), s.slant()); - } - cache.add(std::move(request), cachedTypeface); - return cachedTypeface; - } - - if constexpr (kSkFontMgrVerbose) { - SkString familyName; - typeface->getFamilyName(&familyName); - SkFontStyle s = newTypeface->fontStyle(); - SkDebugf("New variant of \"%s\" weight:%d width: %d slant %d\n", - familyName.c_str(), s.weight(), s.width(), s.slant()); - } - cache.add(std::move(match), newTypeface); - cache.add(std::move(request), newTypeface); - return newTypeface; -} - class SkFontStyleSet_AndroidNDK : public SkFontStyleSet { public: - explicit SkFontStyleSet_AndroidNDK(sk_sp<TypefaceCache> cache) : fCache(std::move(cache)) {} + explicit SkFontStyleSet_AndroidNDK() { } int count() override { return fStyles.size(); @@ -903,20 +335,17 @@ public: name->reset(); } } - sk_sp<SkTypeface_AndroidNDK> createATypeface(int index) { + sk_sp<SkTypeface> createTypeface(int index) override { if (index < 0 || fStyles.size() <= index) { return nullptr; } return fStyles[index]; } - sk_sp<SkTypeface> createTypeface(int index) override { - return createATypeface(index); - } - sk_sp<SkTypeface_AndroidNDK> matchAStyle(const SkFontStyle& pattern) { + sk_sp<SkTypeface> matchStyle(const SkFontStyle& pattern) override { sk_sp<SkTypeface> match = this->matchStyleCSS3(pattern); - sk_sp<SkTypeface_AndroidNDK> amatch(static_cast<SkTypeface_AndroidNDK*>(match.release())); if constexpr (kSkFontMgrVerbose) { + SkTypeface_AndroidNDK* amatch = static_cast<SkTypeface_AndroidNDK*>(match.get()); SkString name; amatch->getFamilyName(&name); SkString resourceName; @@ -927,15 +356,11 @@ public: name.c_str(), fontStyle.weight(), fontStyle.width(), fontStyle.slant(), resourceName.c_str()); } - return amatch; - } - sk_sp<SkTypeface> matchStyle(const SkFontStyle& pattern) override { - return adjustForStyle(this->matchAStyle(pattern), pattern, *fCache); + return match; } private: TArray<sk_sp<SkTypeface_AndroidNDK>> fStyles; - sk_sp<TypefaceCache> fCache; friend class SkFontMgr_AndroidNDK; }; @@ -953,17 +378,17 @@ struct NameToFamily { class SkFontMgr_AndroidNDK : public SkFontMgr { void addSystemTypeface(sk_sp<SkTypeface_AndroidNDK> typeface, const SkString& name) { NameToFamily* nameToFamily = nullptr; - SkString normalizedName(fICU.casefold({name.data(), name.size()})); for (NameToFamily& current : fNameToFamilyMap) { - if (current.normalizedName == normalizedName) { + if (current.name == name) { nameToFamily = &current; break; } } if (!nameToFamily) { - sk_sp<SkFontStyleSet_AndroidNDK> newSet(new SkFontStyleSet_AndroidNDK(fCache)); + sk_sp<SkFontStyleSet_AndroidNDK> newSet(new SkFontStyleSet_AndroidNDK()); + SkAutoAsciiToLC tolc(name.c_str()); nameToFamily = &fNameToFamilyMap.emplace_back( - NameToFamily{name, normalizedName, newSet.get()}); + NameToFamily{name, SkString(tolc.lc(), tolc.length()), newSet.get()}); fStyleSets.push_back(std::move(newSet)); } if constexpr (kSkFontMgrVerbose) { SkDebugf("SKIA: Adding member to %s\n", name.c_str()); } @@ -971,11 +396,10 @@ class SkFontMgr_AndroidNDK : public SkFontMgr { } public: - SkFontMgr_AndroidNDK(AndroidFontAPI&& fontAPI, bool const cacheFontFiles, - std::unique_ptr<SkFontScanner> scanner) - : fAPI(std::move(fontAPI)) + SkFontMgr_AndroidNDK(const AndroidFontAPI& androidFontAPI, bool const cacheFontFiles, + std::unique_ptr<SkFontScanner> scanner) + : fAPI(androidFontAPI) , fScanner(std::move(scanner)) - , fCache(new TypefaceCache()) { SkASystemFontIterator fontIter(fAPI); if (!fontIter) { @@ -983,14 +407,11 @@ public: return; } - std::vector<std::unique_ptr<FontFamily>> xmlFamilies; - SkFontMgr_Android_Parser::GetSystemFontFamilies(xmlFamilies); - skia_private::THashMap<SkString, std::unique_ptr<SkStreamAsset>> streamForPath; if constexpr (kSkFontMgrVerbose) { SkDebugf("SKIA: Iterating over AFonts\n"); } while (SkAFont font = fontIter.next()) { - sk_sp<SkTypeface_AndroidNDK> typeface = this->make(font, xmlFamilies, streamForPath); + sk_sp<SkTypeface_AndroidNDK> typeface = this->make(std::move(font), streamForPath); if (!typeface) { continue; } @@ -1007,6 +428,9 @@ public: this->addSystemTypeface(typeface, localeName.fString); } } + + // There nothing in the NDK to indicate how to handle generic font names like 'serif', + // 'sans-serif`, 'monospace', etc. } if (fStyleSets.empty()) { @@ -1039,109 +463,31 @@ protected: return sk_ref_sp(fNameToFamilyMap[index].styleSet); } - sk_sp<SkFontStyleSet_AndroidNDK> onMatchAFamily(const char familyName[]) const { + sk_sp<SkFontStyleSet> onMatchFamily(const char familyName[]) const override { if (!familyName) { return nullptr; } - SkString normalizedFamilyName(fICU.casefold({familyName, strlen(familyName)})); - for (const NameToFamily& nameToFamily : fNameToFamilyMap) { - if (nameToFamily.normalizedName == normalizedFamilyName) { - return sk_ref_sp(nameToFamily.styleSet); + SkAutoAsciiToLC tolc(familyName); + for (int i = 0; i < fNameToFamilyMap.size(); ++i) { + if (fNameToFamilyMap[i].normalizedName.equals(tolc.lc())) { + return sk_ref_sp(fNameToFamilyMap[i].styleSet); } } return nullptr; } - sk_sp<SkFontStyleSet> onMatchFamily(const char familyName[]) const override { - return this->onMatchAFamily(familyName); - } sk_sp<SkTypeface> onMatchFamilyStyle(const char familyName[], const SkFontStyle& style) const override { - sk_sp<SkFontStyleSet_AndroidNDK> sset(this->onMatchAFamily(familyName)); + sk_sp<SkFontStyleSet> sset(this->onMatchFamily(familyName)); if (!sset) { return nullptr; } return sset->matchStyle(style); } - static TArray<SkString> GetExtraFamilyNames( - const SkAFont& font, const SkTypeface& typeface, - const SkSpan<const std::unique_ptr<FontFamily>> xmlFamilies) - { - // The NDK does not report aliases like 'serif', 'sans-serif`, 'monospace', etc. - // If a font matches an entry in fonts.xml, add the fonts.xml family name as well. - - // In Android <= 14 AFont reports the variation as specified in fonts.xml. - // In Android >= 15 AFont only reports fixed axes. - variation::Storage variationStorage; - SkSpan<variation::Coordinate> variation = variation::Get(typeface, variationStorage); - variation::Sort(variation::Normalize(variation)); - - variation::Storage xmlVariationStorage; - - TArray<SkString> extraFamilyNames; - for (const std::unique_ptr<FontFamily>& xmlFamily : xmlFamilies) { - if (xmlFamily->fNames.empty()) { - continue; - } - - for (const FontFileInfo& xmlFont : xmlFamily->fFonts) { - SkString pathName(xmlFamily->fBasePath); - pathName.append(xmlFont.fFileName); - if (!pathName.equals(font.getFontFilePath())) { - continue; - } - - if (font.getCollectionIndex() != static_cast<size_t>(xmlFont.fIndex)) { - continue; - } - - if (!xmlFont.fTypeface) { - xmlFont.fTypeface = typeface.makeClone(SkFontArguments() - .setCollectionIndex(xmlFont.fIndex) - .setVariationDesignPosition(SkFontArguments::VariationPosition{ - xmlFont.fVariationDesignPosition.data(), - xmlFont.fVariationDesignPosition.size() - }) - ); - } - if (!xmlFont.fTypeface) { - SkDEBUGFAIL("Cannot create clone."); - continue; - } - - SkSpan<variation::Coordinate> xmlVariation = - variation::Get(*xmlFont.fTypeface, xmlVariationStorage); - if (variation.size() != xmlVariation.size()) { - SkDEBUGFAIL("Clone does not have same number of axes."); - continue; - } - - variation::Sort(variation::Normalize(xmlVariation)); - if (!std::equal(variation.begin(), variation.end(), - xmlVariation.begin(), variation::coordinateEqual)) - { - continue; - } - - if (xmlFont.fWeight != 0 && xmlFont.fWeight != font.getWeight()) { - continue; - } - - for (auto&& xmlName : xmlFamily->fNames) { - extraFamilyNames.push_back(xmlName); - } - } - } - return extraFamilyNames; - } - - sk_sp<SkTypeface_AndroidNDK> make( - const SkAFont& font, - const SkSpan<const std::unique_ptr<FontFamily>> xmlFamilies, - skia_private::THashMap<SkString, std::unique_ptr<SkStreamAsset>>& streamForPath) const - { + sk_sp<SkTypeface_AndroidNDK> make(SkAFont font, skia_private::THashMap<SkString, + std::unique_ptr<SkStreamAsset>>& streamForPath) const { SkString filePath(font.getFontFilePath()); std::unique_ptr<SkStreamAsset>* streamPtr = streamForPath.find(filePath); @@ -1171,18 +517,20 @@ protected: return nullptr; } + constexpr SkFourByteTag wdth = SkSetFourByteTag('w','d','t','h'); size_t requestAxisCount = font.getAxisCount(); if (!SkTFitsIn<int>(requestAxisCount)) { if constexpr (kSkFontMgrVerbose) { SkDebugf("SKIA: Axis count unreasonable!"); } return nullptr; } - variation::Storage requestAxisValues(requestAxisCount); + using Coordinate = SkFontArguments::VariationPosition::Coordinate; + AutoSTMalloc<4, Coordinate> requestAxisValues(requestAxisCount); std::optional<int> 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 == variation::wdthTag) { + if (tag == wdth) { // Set the width based on the requested `wdth` axis value. requestedWidth = SkFontDescriptor::SkFontStyleWidthForWidthAxisValue(value); } @@ -1202,12 +550,6 @@ protected: } return nullptr; } - variation::Storage variationStorage; - SkSpan<variation::Coordinate> variation = variation::Get(*proxy, variationStorage); - SkTypeface_AndroidNDK::AutoAxis autoAxis(variation); - autoAxis.remove(SkTypeface_AndroidNDK::AutoAxis(SkSpan(requestedPosition.coordinates, - requestedPosition.coordinateCount))); - SkFontStyle style = proxy->fontStyle(); int weight = SkTo<int>(font.getWeight()); SkFontStyle::Slant slant = style.slant(); @@ -1219,47 +561,11 @@ protected: // The family name(s) are not reported. // This would be very helpful for aliases, like "sans-serif", "Arial", etc. - TArray<SkString> extraFamilyNames = GetExtraFamilyNames(font, *proxy, xmlFamilies); - SkString familyName; proxy->getFamilyName(&familyName); - STArray<4, SkALanguage> skLangs; + STArray<4, SkLanguage> skLangs; const char* aLangs = font.getLocale(); - { - SkString postscriptName; - proxy->getPostScriptName(&postscriptName); - - // HACK: For backwards compatibility NotoSansSymbols-Regular-Subsetted needs "und-Zsym". - // Base Android appears to hack this into its fallback list for similar reasons. - static constexpr char kNotoSansSymbols[] = "NotoSansSymbols-Regular-Subsetted"; - if (postscriptName.equals(kNotoSansSymbols, std::size(kNotoSansSymbols)-1) && - (!aLangs || aLangs[0] == '\0') && - proxy->unicharToGlyph(0x2603) != 0) - { - if constexpr (kSkFontMgrVerbose) { - SkDebugf("SKIA: Hacking in und-Zsym for NotoSansSymbols-Regular-Subsetted\n"); - } - aLangs = "und-Zsym"; - } - - // HACK: Some Android versions have a variable Roboto font named Roboto but also use - // a font named RobotoStatic (which does not claim to be Roboto) for the 400 weight. - // If RobotoStatic is found but does not have the name "Roboto", add it. - // Fixed in U "[2nd attempt] Revive use of VF font for regular style of roboto font" - // https://android.googlesource.com/platform/frameworks/base/+/89abe560d722a6f4136b7a05d80f23b269413aad - static constexpr char kRobotoStatic[] = "RobotoStatic-Regular"; - static constexpr char kRoboto[] = "Roboto"; - if (postscriptName.equals(kRobotoStatic, std::size(kRobotoStatic)-1) && - std::none_of(extraFamilyNames.begin(), extraFamilyNames.end(), - [](SkString& n){return n.equals(kRoboto, std::size(kRoboto)-1);})) - { - if constexpr (kSkFontMgrVerbose) { - SkDebugf("SKIA: Hacking in Roboto for RobotoStatic-Regular\n"); - } - extraFamilyNames.push_back(SkString(kRoboto, std::size(kRoboto)-1)); - } - } if (aLangs) { if constexpr (kSkFontMgrVerbose) { SkDebugf("SKIA: %s ALangs %s\n", familyName.c_str(), aLangs); @@ -1273,7 +579,7 @@ protected: } const size_t size = end - begin; if (size) { - skLangs.emplace_back(SkSpan(begin, size)); + skLangs.emplace_back(begin, size); } if (*end == '\0') { break; @@ -1284,11 +590,7 @@ protected: } if constexpr (kSkFontMgrVerbose) { for (auto&& lang : skLangs) { - SkDebugf("SKIA: %s Lang %s Script %s Region %s\n", - familyName.c_str(), - lang.getLanguage().c_str(), - lang.getScript().c_str(), - lang.getRegion().c_str()); + SkDebugf("SKIA: %s Lang %s\n", familyName.c_str(), lang.getTag().c_str()); } } @@ -1298,19 +600,18 @@ protected: } return SkTypeface_AndroidNDK::Make( - proxy, style, proxy->isFixedPitch(), - familyName, std::move(extraFamilyNames), std::move(skLangs), autoAxis); + proxy, style, proxy->isFixedPitch(), familyName, std::move(skLangs)); } static bool has_locale_and_character(SkTypeface_AndroidNDK* face, - const SkALanguage& langTag, + const SkString& langTag, SkUnichar character, const char* scope, size_t* step) { ++*step; if (!langTag.isEmpty() && - std::none_of(face->fLang.begin(), face->fLang.end(), [&](SkALanguage lang) { - return lang.startsWith(langTag); + std::none_of(face->fLang.begin(), face->fLang.end(), [&](SkLanguage lang) { + return lang.getTag().startsWith(langTag.c_str()); })) { return false; @@ -1323,11 +624,8 @@ protected: if constexpr (kSkFontMgrVerbose) { SkString foundName; face->getFamilyName(&foundName); - SkDebugf("SKIA: Found U+%" PRIx32 " in \"%s\" lang \"%s\" " - "script \"%s\" region \"%s\" scope %s step %zu.\n", - character, foundName.c_str(), langTag.getLanguage().c_str(), - langTag.getScript().c_str(), langTag.getRegion().c_str(), scope, *step); - + SkDebugf("SKIA: Found U+%" PRIx32 " in \"%s\" lang \"%s\" scope %s step %zu.\n", + character, foundName.c_str(), langTag.c_str(), scope, *step); } return true; } @@ -1335,7 +633,7 @@ protected: sk_sp<SkTypeface> findByCharacterLocaleFamily( SkTypeface_AndroidNDK* familyFace, const SkFontStyle& style, - const SkALanguage& langTag, + const SkString& langTag, SkUnichar character) const { size_t step = 0; @@ -1345,10 +643,12 @@ protected: } // Look through the styles that match in each family. - for (const NameToFamily& nameToFamily : fNameToFamilyMap) { - sk_sp<SkTypeface_AndroidNDK> face(nameToFamily.styleSet->matchAStyle(style)); - if (has_locale_and_character(face.get(), langTag, character, "style", &step)) { - return adjustForStyle(std::move(face), style, *fCache); + for (int i = 0; i < fNameToFamilyMap.size(); ++i) { + SkFontStyleSet_AndroidNDK* family = fNameToFamilyMap[i].styleSet; + sk_sp<SkTypeface> face(family->matchStyle(style)); + auto aface = static_cast<SkTypeface_AndroidNDK*>(face.get()); + if (has_locale_and_character(aface, langTag, character, "style", &step)) { + return face; } } @@ -1364,10 +664,13 @@ protected: // While Android internally depends on all fonts in a family having the same characters // mapped, this cannot be relied upon when guessing at the families by name. - for (const NameToFamily& nameToFamily : fNameToFamilyMap) { - for (const sk_sp<SkTypeface_AndroidNDK>& face : nameToFamily.styleSet->fStyles) { - if (has_locale_and_character(face.get(), langTag, character, "anything", &step)) { - return adjustForStyle(sk_sp(face), style, *fCache); + for (int i = 0; i < fNameToFamilyMap.size(); ++i) { + SkFontStyleSet_AndroidNDK* family = fNameToFamilyMap[i].styleSet; + for (int j = 0; j < family->count(); ++j) { + sk_sp<SkTypeface> face(family->createTypeface(j)); + auto aface = static_cast<SkTypeface_AndroidNDK*>(face.get()); + if (has_locale_and_character(aface, langTag, character, "anything", &step)) { + return face; } } } @@ -1390,28 +693,20 @@ protected: afamilyFace = static_cast<SkTypeface_AndroidNDK*>(familyFace.get()); } - SkSpan langtags(bcp47, bcp47Count); - for (auto&& langtag = langtags.rbegin(); langtag != langtags.rend(); ++langtag) { - SkALanguage lang(*langtag); - if constexpr (kSkFontMgrVerbose) { - SkDebugf("SKIA: Matching against %s Lang %s Script %s Region %s\n", - familyName ? familyName : "", - lang.getLanguage().c_str(), - lang.getScript().c_str(), - lang.getRegion().c_str()); - } - while (!lang.isEmpty()) { + for (int bcp47Index = bcp47Count; bcp47Index --> 0;) { + SkLanguage lang(bcp47[bcp47Index]); + while (!lang.getTag().isEmpty()) { sk_sp<SkTypeface> typeface = - findByCharacterLocaleFamily(afamilyFace, style, lang, character); + findByCharacterLocaleFamily(afamilyFace, style, lang.getTag(), character); if (typeface) { return typeface; } - lang = lang.lessSpecific(); + lang = lang.getParent(); } } sk_sp<SkTypeface> typeface = - findByCharacterLocaleFamily(afamilyFace, style, SkALanguage(), character); + findByCharacterLocaleFamily(afamilyFace, style, SkString(), character); if (typeface) { return typeface; } @@ -1459,15 +754,13 @@ protected: private: - AndroidFontAPI fAPI; - AndroidIcuAPI fICU; + const AndroidFontAPI& fAPI; std::unique_ptr<SkFontScanner> fScanner; - TArray<NameToFamily> fNameToFamilyMap; TArray<sk_sp<SkFontStyleSet_AndroidNDK>> fStyleSets; sk_sp<SkFontStyleSet> fDefaultStyleSet; - sk_sp<TypefaceCache> fCache; + TArray<NameToFamily> fNameToFamilyMap; void findDefaultStyleSet() { SkASSERT(!fStyleSets.empty()); @@ -1488,12 +781,16 @@ private: } // namespace +sk_sp<SkFontMgr> SkFontMgr_New_AndroidNDK(bool cacheFontFiles) { + return SkFontMgr_New_AndroidNDK(cacheFontFiles, SkFontScanner_Make_FreeType()); +} + sk_sp<SkFontMgr> SkFontMgr_New_AndroidNDK(bool cacheFontFiles, - std::unique_ptr<SkFontScanner> scanner) + std::unique_ptr<SkFontScanner> scanner) { - std::optional<AndroidFontAPI> fontAPI = AndroidFontAPI::Make(); - if (!fontAPI) { + AndroidFontAPI const * const androidFontAPI = GetAndroidFontAPI(); + if (!androidFontAPI) { return nullptr; } - return sk_sp(new SkFontMgr_AndroidNDK(*std::move(fontAPI), cacheFontFiles, std::move(scanner))); + return sk_sp(new SkFontMgr_AndroidNDK(*androidFontAPI, cacheFontFiles, std::move(scanner))); } diff --git a/gfx/skia/skia/src/ports/SkFontMgr_android_parser.cpp b/gfx/skia/skia/src/ports/SkFontMgr_android_parser.cpp @@ -24,7 +24,6 @@ #include <string.h> #include <memory> -#include <vector> using namespace skia_private; @@ -97,7 +96,7 @@ struct TagHandler { /** Represents the current parsing state. */ struct FamilyData { - FamilyData(XML_Parser parser, std::vector<std::unique_ptr<FontFamily>>& families, + FamilyData(XML_Parser parser, SkTDArray<FontFamily*>& families, const SkString& basePath, bool isFallback, const char* filename, const TagHandler* topLevelHandler) : fParser(parser) @@ -114,7 +113,7 @@ struct FamilyData { { } XML_Parser fParser; // The expat parser doing the work, owned by caller - std::vector<std::unique_ptr<FontFamily>>& fFamilies; // Collection of families, owned by caller + SkTDArray<FontFamily*>& fFamilies; // The array to append families, owned by caller std::unique_ptr<FontFamily> fCurrentFamily; // The family being created, owned by this FontFileInfo* fCurrentFontInfo; // The info being created, owned by fCurrentFamily int fVersion; // The version of the file parsed. @@ -322,7 +321,7 @@ static const TagHandler familyHandler = { } }, /*end*/[](FamilyData* self, const char* tag) { - self->fFamilies.push_back(std::move(self->fCurrentFamily)); + *self->fFamilies.append() = self->fCurrentFamily.release(); }, /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* { size_t len = strlen(tag); @@ -335,10 +334,11 @@ static const TagHandler familyHandler = { }; static FontFamily* find_family(FamilyData* self, const SkString& familyName) { - for (std::unique_ptr<FontFamily>& candidate : self->fFamilies) { - for (const SkString& candidateName : candidate->fNames) { - if (candidateName == familyName) { - return &*candidate; + for (int i = 0; i < self->fFamilies.size(); i++) { + FontFamily* candidate = self->fFamilies[i]; + for (int j = 0; j < candidate->fNames.size(); j++) { + if (candidate->fNames[j] == familyName) { + return candidate; } } } @@ -381,8 +381,7 @@ static const TagHandler aliasHandler = { } if (weight) { - std::unique_ptr<FontFamily> family( - new FontFamily(targetFamily->fBasePath, self->fIsFallback)); + FontFamily* family = new FontFamily(targetFamily->fBasePath, self->fIsFallback); family->fNames.push_back().set(aliasName); for (int i = 0; i < targetFamily->fFonts.size(); i++) { @@ -390,7 +389,7 @@ static const TagHandler aliasHandler = { family->fFonts.push_back(targetFamily->fFonts[i]); } } - self->fFamilies.push_back(std::move(family)); + *self->fFamilies.append() = family; } else { targetFamily->fNames.push_back().set(aliasName); } @@ -529,7 +528,7 @@ static const TagHandler familyHandler = { } }, /*end*/[](FamilyData* self, const char* tag) { - self->fFamilies.push_back(std::move(self->fCurrentFamily)); + *self->fFamilies.append() = self->fCurrentFamily.release(); }, /*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* { size_t len = strlen(tag); @@ -652,8 +651,7 @@ static const XML_Memory_Handling_Suite sk_XML_alloc = { * This function parses the given filename and stores the results in the given * families array. Returns the version of the file, negative if the file does not exist. */ -static int parse_config_file(const char* filename, - std::vector<std::unique_ptr<FontFamily>>& families, +static int parse_config_file(const char* filename, SkTDArray<FontFamily*>& families, const SkString& basePath, bool isFallback) { SkFILEStream file(filename); @@ -710,10 +708,10 @@ static int parse_config_file(const char* filename, } /** Returns the version of the system font file actually found, negative if none. */ -static int append_system_font_families(std::vector<std::unique_ptr<FontFamily>>& fontFamilies, +static int append_system_font_families(SkTDArray<FontFamily*>& fontFamilies, const SkString& basePath) { - size_t initialCount = fontFamilies.size(); + int initialCount = fontFamilies.size(); int version = parse_config_file(LMP_SYSTEM_FONTS_FILE, fontFamilies, basePath, false); if (version < 0 || fontFamilies.size() == initialCount) { version = parse_config_file(OLD_SYSTEM_FONTS_FILE, fontFamilies, basePath, false); @@ -728,10 +726,9 @@ static int append_system_font_families(std::vector<std::unique_ptr<FontFamily>>& * directory for those files,add all of their entries to the fallback chain, and * include the locale as part of each entry. */ -static void append_fallback_font_families_for_locale( - std::vector<std::unique_ptr<FontFamily>>& fallbackFonts, - const char* dir, - const SkString& basePath) +static void append_fallback_font_families_for_locale(SkTDArray<FontFamily*>& fallbackFonts, + const char* dir, + const SkString& basePath) { SkOSFile::Iter iter(dir, nullptr); SkString fileName; @@ -756,19 +753,19 @@ static void append_fallback_font_families_for_locale( SkString absoluteFilename; absoluteFilename.printf("%s/%s", dir, fileName.c_str()); - std::vector<std::unique_ptr<FontFamily>> langSpecificFonts; + SkTDArray<FontFamily*> langSpecificFonts; parse_config_file(absoluteFilename.c_str(), langSpecificFonts, basePath, true); - for (std::unique_ptr<FontFamily>& family : langSpecificFonts) { + for (int i = 0; i < langSpecificFonts.size(); ++i) { + FontFamily* family = langSpecificFonts[i]; family->fLanguages.emplace_back(locale); - fallbackFonts.push_back(std::move(family)); + *fallbackFonts.append() = family; } } } -static void append_system_fallback_font_families( - std::vector<std::unique_ptr<FontFamily>>& fallbackFonts, - const SkString& basePath) +static void append_system_fallback_font_families(SkTDArray<FontFamily*>& fallbackFonts, + const SkString& basePath) { parse_config_file(FALLBACK_FONTS_FILE, fallbackFonts, basePath, true); append_fallback_font_families_for_locale(fallbackFonts, @@ -776,11 +773,10 @@ static void append_system_fallback_font_families( basePath); } -static void mixin_vendor_fallback_font_families( - std::vector<std::unique_ptr<FontFamily>>& fallbackFonts, - const SkString& basePath) +static void mixin_vendor_fallback_font_families(SkTDArray<FontFamily*>& fallbackFonts, + const SkString& basePath) { - std::vector<std::unique_ptr<FontFamily>> vendorFonts; + SkTDArray<FontFamily*> vendorFonts; parse_config_file(VENDOR_FONTS_FILE, vendorFonts, basePath, true); append_fallback_font_families_for_locale(vendorFonts, LOCALE_FALLBACK_FONTS_VENDOR_DIR, @@ -789,30 +785,28 @@ static void mixin_vendor_fallback_font_families( // This loop inserts the vendor fallback fonts in the correct order in the // overall fallbacks list. int currentOrder = -1; - for (std::unique_ptr<FontFamily>& family : vendorFonts) { + for (int i = 0; i < vendorFonts.size(); ++i) { + FontFamily* family = vendorFonts[i]; int order = family->fOrder; if (order < 0) { if (currentOrder < 0) { // Default case - just add it to the end of the fallback list - fallbackFonts.push_back(std::move(family)); + *fallbackFonts.append() = family; } else { // no order specified on this font, but we're incrementing the order // based on an earlier order insertion request - fallbackFonts.insert(fallbackFonts.begin() + currentOrder, std::move(family)); - ++currentOrder; + *fallbackFonts.insert(currentOrder++) = family; } } else { // Add the font into the fallback list in the specified order. Set // currentOrder for correct placement of other fonts in the vendor list. - fallbackFonts.insert(fallbackFonts.begin() + order, std::move(family)); + *fallbackFonts.insert(order) = family; currentOrder = order + 1; } } } -void SkFontMgr_Android_Parser::GetSystemFontFamilies( - std::vector<std::unique_ptr<FontFamily>>& fontFamilies) -{ +void SkFontMgr_Android_Parser::GetSystemFontFamilies(SkTDArray<FontFamily*>& fontFamilies) { // Version 21 of the system font configuration does not need any fallback configuration files. SkString basePath(getenv("ANDROID_ROOT")); basePath.append(SK_FONT_FILE_PREFIX, sizeof(SK_FONT_FILE_PREFIX) - 1); @@ -822,20 +816,17 @@ void SkFontMgr_Android_Parser::GetSystemFontFamilies( } // Append all the fallback fonts to system fonts - std::vector<std::unique_ptr<FontFamily>> fallbackFonts; + SkTDArray<FontFamily*> fallbackFonts; append_system_fallback_font_families(fallbackFonts, basePath); mixin_vendor_fallback_font_families(fallbackFonts, basePath); - fontFamilies.insert(fontFamilies.end(), - std::make_move_iterator(fallbackFonts.begin()), - std::make_move_iterator(fallbackFonts.end())); + fontFamilies.append(fallbackFonts.size(), fallbackFonts.begin()); } -void SkFontMgr_Android_Parser::GetCustomFontFamilies( - std::vector<std::unique_ptr<FontFamily>>& fontFamilies, - const SkString& basePath, - const char* fontsXml, - const char* fallbackFontsXml, - const char* langFallbackFontsDir) +void SkFontMgr_Android_Parser::GetCustomFontFamilies(SkTDArray<FontFamily*>& fontFamilies, + const SkString& basePath, + const char* fontsXml, + const char* fallbackFontsXml, + const char* langFallbackFontsDir) { if (fontsXml) { parse_config_file(fontsXml, fontFamilies, basePath, false); diff --git a/gfx/skia/skia/src/ports/SkFontMgr_android_parser.h b/gfx/skia/skia/src/ports/SkFontMgr_android_parser.h @@ -10,9 +10,7 @@ #include "include/core/SkFontArguments.h" #include "include/core/SkFontMgr.h" -#include "include/core/SkRefCnt.h" #include "include/core/SkString.h" -#include "include/core/SkTypeface.h" #include "include/core/SkTypes.h" #include "include/private/base/SkTArray.h" #include "include/private/base/SkTDArray.h" @@ -20,7 +18,6 @@ #include <climits> #include <limits> -#include <vector> /** \class SkLanguage @@ -77,7 +74,6 @@ struct FontFileInfo { enum class Style { kAuto, kNormal, kItalic } fStyle; skia_private::TArray<SkFontArguments::VariationPosition::Coordinate, true> fVariationDesignPosition; - mutable sk_sp<SkTypeface> fTypeface; }; /** @@ -109,10 +105,10 @@ struct FontFamily { namespace SkFontMgr_Android_Parser { /** Parses system font configuration files and appends result to fontFamilies. */ -void GetSystemFontFamilies(std::vector<std::unique_ptr<FontFamily>>& fontFamilies); +void GetSystemFontFamilies(SkTDArray<FontFamily*>& fontFamilies); /** Parses font configuration files and appends result to fontFamilies. */ -void GetCustomFontFamilies(std::vector<std::unique_ptr<FontFamily>>& fontFamilies, +void GetCustomFontFamilies(SkTDArray<FontFamily*>& fontFamilies, const SkString& basePath, const char* fontsXml, const char* fallbackFontsXml, diff --git a/gfx/skia/skia/src/ports/SkFontMgr_fontconfig.cpp b/gfx/skia/skia/src/ports/SkFontMgr_fontconfig.cpp @@ -4,12 +4,10 @@ * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ -#include "include/ports/SkFontMgr_fontconfig.h" #include "include/core/SkDataTable.h" #include "include/core/SkFontArguments.h" #include "include/core/SkFontMgr.h" -#include "include/core/SkFontScanner.h" #include "include/core/SkFontStyle.h" #include "include/core/SkMatrix.h" #include "include/core/SkRefCnt.h" @@ -18,6 +16,8 @@ #include "include/core/SkString.h" #include "include/core/SkTypeface.h" #include "include/core/SkTypes.h" +#include "include/ports/SkFontMgr_fontconfig.h" +#include "include/ports/SkFontScanner_FreeType.h" #include "include/private/base/SkDebug.h" #include "include/private/base/SkMutex.h" #include "include/private/base/SkTArray.h" @@ -74,7 +74,7 @@ namespace { // FontConfig was thread antagonistic until 2.10.91 with known thread safety issues until 2.13.93. // Before that, lock with a global mutex. -// See skbug.com/40032593 and cl/339089311 for background. +// See https://bug.skia.org/1497 and cl/339089311 for background. static SkMutex& f_c_mutex() { static SkMutex& mutex = *(new SkMutex); return mutex; @@ -480,7 +480,9 @@ protected: -fcMatrix->yx, fcMatrix->yy, 0, 0 , 0 , 1); - SkMatrix sm = rec->getMatrixFrom2x2(); + SkMatrix sm; + rec->getMatrixFrom2x2(&sm); + sm.preConcat(fm); rec->fPost2x2[0][0] = sm.getScaleX(); rec->fPost2x2[0][1] = sm.getSkewX(); @@ -992,3 +994,7 @@ protected: sk_sp<SkFontMgr> SkFontMgr_New_FontConfig(FcConfig* fc, std::unique_ptr<SkFontScanner> scanner) { return sk_make_sp<SkFontMgr_fontconfig>(fc, std::move(scanner)); } + +sk_sp<SkFontMgr> SkFontMgr_New_FontConfig(FcConfig* fc) { + return sk_make_sp<SkFontMgr_fontconfig>(fc, SkFontScanner_Make_FreeType()); +} diff --git a/gfx/skia/skia/src/ports/SkFontMgr_fuchsia.cpp b/gfx/skia/skia/src/ports/SkFontMgr_fuchsia.cpp @@ -519,9 +519,7 @@ sk_sp<SkTypeface> SkFontMgr_Fuchsia::GetOrCreateTypeface(TypefaceId id, if (!data) return nullptr; auto result = CreateTypefaceFromSkData(std::move(data), id); - if (result) { - fTypefaceCache.add(result); - } + fTypefaceCache.add(result); return result; } diff --git a/gfx/skia/skia/src/ports/SkFontScanner_fontations.cpp b/gfx/skia/skia/src/ports/SkFontScanner_fontations.cpp @@ -167,7 +167,8 @@ bool SkFontScanner_Fontations::scanInstance(SkStreamAsset* stream, axes->reset(size); auto variationAxes = std::make_unique<SkFontParameters::Variation::Axis[]>(size); sk_fontations::AxisWrapper axisWrapper(variationAxes.get(), size); - SkASSERT(size == fontations_ffi::populate_axes(*bridgeFontNamedInstanceRef, axisWrapper)); + auto size1 = fontations_ffi::populate_axes(*bridgeFontNamedInstanceRef, axisWrapper); + SkASSERT(size == size1); for (auto i = 0; i < size; ++i) { const auto var = variationAxes[i]; (*axes)[i].tag = var.tag; diff --git a/gfx/skia/skia/src/ports/SkImageEncoder_NDK.cpp b/gfx/skia/skia/src/ports/SkImageEncoder_NDK.cpp @@ -70,11 +70,6 @@ bool Encode(SkWStream* dst, const SkPixmap& src, const Options& options) { return write_image_to_stream(dst, src, ANDROID_BITMAP_COMPRESS_FORMAT_PNG, 100); } -sk_sp<SkData> Encode(const SkPixmap& src, const Options& options) { - SkDynamicMemoryWStream stream; - return Encode(&stream, src, options) ? stream.detachAsData() : nullptr; -} - sk_sp<SkData> Encode(GrDirectContext* ctx, const SkImage* img, const Options& options) { if (!img) { return nullptr; @@ -83,7 +78,11 @@ sk_sp<SkData> Encode(GrDirectContext* ctx, const SkImage* img, const Options& op if (!as_IB(img)->getROPixels(ctx, &bm)) { return nullptr; } - return Encode(bm.pixmap(), options); + SkDynamicMemoryWStream stream; + if (Encode(&stream, bm.pixmap(), options)) { + return stream.detachAsData(); + } + return nullptr; } } // namespace SkPngEncoder @@ -93,11 +92,6 @@ bool Encode(SkWStream* dst, const SkPixmap& src, const Options& options) { return write_image_to_stream(dst, src, ANDROID_BITMAP_COMPRESS_FORMAT_JPEG, options.fQuality); } -sk_sp<SkData> Encode(const SkPixmap& src, const Options& options) { - SkDynamicMemoryWStream stream; - return Encode(&stream, src, options) ? stream.detachAsData() : nullptr; -} - bool Encode(SkWStream*, const SkYUVAPixmaps&, const SkColorSpace*, const Options&) { SkDEBUGFAIL("encoding a YUVA pixmap is not supported via the NDK"); return false; @@ -111,7 +105,11 @@ sk_sp<SkData> Encode(GrDirectContext* ctx, const SkImage* img, const Options& op if (!as_IB(img)->getROPixels(ctx, &bm)) { return nullptr; } - return Encode(bm.pixmap(), options); + SkDynamicMemoryWStream stream; + if (Encode(&stream, bm.pixmap(), options)) { + return stream.detachAsData(); + } + return nullptr; } std::unique_ptr<SkEncoder> Make(SkWStream*, const SkPixmap&, const Options&) { @@ -140,11 +138,6 @@ bool Encode(SkWStream* dst, const SkPixmap& src, const Options& options) { dst, src, ANDROID_BITMAP_COMPRESS_FORMAT_WEBP_LOSSY, options.fQuality); } -sk_sp<SkData> Encode(const SkPixmap& src, const Options& options) { - SkDynamicMemoryWStream stream; - return Encode(&stream, src, options) ? stream.detachAsData() : nullptr; -} - sk_sp<SkData> Encode(GrDirectContext* ctx, const SkImage* img, const Options& options) { if (!img) { return nullptr; @@ -153,7 +146,11 @@ sk_sp<SkData> Encode(GrDirectContext* ctx, const SkImage* img, const Options& op if (!as_IB(img)->getROPixels(ctx, &bm)) { return nullptr; } - return Encode(bm.pixmap(), options); + SkDynamicMemoryWStream stream; + if (Encode(&stream, bm.pixmap(), options)) { + return stream.detachAsData(); + } + return nullptr; } bool EncodeAnimated(SkWStream*, SkSpan<const SkEncoder::Frame>, const Options&) { diff --git a/gfx/skia/skia/src/ports/SkScalerContext_mac_ct.cpp b/gfx/skia/skia/src/ports/SkScalerContext_mac_ct.cpp @@ -552,6 +552,7 @@ void SkScalerContext_Mac::generateImage(const SkGlyph& glyph, void* imageBuffer) namespace { class SkCTPathGeometrySink { + SkPathBuilder fBuilder; bool fStarted; CGPoint fCurrent; @@ -568,10 +569,10 @@ class SkCTPathGeometrySink { } public: - SkPathBuilder fBuilder; - SkCTPathGeometrySink() : fStarted{false}, fCurrent{0,0} {} + SkPath detach() { return fBuilder.detach(); } + static void ApplyElement(void *ctx, const CGPathElement *element) { SkCTPathGeometrySink& self = *(SkCTPathGeometrySink*)ctx; CGPoint* points = element->points; @@ -630,8 +631,7 @@ public: */ #define kScaleForSubPixelPositionHinting (4.0f) -std::optional<SkScalerContext::GeneratedPath> -SkScalerContext_Mac::generatePath(const SkGlyph& glyph) { +bool SkScalerContext_Mac::generatePath(const SkGlyph& glyph, SkPath* path, bool* modified) { SkScalar scaleX = SK_Scalar1; SkScalar scaleY = SK_Scalar1; @@ -668,18 +668,20 @@ SkScalerContext_Mac::generatePath(const SkGlyph& glyph) { CGGlyph cgGlyph = SkTo<CGGlyph>(glyph.getGlyphID()); SkUniqueCFRef<CGPathRef> cgPath(CTFontCreatePathForGlyph(fCTFont.get(), cgGlyph, &xform)); + path->reset(); if (!cgPath) { - return {}; + return false; } SkCTPathGeometrySink sink; CGPathApply(cgPath.get(), &sink, SkCTPathGeometrySink::ApplyElement); + *path = sink.detach(); if (fDoSubPosition) { SkMatrix m; m.setScale(SkScalarInvert(scaleX), SkScalarInvert(scaleY)); - sink.fBuilder.transform(m); + path->transform(m); } - return {{sink.fBuilder.detach(), false}}; + return true; } void SkScalerContext_Mac::generateFontMetrics(SkFontMetrics* metrics) { diff --git a/gfx/skia/skia/src/ports/SkScalerContext_mac_ct.h b/gfx/skia/skia/src/ports/SkScalerContext_mac_ct.h @@ -46,7 +46,7 @@ public: protected: GlyphMetrics generateMetrics(const SkGlyph&, SkArenaAlloc*) override; void generateImage(const SkGlyph&, void*) override; - std::optional<GeneratedPath> generatePath(const SkGlyph&) override; + bool generatePath(const SkGlyph& glyph, SkPath* path, bool* modified) override; void generateFontMetrics(SkFontMetrics*) override; private: diff --git a/gfx/skia/skia/src/ports/SkScalerContext_win_dw.cpp b/gfx/skia/skia/src/ports/SkScalerContext_win_dw.cpp @@ -23,7 +23,6 @@ #include "include/core/SkOpenTypeSVGDecoder.h" #include "include/core/SkPath.h" #include "include/core/SkPictureRecorder.h" -#include "include/core/SkSpan.h" #include "include/effects/SkGradientShader.h" #include "include/private/base/SkMutex.h" #include "include/private/base/SkTo.h" @@ -663,9 +662,9 @@ bool SkScalerContext_DW::drawColorV1Paint(SkCanvas& canvas, // glyphIndex, color.value, color.paletteEntryIndex, color.alpha, color.colorAttributes auto const& solidGlyph = element.paint.solidGlyph; - SkPathBuilder builder; + SkPath path; SkTScopedComPtr<IDWriteGeometrySink> geometryToPath; - HRBM(SkDWriteGeometrySink::Create(&builder, &geometryToPath), + HRBM(SkDWriteGeometrySink::Create(&path, &geometryToPath), "Could not create geometry to path converter."); UINT16 glyphId = SkTo<UINT16>(solidGlyph.glyphIndex); { @@ -682,11 +681,11 @@ bool SkScalerContext_DW::drawColorV1Paint(SkCanvas& canvas, "Could not create glyph outline."); } - builder.transform(SkMatrix::Scale(1.0f / fTextSizeRender, 1.0f / fTextSizeRender)); + path.transform(SkMatrix::Scale(1.0f / fTextSizeRender, 1.0f / fTextSizeRender)); SkPaint skPaint; skPaint.setColor4f(sk_color_from(solidGlyph.color.value)); skPaint.setAntiAlias(fRenderingMode != DWRITE_RENDERING_MODE_ALIASED); - canvas.drawPath(builder.detach(), skPaint); + canvas.drawPath(path, skPaint); return true; } @@ -1131,9 +1130,9 @@ bool SkScalerContext_DW::drawColorV1Paint(SkCanvas& canvas, case DWRITE_PAINT_TYPE_GLYPH: { // A glyph paint element has one child, which is the fill for the glyph shape glyphIndex. - SkPathBuilder builder; + SkPath path; SkTScopedComPtr<IDWriteGeometrySink> geometryToPath; - HRBM(SkDWriteGeometrySink::Create(&builder, &geometryToPath), + HRBM(SkDWriteGeometrySink::Create(&path, &geometryToPath), "Could not create geometry to path converter."); UINT16 glyphId = SkTo<UINT16>(element.paint.glyph.glyphIndex); { @@ -1150,8 +1149,8 @@ bool SkScalerContext_DW::drawColorV1Paint(SkCanvas& canvas, "Could not create glyph outline."); } - builder.transform(SkMatrix::Scale(1.0f / fTextSizeRender, 1.0f / fTextSizeRender)); - canvas.clipPath(builder.detach(), fRenderingMode != DWRITE_RENDERING_MODE_ALIASED); + path.transform(SkMatrix::Scale(1.0f / fTextSizeRender, 1.0f / fTextSizeRender)); + canvas.clipPath(path, fRenderingMode != DWRITE_RENDERING_MODE_ALIASED); drawChildren(1); return true; @@ -1322,9 +1321,9 @@ bool SkScalerContext_DW::generateColorV1PaintBounds( // A solid glyph paint element has no children. // glyphIndex, color.value, color.paletteEntryIndex, color.alpha, color.colorAttributes - SkPathBuilder builder; + SkPath path; SkTScopedComPtr<IDWriteGeometrySink> geometryToPath; - HRBM(SkDWriteGeometrySink::Create(&builder, &geometryToPath), + HRBM(SkDWriteGeometrySink::Create(&path, &geometryToPath), "Could not create geometry to path converter."); UINT16 glyphId = SkTo<UINT16>(element.paint.solidGlyph.glyphIndex); { @@ -1343,8 +1342,8 @@ bool SkScalerContext_DW::generateColorV1PaintBounds( SkMatrix t = *ctm; t.preConcat(SkMatrix::Scale(1.0f / fTextSizeRender, 1.0f / fTextSizeRender)); - builder.transform(t); - bounds->join(builder.detach().getBounds()); + path.transform(t); + bounds->join(path.getBounds()); return true; } @@ -1366,9 +1365,9 @@ bool SkScalerContext_DW::generateColorV1PaintBounds( case DWRITE_PAINT_TYPE_GLYPH: { // A glyph paint element has one child, which is the fill for the glyph shape glyphIndex. - SkPathBuilder builder; + SkPath path; SkTScopedComPtr<IDWriteGeometrySink> geometryToPath; - HRBM(SkDWriteGeometrySink::Create(&builder, &geometryToPath), + HRBM(SkDWriteGeometrySink::Create(&path, &geometryToPath), "Could not create geometry to path converter."); UINT16 glyphId = SkTo<UINT16>(element.paint.glyph.glyphIndex); { @@ -1387,8 +1386,8 @@ bool SkScalerContext_DW::generateColorV1PaintBounds( SkMatrix t = *ctm; t.preConcat(SkMatrix::Scale(1.0f / fTextSizeRender, 1.0f / fTextSizeRender)); - builder.transform(t); - bounds->join(builder.detach().getBounds()); + path.transform(t); + bounds->join(path.getBounds()); return true; } @@ -1534,7 +1533,7 @@ bool SkScalerContext_DW::setAdvance(const SkGlyph& glyph, SkVector* advance) { // the end result is not always an integer as it would be with GDI. advance->fX = SkScalarRoundToScalar(advance->fX); } - *advance = fSkXform.mapVector(*advance); + fSkXform.mapVectors(advance, 1); return true; } @@ -1659,9 +1658,9 @@ bool SkScalerContext_DW::generateColorMetrics(const SkGlyph& glyph, SkRect* boun const DWRITE_COLOR_GLYPH_RUN* colorGlyph; HRBM(colorLayers->GetCurrentRun(&colorGlyph), "Could not get current color glyph run"); - SkPathBuilder builder; + SkPath path; SkTScopedComPtr<IDWriteGeometrySink> geometryToPath; - HRBM(SkDWriteGeometrySink::Create(&builder, &geometryToPath), + HRBM(SkDWriteGeometrySink::Create(&path, &geometryToPath), "Could not create geometry to path converter."); { Exclusive l(maybe_dw_mutex(*this->getDWriteTypeface())); @@ -1676,7 +1675,7 @@ bool SkScalerContext_DW::generateColorMetrics(const SkGlyph& glyph, SkRect* boun geometryToPath.get()), "Could not create glyph outline."); } - bounds->join(builder.detach().getBounds()); + bounds->join(path.getBounds()); } SkMatrix matrix = fSkXform; if (this->isSubpixel()) { @@ -2237,9 +2236,9 @@ bool SkScalerContext_DW::drawColorImage(const SkGlyph& glyph, SkCanvas& canvas) } paint.setColor(color); - SkPathBuilder builder; + SkPath path; SkTScopedComPtr<IDWriteGeometrySink> geometryToPath; - HRBM(SkDWriteGeometrySink::Create(&builder, &geometryToPath), + HRBM(SkDWriteGeometrySink::Create(&path, &geometryToPath), "Could not create geometry to path converter."); { Exclusive l(maybe_dw_mutex(*this->getDWriteTypeface())); @@ -2254,7 +2253,7 @@ bool SkScalerContext_DW::drawColorImage(const SkGlyph& glyph, SkCanvas& canvas) geometryToPath.get()), "Could not create glyph outline."); } - canvas.drawPath(builder.detach(), paint); + canvas.drawPath(path, paint); } return true; } @@ -2425,32 +2424,42 @@ void SkScalerContext_DW::generateImage(const SkGlyph& glyph, void* imageBuffer) this->generatePngImage(glyph, imageBuffer); #endif } else if (format == ScalerContextBits::PATH) { - this->generateImageFromPath(glyph, imageBuffer); + const SkPath* devPath = glyph.path(); + SkASSERT_RELEASE(devPath); + SkMaskBuilder mask(static_cast<uint8_t*>(imageBuffer), + glyph.iRect(), glyph.rowBytes(), glyph.maskFormat()); + 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); + const bool hairline = glyph.pathIsHairline(); + GenerateImageFromPath(mask, *devPath, fPreBlend, doBGR, doVert, a8LCD, hairline); } else { SK_ABORT("Bad format"); } } -std::optional<SkScalerContext::GeneratedPath> -SkScalerContext_DW::generatePath(const SkGlyph& glyph) { +bool SkScalerContext_DW::generatePath(const SkGlyph& glyph, SkPath* path, bool* modified) { + SkASSERT(path); + path->reset(); + SkGlyphID glyphID = glyph.getGlyphID(); // DirectWrite treats all out of bounds glyph ids as having the same data as glyph 0. // For consistency with all other backends, treat out of range glyph ids as an error. if (fGlyphCount <= glyphID) { - return {}; + return false; } - SkPathBuilder builder; SkTScopedComPtr<IDWriteGeometrySink> geometryToPath; - HR_GENERAL(SkDWriteGeometrySink::Create(&builder, &geometryToPath), - "Could not create geometry to path converter.", {}); + HRBM(SkDWriteGeometrySink::Create(path, &geometryToPath), + "Could not create geometry to path converter."); UINT16 glyphId = SkTo<UINT16>(glyphID); { Exclusive l(maybe_dw_mutex(*this->getDWriteTypeface())); //TODO: convert to<->from DIUs? This would make a difference if hinting. //It may not be needed, it appears that DirectWrite only hints at em size. - HR_GENERAL(this->getDWriteTypeface()->fDWriteFontFace->GetGlyphRunOutline( + HRBM(this->getDWriteTypeface()->fDWriteFontFace->GetGlyphRunOutline( SkScalarToFloat(fTextSizeRender), &glyphId, nullptr, //advances @@ -2459,11 +2468,11 @@ SkScalerContext_DW::generatePath(const SkGlyph& glyph) { FALSE, //sideways FALSE, //rtl geometryToPath.get()), - "Could not create glyph outline.", {}); + "Could not create glyph outline."); } - builder.transform(fSkXform); - return {{builder.detach(), false}}; + path->transform(fSkXform); + return true; } sk_sp<SkDrawable> SkScalerContext_DW::generateDrawable(const SkGlyph& glyph) { diff --git a/gfx/skia/skia/src/ports/SkScalerContext_win_dw.h b/gfx/skia/skia/src/ports/SkScalerContext_win_dw.h @@ -34,7 +34,7 @@ public: protected: GlyphMetrics generateMetrics(const SkGlyph&, SkArenaAlloc*) override; void generateImage(const SkGlyph&, void* imageBuffer) override; - std::optional<GeneratedPath> generatePath(const SkGlyph&) override; + bool generatePath(const SkGlyph&, SkPath*, bool*) override; sk_sp<SkDrawable> generateDrawable(const SkGlyph&) override; void generateFontMetrics(SkFontMetrics*) override; diff --git a/gfx/skia/skia/src/ports/SkTypeface_FreeType.h b/gfx/skia/skia/src/ports/SkTypeface_FreeType.h @@ -55,23 +55,24 @@ protected: const SkDescriptor*, SkTypeface* proxyTypeface) const override; void onFilterRec(SkScalerContextRec*) const override; - void getGlyphToUnicodeMap(SkSpan<SkUnichar>) const override; + void getGlyphToUnicodeMap(SkUnichar*) const override; std::unique_ptr<SkAdvancedTypefaceMetrics> onGetAdvancedMetrics() const override; void getPostScriptGlyphNames(SkString* dstArray) const override; bool onGetPostScriptName(SkString*) const override; int onGetUPEM() const override; - bool onGetKerningPairAdjustments(SkSpan<const SkGlyphID>, - SkSpan<int32_t> adjustments) const override; - void onCharsToGlyphs(SkSpan<const SkUnichar>, SkSpan<SkGlyphID>) const override; + 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; LocalizedStrings* onCreateFamilyNameIterator() const override; bool onGlyphMaskNeedsCurrentColor() const override; - int onGetVariationDesignPosition( - SkSpan<SkFontArguments::VariationPosition::Coordinate>) const override; - int onGetVariationDesignParameters(SkSpan<SkFontParameters::Variation::Axis>) const override; - int onGetTableTags(SkSpan<SkFontTableTag>) const override; + int onGetVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[], + int coordinateCount) const override; + int onGetVariationDesignParameters(SkFontParameters::Variation::Axis parameters[], + int parameterCount) const override; + int onGetTableTags(SkFontTableTag tags[]) const override; size_t onGetTableData(SkFontTableTag, size_t offset, size_t length, void* data) const override; sk_sp<SkData> onCopyTableData(SkFontTableTag) const override; diff --git a/gfx/skia/skia/src/ports/SkTypeface_fontations.cpp b/gfx/skia/skia/src/ports/SkTypeface_fontations.cpp @@ -7,6 +7,7 @@ #include "include/ports/SkTypeface_fontations.h" #include "include/codec/SkCodec.h" +#include "include/codec/SkPngDecoder.h" #include "include/core/SkBitmap.h" #include "include/core/SkCanvas.h" #include "include/core/SkData.h" @@ -15,9 +16,9 @@ #include "include/core/SkPictureRecorder.h" #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/codec/SkCodecPriv.h" #include "src/core/SkFontDescriptor.h" #include "src/core/SkFontPriv.h" #include "src/ports/SkTypeface_fontations_priv.h" @@ -25,18 +26,6 @@ namespace { -template <typename T> rust::Slice<T> toSlice(SkSpan<T> span) { - return rust::Slice<T>(span.data(), span.size()); -} - -void CheckPng() { -#if defined(SK_DEBUG) - if (!SkCodecs::HasDecoder("png")) { - SkDebugf("No PNG decoder registered. A call to SkCodecs::Register is necessary.\n"); - } -#endif -} - [[maybe_unused]] static inline const constexpr bool kSkShowTextBlitCoverage = false; sk_sp<SkData> streamToData(const std::unique_ptr<SkStreamAsset>& font_data) { @@ -279,34 +268,32 @@ bool SkTypeface_Fontations::onGlyphMaskNeedsCurrentColor() const { return fGlyphMasksMayNeedCurrentColor; } -void SkTypeface_Fontations::onCharsToGlyphs(SkSpan<const SkUnichar> chars, - SkSpan<SkGlyphID> glyphs) const { - SkASSERT(chars.size() == glyphs.size()); - rust::Slice<const uint32_t> codepointSlice{reinterpret_cast<const uint32_t*>(chars.data()), - chars.size()}; +void SkTypeface_Fontations::onCharsToGlyphs(const SkUnichar* chars, + int count, + SkGlyphID glyphs[]) const { + size_t realCount = SkToSizeT(count); + rust::Slice<const uint32_t> codepointSlice{reinterpret_cast<const uint32_t*>(chars), realCount}; + rust::Slice<uint16_t> glyphSlice{reinterpret_cast<uint16_t*>(glyphs), realCount}; fontations_ffi::lookup_glyph_or_zero(*fBridgeFontRef, *fMappingIndex, - codepointSlice, toSlice(glyphs)); + codepointSlice, glyphSlice); } int SkTypeface_Fontations::onCountGlyphs() const { return fontations_ffi::num_glyphs(*fBridgeFontRef); } -void SkTypeface_Fontations::getGlyphToUnicodeMap(SkSpan<SkUnichar> codepointForGlyphMap) const { - size_t numGlyphs = std::min(SkToSizeT(onCountGlyphs()), codepointForGlyphMap.size()); - rust::Slice<uint32_t> codepointForGlyphSlice - {reinterpret_cast<uint32_t*>(codepointForGlyphMap.data()), numGlyphs}; +void SkTypeface_Fontations::getGlyphToUnicodeMap(SkUnichar* codepointForGlyphMap) const { + size_t numGlyphs = SkToSizeT(onCountGlyphs()); + if (!codepointForGlyphMap) { + SkASSERT(numGlyphs == 0); + } + rust::Slice<uint32_t> codepointForGlyphSlice{reinterpret_cast<uint32_t*>(codepointForGlyphMap), + numGlyphs}; fontations_ffi::fill_glyph_to_unicode_map(*fBridgeFontRef, codepointForGlyphSlice); } void SkTypeface_Fontations::onFilterRec(SkScalerContextRec* rec) const { rec->useStrokeForFakeBold(); - // 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. - rec->fFlags &= ~SkScalerContext::kGenA8FromLCD_Flag; - // Opportunistic hinting downgrades copied from SkFontHost_FreeType.cpp SkFontHinting h = rec->getHinting(); if (SkFontHinting::kFull == h && !isLCD(*rec)) { @@ -360,24 +347,15 @@ public: , fMappingIndex(realTypeface.getMappingIndex()) , fPalette(realTypeface.getPalette()) , fHintingInstance(fontations_ffi::no_hinting_instance()) { - using AutoHinting = fontations_ffi::AutoHintingControl; - fRec.computeMatrices( SkScalerContextRec::PreMatrixScale::kVertical, &fScale, &fRemainingMatrix); fDoLinearMetrics = this->isLinearMetrics(); - // See below for the exception for SkFontHinting::kSlight. - bool forceAutoHinting = SkToBool(fRec.fFlags & kForceAutohinting_Flag); - AutoHinting autoHintingControl = forceAutoHinting ? AutoHinting::ForceForGlyfAndCff - : AutoHinting::Fallback; - -#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK - // On Android, match the FreeType backend and disable autohinting completely - // unless the force flag is set. - if (!forceAutoHinting) - autoHintingControl = AutoHinting::ForceOff; -#endif + fontations_ffi::AutoHintingControl autoHintingControl = + SkToBool(fRec.fFlags & kForceAutohinting_Flag) + ? fontations_ffi::AutoHintingControl::ForceForGlyfAndCff + : fontations_ffi::AutoHintingControl::AutoAsFallback; // Hinting-reliant fonts exist that display incorrect contours when not executing their // hinting instructions. Detect those and force-enable hinting for them. @@ -403,10 +381,10 @@ public: break; case SkFontHinting::kSlight: // Unhinted metrics. - if (autoHintingControl != AutoHinting::ForceForGlyfAndCff && - autoHintingControl != AutoHinting::ForceOff) - { - autoHintingControl = AutoHinting::ForceForGlyf; + if (autoHintingControl != + fontations_ffi::AutoHintingControl::ForceForGlyfAndCff) { + autoHintingControl = + fontations_ffi::AutoHintingControl::PreferAutoOverHintsForGlyf; } fHintingInstance = fontations_ffi::make_hinting_instance( fOutlines, @@ -449,31 +427,34 @@ public: } } - std::optional<SkScalar> getContourHeightForLetter(SkUnichar letter) { + bool getContourHeightForLetter(SkUnichar letter, SkScalar& height) { SkGlyphID glyphId; rust::Slice<const uint32_t> codepointSlice{reinterpret_cast<const uint32_t*>(&letter), 1}; rust::Slice<uint16_t> glyphSlice{reinterpret_cast<uint16_t*>(&glyphId), 1}; fontations_ffi::lookup_glyph_or_zero(fBridgeFontRef, fMappingIndex, codepointSlice, glyphSlice); if (!glyphId) { - return {}; + return false; } - fontations_ffi::BridgeScalerMetrics scalerMetrics; - if (auto glyphPath = generatePathForGlyphId(glyphId, fScale.y(), - *fHintingInstance, scalerMetrics)) { - return glyphPath->getBounds().height(); + SkPath glyphPath; + if (!generateYScalePathForGlyphId(glyphId, &glyphPath, fScale.y(), *fHintingInstance)) { + return false; } - return {}; + 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, // unscaled path retrieval. - std::optional<SkPath> generatePathForGlyphId(uint16_t glyphId, - float yScale, - const fontations_ffi::BridgeHintingInstance& hintingInstance, - fontations_ffi::BridgeScalerMetrics& scalerMetrics) { + bool generateYScalePathForGlyphId( + uint16_t glyphId, + SkPath* path, + float yScale, + 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 @@ -491,19 +472,22 @@ public: fPathVerbs, fPathPoints, scalerMetrics)) { - return {}; + return false; } + *path = SkPath::Make(reinterpret_cast<const SkPoint*>(fPathPoints.data()), + fPathPoints.size(), + fPathVerbs.data(), + fPathVerbs.size(), + nullptr, + 0, + SkPathFillType::kWinding); // See https://issues.skia.org/345178242 for details: // The FreeType backend performs a path simplification here based on the // equivalent of what we have here as scalerMetrics.has_overlaps // Since PathOps::Simplify fails or at times produces incorrect simplified // contours, skip that step here. - return SkPath::Make({reinterpret_cast<const SkPoint*>(fPathPoints.data()), - fPathPoints.size()}, - fPathVerbs, - {}, - SkPathFillType::kWinding); + return true; } protected: @@ -544,28 +528,23 @@ protected: x_advance = fontations_ffi::unhinted_advance_width_or_zero( fBridgeFontRef, fScale.y(), fBridgeNormalizedCoords, glyph.getGlyphID()); if (!doLinearMetrics) { - fontations_ffi::BridgeScalerMetrics scalerMetrics; - if (auto generatedPath = generatePathImpl(glyph.getGlyphID(), - scalerMetrics)) { - mx.generatedPath = std::move(*generatedPath); - - if (scalerMetrics.has_adjusted_advance) { - // FreeType rounds the advance to full pixels when in hinting modes. - // Match FreeType and round here. - // See - // * https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/autofit/afloader.c#L422 - // * https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttgload.c#L823 - float hinted_advance = roundf(scalerMetrics.adjusted_advance); - // TODO(drott): Remove this workaround for fontations returning 0 - // for a space glyph without contours, compare - // https://github.com/googlefonts/fontations/issues/905 - if (hinted_advance != x_advance && hinted_advance != 0) { - x_advance = hinted_advance; - } - } + float hinted_advance = 0; + fontations_ffi::scaler_hinted_advance_width( + fOutlines, *fHintingInstance, glyph.getGlyphID(), hinted_advance); + // FreeType rounds the advance to full pixels when in hinting modes. + // Match FreeType and round here. + // See + // * https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/autofit/afloader.c#L422 + // * https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/truetype/ttgload.c#L823 + hinted_advance = roundf(hinted_advance); + // TODO(drott): Remove this workaround for fontations returning 0 + // for a space glyph without contours, compare + // https://github.com/googlefonts/fontations/issues/905 + if (hinted_advance != x_advance && hinted_advance != 0) { + x_advance = hinted_advance; } } - mx.advance = fRemainingMatrix.mapPoint({x_advance, 0}); + mx.advance = fRemainingMatrix.mapXY(x_advance, SkFloatToScalar(0.f)); if (has_colrv1_glyph || has_colrv0_glyph) { mx.extraBits = has_colrv1_glyph ? ScalerContextBits::COLRv1 : ScalerContextBits::COLRv0; @@ -595,7 +574,8 @@ protected: if (upem == 0) { mx.bounds = SkRect::MakeEmpty(); } else { - SkMatrix fullTransform = fRec.getSingleMatrix(); + SkMatrix fullTransform; + fRec.getSingleMatrix(&fullTransform); fullTransform.preScale(1.f / upem, 1.f / upem); sk_fontations::BoundsPainter boundsPainter(*this, fullTransform, upem); @@ -627,14 +607,13 @@ protected: const fontations_ffi::BitmapMetrics bitmapMetrics = fontations_ffi::bitmap_metrics(*bitmap_glyph); - sk_sp<SkImage> img = SkImages::DeferredFromEncodedData( - SkData::MakeWithoutCopy(png_data.data(), png_data.size())); - if (!img) { - CheckPng(); + std::unique_ptr<SkCodec> codec = SkPngDecoder::Decode( + SkData::MakeWithoutCopy(png_data.data(), png_data.size()), nullptr); + if (!codec) { return mx; } - SkImageInfo info = img->imageInfo(); + SkImageInfo info = codec->getInfo(); SkRect bounds = SkRect::Make(info.bounds()); SkMatrix matrix = fRemainingMatrix; @@ -643,9 +622,8 @@ protected: // device pixels, and scaling the embedded PNG from its number of // rows to a specific size, depending on the ppem values in the // bitmap glyph information. - float imageToSize = fScale.y() / bitmapMetrics.ppem_y; - float fontUnitsToSize = fScale.y() / - fontations_ffi::units_per_em_or_zero(fBridgeFontRef); + SkScalar imageToSize = fScale.y() / bitmapMetrics.ppem_y; + float fontUnitsToSize = fScale.y() / fontations_ffi::units_per_em_or_zero(fBridgeFontRef); // The offset from origin is given in font units, so requires a // different scale factor than the scaling of the image. @@ -695,10 +673,15 @@ protected: rust::cxxbridge1::Slice<const uint8_t> png_data = fontations_ffi::png_data(*bitmap_glyph); SkASSERT(png_data.size()); - sk_sp<SkImage> glyph_image = SkImages::DeferredFromEncodedData( - SkData::MakeWithoutCopy(png_data.data(), png_data.size())); - if (!glyph_image) { - CheckPng(); + std::unique_ptr<SkCodec> codec = SkPngDecoder::Decode( + SkData::MakeWithoutCopy(png_data.data(), png_data.size()), nullptr); + + if (!codec) { + return; + } + + auto [glyph_image, result] = codec->getImage(); + if (result != SkCodec::Result::kSuccess) { return; } @@ -735,7 +718,26 @@ protected: void generateImage(const SkGlyph& glyph, void* imageBuffer) override { ScalerContextBits::value_type format = glyph.extraBits(); if (format == ScalerContextBits::PATH) { - this->generateImageFromPath(glyph, imageBuffer); + const SkPath* devPath = glyph.path(); + SkASSERT_RELEASE(devPath); + SkMaskBuilder mask(static_cast<uint8_t*>(imageBuffer), + glyph.iRect(), + glyph.rowBytes(), + glyph.maskFormat()); + 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); + // 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, + // as this is done in SkScalerContext::internalGetPath. + GenerateImageFromPath(mask, *devPath, fPreBlend, doBGR, doVert, a8LCD, hairline); + } else if (format == ScalerContextBits::COLRv1 || format == ScalerContextBits::COLRv0) { SkASSERT(glyph.maskFormat() == SkMask::kARGB32_Format); SkBitmap dstBitmap; @@ -761,24 +763,21 @@ protected: } } - std::optional<GeneratedPath> generatePathImpl(SkGlyphID glyphId, - fontations_ffi::BridgeScalerMetrics& scalerMetrics) { - if (auto path = generatePathForGlyphId(glyphId, fScale.y(), - *fHintingInstance, scalerMetrics)) { - return {{ - path->makeTransform(fRemainingMatrix), - !fRemainingMatrix.isIdentity() - }}; + bool generatePath(const SkGlyph& glyph, SkPath* path, bool* modified) override { + SkASSERT(glyph.extraBits() == ScalerContextBits::PATH); + + bool result = generateYScalePathForGlyphId( + glyph.getGlyphID(), path, fScale.y(), *fHintingInstance); + if (!result) { + return false; } - return {}; - } - // For hinted glyphs, generateMetrics provides a shortcut to set a generated path on - // SkGlyph - so this method will not be called when a path has already been set. - std::optional<GeneratedPath> generatePath(const SkGlyph& glyph) override { - SkASSERT(glyph.extraBits() == ScalerContextBits::PATH); - fontations_ffi::BridgeScalerMetrics scalerMetrics; - return generatePathImpl(glyph.getGlyphID(), scalerMetrics); + *path = path->makeTransform(fRemainingMatrix); + + if (fScale.y() != 1.0f || !fRemainingMatrix.isIdentity()) { + *modified = true; + } + return true; } bool drawCOLRGlyph(const SkGlyph& glyph, SkColor foregroundColor, SkCanvas* canvas) { @@ -787,7 +786,8 @@ protected: return false; } - SkMatrix scalerMatrix = fRec.getSingleMatrix(); + SkMatrix scalerMatrix; + fRec.getSingleMatrix(&scalerMatrix); SkAutoCanvasRestore autoRestore(canvas, true /* doSave */); // Scale down so that COLR operations can happen in glyph coordinates. @@ -852,16 +852,18 @@ protected: // Cap height synthesis. if (!out_metrics->fCapHeight) { - if (auto height = getContourHeightForLetter('H')) { - out_metrics->fCapHeight = *height; + SkScalar height; + if (getContourHeightForLetter('H', height)) { + out_metrics->fCapHeight = height; } else { out_metrics->fCapHeight = metrics.ascent; } } if (!out_metrics->fXHeight) { - if (auto xHeight = getContourHeightForLetter('x')) { - out_metrics->fXHeight = *xHeight; + SkScalar xHeight; + if (getContourHeightForLetter('x', xHeight)) { + out_metrics->fXHeight = xHeight; } else { out_metrics->fXHeight = metrics.ascent; } @@ -926,10 +928,10 @@ sk_sp<SkTypeface> SkTypeface_Fontations::onMakeClone(const SkFontArguments& args return sk_ref_sp(this); } - int numAxes = onGetVariationDesignPosition({}); + int numAxes = onGetVariationDesignPosition(nullptr, 0); auto fusedDesignPosition = std::make_unique<SkFontArguments::VariationPosition::Coordinate[]>(numAxes); - int retrievedAxes = onGetVariationDesignPosition({fusedDesignPosition.get(), numAxes}); + int retrievedAxes = onGetVariationDesignPosition(fusedDesignPosition.get(), numAxes); if (numAxes != retrievedAxes) { return nullptr; } @@ -956,7 +958,7 @@ sk_sp<SkTypeface> SkTypeface_Fontations::onMakeClone(const SkFontArguments& args return MakeFromData(fFontData, fusedArgs); } - // TODO(skbug.com/330149870): Palette differences are not fused, see DWrite backend impl. + // TODO(crbug.com/skia/330149870): Palette differences are not fused, see DWrite backend impl. rust::Slice<const fontations_ffi::PaletteOverride> argPaletteOverrides( reinterpret_cast<const fontations_ffi::PaletteOverride*>(args.getPalette().overrides), args.getPalette().overrideCount); @@ -1003,18 +1005,6 @@ std::unique_ptr<SkAdvancedTypefaceMetrics> SkTypeface_Fontations::onGetAdvancedM info->fFlags |= SkAdvancedTypefaceMetrics::kVariable_FontFlag; } - switch (fontations_ffi::outline_format(*fOutlines)) { - case fontations_ffi::OutlineFormat::Glyf: - info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font; - break; - case fontations_ffi::OutlineFormat::Cff: - info->fType = SkAdvancedTypefaceMetrics::kCFF_Font; - break; - default: - // leave info->fType SkAdvancedTypefaceMetrics::kOther; - break; - } - // Metrics information. fontations_ffi::Metrics metrics = fontations_ffi::get_unscaled_metrics(*fBridgeFontRef, *fBridgeNormalizedCoords); @@ -1032,11 +1022,6 @@ std::unique_ptr<SkAdvancedTypefaceMetrics> SkTypeface_Fontations::onGetAdvancedM info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style; } - rust::String readPsName; - if (fontations_ffi::postscript_name(*fBridgeFontRef, readPsName)) { - info->fPostScriptName = SkString(readPsName.data(), readPsName.size()); - } - fontations_ffi::BridgeFontStyle fontStyle; if (fontations_ffi::get_font_style(*fBridgeFontRef, *fBridgeNormalizedCoords, fontStyle)) { if (fontStyle.slant == SkFontStyle::Slant::kItalic_Slant) { @@ -1088,29 +1073,29 @@ size_t SkTypeface_Fontations::onGetTableData(SkFontTableTag tag, return std::min(copied, length); } -int SkTypeface_Fontations::onGetTableTags(SkSpan<SkFontTableTag> tags) const { +int SkTypeface_Fontations::onGetTableTags(SkFontTableTag tags[]) const { uint16_t numTables = fontations_ffi::table_tags(*fBridgeFontRef, rust::Slice<uint32_t>()); - if (tags.empty()) { + if (!tags) { return numTables; } - const size_t n = std::min<size_t>(numTables, tags.size()); - return fontations_ffi::table_tags(*fBridgeFontRef, toSlice(tags.first(n))); + rust::Slice<uint32_t> copyToTags(tags, numTables); + return fontations_ffi::table_tags(*fBridgeFontRef, copyToTags); } int SkTypeface_Fontations::onGetVariationDesignPosition( - SkSpan<SkFontArguments::VariationPosition::Coordinate> coordinates) const { + SkFontArguments::VariationPosition::Coordinate coordinates[], int coordinateCount) const { rust::Slice<fontations_ffi::SkiaDesignCoordinate> copyToCoordinates; - if (!coordinates.empty()) { + if (coordinates) { copyToCoordinates = rust::Slice<fontations_ffi::SkiaDesignCoordinate>( - reinterpret_cast<fontations_ffi::SkiaDesignCoordinate*>(coordinates.data()), - coordinates.size()); + reinterpret_cast<fontations_ffi::SkiaDesignCoordinate*>(coordinates), + coordinateCount); } return fontations_ffi::variation_position(*fBridgeNormalizedCoords, copyToCoordinates); } int SkTypeface_Fontations::onGetVariationDesignParameters( - SkSpan<SkFontParameters::Variation::Axis> parameters) const { - sk_fontations::AxisWrapper axisWrapper(parameters.data(), parameters.size()); + SkFontParameters::Variation::Axis parameters[], int parameterCount) const { + sk_fontations::AxisWrapper axisWrapper(parameters, parameterCount); return fontations_ffi::populate_axes(*fBridgeFontRef, axisWrapper); } @@ -1296,11 +1281,9 @@ void ColorPainter::pop_transform() { fCanvas.restore(); } void ColorPainter::push_clip_glyph(uint16_t glyph_id) { fCanvas.save(); - fontations_ffi::BridgeScalerMetrics scalerMetrics; - auto path = fScalerContext.generatePathForGlyphId(glyph_id, fUpem, - *fontations_ffi::no_hinting_instance(), - scalerMetrics); - fCanvas.clipPath(path.has_value() ? *path : SkPath(), fAntialias); + SkPath path; + fScalerContext.generateYScalePathForGlyphId(glyph_id, &path, fUpem, *fontations_ffi::no_hinting_instance()); + fCanvas.clipPath(path, fAntialias); } void ColorPainter::push_clip_rectangle(float x_min, float y_min, float x_max, float y_max) { @@ -1331,14 +1314,12 @@ void ColorPainter::fill_solid(uint16_t palette_index, float alpha) { } void ColorPainter::fill_glyph_solid(uint16_t glyph_id, uint16_t palette_index, float alpha) { - fontations_ffi::BridgeScalerMetrics scalerMetrics; - if (auto path = fScalerContext.generatePathForGlyphId(glyph_id, fUpem, - *fontations_ffi::no_hinting_instance(), - scalerMetrics)) { - SkPaint paint; - configure_solid_paint(palette_index, alpha, paint); - fCanvas.drawPath(*path, paint); - } + SkPath path; + fScalerContext.generateYScalePathForGlyphId(glyph_id, &path, fUpem, *fontations_ffi::no_hinting_instance()); + + SkPaint paint; + configure_solid_paint(palette_index, alpha, paint); + fCanvas.drawPath(path, paint); } void ColorPainter::configure_linear_paint(const fontations_ffi::FillLinearParams& linear_params, @@ -1396,15 +1377,13 @@ void ColorPainter::fill_glyph_linear(uint16_t glyph_id, const fontations_ffi::FillLinearParams& linear_params, fontations_ffi::BridgeColorStops& bridge_stops, uint8_t extend_mode) { - fontations_ffi::BridgeScalerMetrics scalerMetrics; - if (auto path = fScalerContext.generatePathForGlyphId(glyph_id, fUpem, - *fontations_ffi::no_hinting_instance(), - scalerMetrics)) { - SkPaint paint; - SkMatrix paintTransform = SkMatrixFromFontationsTransform(transform); - configure_linear_paint(linear_params, bridge_stops, extend_mode, paint, &paintTransform); - fCanvas.drawPath(*path, paint); - } + SkPath path; + fScalerContext.generateYScalePathForGlyphId(glyph_id, &path, fUpem, *fontations_ffi::no_hinting_instance()); + + SkPaint paint; + SkMatrix paintTransform = SkMatrixFromFontationsTransform(transform); + configure_linear_paint(linear_params, bridge_stops, extend_mode, paint, &paintTransform); + fCanvas.drawPath(path, paint); } void ColorPainter::configure_radial_paint( @@ -1555,15 +1534,13 @@ void ColorPainter::fill_glyph_radial(uint16_t glyph_id, const fontations_ffi::FillRadialParams& fill_radial_params, fontations_ffi::BridgeColorStops& bridge_stops, uint8_t extend_mode) { - fontations_ffi::BridgeScalerMetrics scalerMetrics; - if (auto path = fScalerContext.generatePathForGlyphId(glyph_id, fUpem, - *fontations_ffi::no_hinting_instance(), - scalerMetrics)) { - SkPaint paint; - SkMatrix paintTransform = SkMatrixFromFontationsTransform(transform); - configure_radial_paint(fill_radial_params, bridge_stops, extend_mode, paint, &paintTransform); - fCanvas.drawPath(*path, paint); - } + SkPath path; + fScalerContext.generateYScalePathForGlyphId(glyph_id, &path, fUpem, *fontations_ffi::no_hinting_instance()); + + SkPaint paint; + SkMatrix paintTransform = SkMatrixFromFontationsTransform(transform); + configure_radial_paint(fill_radial_params, bridge_stops, extend_mode, paint, &paintTransform); + fCanvas.drawPath(path, paint); } void ColorPainter::configure_sweep_paint(const fontations_ffi::FillSweepParams& sweep_params, @@ -1622,15 +1599,13 @@ void ColorPainter::fill_glyph_sweep(uint16_t glyph_id, const fontations_ffi::FillSweepParams& sweep_params, fontations_ffi::BridgeColorStops& bridge_stops, uint8_t extend_mode) { - fontations_ffi::BridgeScalerMetrics scalerMetrics; - if (auto path = fScalerContext.generatePathForGlyphId(glyph_id, fUpem, - *fontations_ffi::no_hinting_instance(), - scalerMetrics)) { - SkPaint paint; - SkMatrix paintTransform = SkMatrixFromFontationsTransform(transform); - configure_sweep_paint(sweep_params, bridge_stops, extend_mode, paint, &paintTransform); - fCanvas.drawPath(*path, paint); - } + SkPath path; + fScalerContext.generateYScalePathForGlyphId(glyph_id, &path, fUpem, *fontations_ffi::no_hinting_instance()); + + SkPaint paint; + SkMatrix paintTransform = SkMatrixFromFontationsTransform(transform); + configure_sweep_paint(sweep_params, bridge_stops, extend_mode, paint, &paintTransform); + fCanvas.drawPath(path, paint); } void ColorPainter::push_layer(uint8_t compositeMode) { @@ -1662,12 +1637,10 @@ void BoundsPainter::pop_transform() { } void BoundsPainter::push_clip_glyph(uint16_t glyph_id) { - fontations_ffi::BridgeScalerMetrics scalerMetrics; - if (auto path = fScalerContext.generatePathForGlyphId(glyph_id, fUpem, - *fontations_ffi::no_hinting_instance(), - scalerMetrics)) { - fBounds.join(path->makeTransform(fMatrixStack.back()).getBounds()); - } + SkPath path; + fScalerContext.generateYScalePathForGlyphId(glyph_id, &path, fUpem, *fontations_ffi::no_hinting_instance()); + path.transform(fMatrixStack.back()); + fBounds.join(path.getBounds()); } void BoundsPainter::push_clip_rectangle(float x_min, float y_min, float x_max, float y_max) { diff --git a/gfx/skia/skia/src/ports/SkTypeface_fontations_priv.h b/gfx/skia/skia/src/ports/SkTypeface_fontations_priv.h @@ -223,19 +223,20 @@ protected: void onFilterRec(SkScalerContextRec*) const override; std::unique_ptr<SkAdvancedTypefaceMetrics> onGetAdvancedMetrics() const override; void onGetFontDescriptor(SkFontDescriptor*, bool*) const override; - void onCharsToGlyphs(SkSpan<const SkUnichar>, SkSpan<SkGlyphID>) const override; + void onCharsToGlyphs(const SkUnichar* chars, int count, SkGlyphID glyphs[]) const override; int onCountGlyphs() const override; void getPostScriptGlyphNames(SkString*) const override {} - void getGlyphToUnicodeMap(SkSpan<SkUnichar>) const override; + void getGlyphToUnicodeMap(SkUnichar*) const override; int onGetUPEM() const override; void onGetFamilyName(SkString* familyName) const override; bool onGetPostScriptName(SkString*) const override; SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override; bool onGlyphMaskNeedsCurrentColor() const override; - int onGetVariationDesignPosition( - SkSpan<SkFontArguments::VariationPosition::Coordinate>) const override; - int onGetVariationDesignParameters(SkSpan<SkFontParameters::Variation::Axis>) const override; - int onGetTableTags(SkSpan<SkFontTableTag>) const override; + int onGetVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[], + int coordinateCount) const override; + int onGetVariationDesignParameters(SkFontParameters::Variation::Axis parameters[], + int parameterCount) const override; + int onGetTableTags(SkFontTableTag tags[]) const override; size_t onGetTableData(SkFontTableTag, size_t, size_t, void*) const override; private: diff --git a/gfx/skia/skia/src/ports/SkTypeface_mac_ct.cpp b/gfx/skia/skia/src/ports/SkTypeface_mac_ct.cpp @@ -479,12 +479,12 @@ static void populate_glyph_to_unicode(CTFontRef ctFont, CFIndex glyphCount, } } -void SkTypeface_Mac::getGlyphToUnicodeMap(SkSpan<SkUnichar> dstArray) const { +void SkTypeface_Mac::getGlyphToUnicodeMap(SkUnichar* dstArray) const { SkUniqueCFRef<CTFontRef> ctFont = SkCTFontCreateExactCopy(fFontRef.get(), CTFontGetUnitsPerEm(fFontRef.get()), fOpszVariation); - CFIndex glyphCount = std::min((size_t)CTFontGetGlyphCount(ctFont.get()), dstArray.size()); - populate_glyph_to_unicode(ctFont.get(), glyphCount, dstArray.data()); + CFIndex glyphCount = CTFontGetGlyphCount(ctFont.get()); + populate_glyph_to_unicode(ctFont.get(), glyphCount, dstArray); } std::unique_ptr<SkAdvancedTypefaceMetrics> SkTypeface_Mac::onGetAdvancedMetrics() const { @@ -622,7 +622,7 @@ std::unique_ptr<SkStreamAsset> SkTypeface_Mac::onOpenStream(int* ttcIndex) const int numTables = this->countTables(); SkTDArray<SkFontTableTag> tableTags; tableTags.resize(numTables); - this->readTableTags(tableTags); + this->getTableTags(tableTags.begin()); // CT seems to be unreliable in being able to obtain the type, // even if all we want is the first four bytes of the font resource. @@ -630,7 +630,7 @@ std::unique_ptr<SkStreamAsset> SkTypeface_Mac::onOpenStream(int* ttcIndex) const if (fontType == 0) { fontType = SkSFNTHeader::fontType_WindowsTrueType::TAG; - // see https://skbug.com/40038881#c7 + // see https://skbug.com/7630#c7 bool couldBeCFF = false; constexpr SkFontTableTag CFFTag = SkSetFourByteTag('C', 'F', 'F', ' '); constexpr SkFontTableTag CFF2Tag = SkSetFourByteTag('C', 'F', 'F', '2'); @@ -745,14 +745,14 @@ CFArrayRef SkTypeface_Mac::getVariationAxes() const { } int SkTypeface_Mac::onGetVariationDesignPosition( - SkSpan<SkFontArguments::VariationPosition::Coordinate> coordinates) const + SkFontArguments::VariationPosition::Coordinate coordinates[], int coordinateCount) const { CFArrayRef ctAxes = this->getVariationAxes(); if (!ctAxes) { return -1; } CFIndex axisCount = CFArrayGetCount(ctAxes); - if (coordinates.size() < (size_t)axisCount) { + if (!coordinates || coordinateCount < axisCount) { return axisCount; } @@ -823,18 +823,19 @@ SkTypeface::LocalizedStrings* SkTypeface_Mac::onCreateFamilyNameIterator() const return nameIter.release(); } -int SkTypeface_Mac::onGetTableTags(SkSpan<SkFontTableTag> tags) const { +int SkTypeface_Mac::onGetTableTags(SkFontTableTag tags[]) const { SkUniqueCFRef<CFArrayRef> cfArray( CTFontCopyAvailableTables(fFontRef.get(), kCTFontTableOptionNoOptions)); if (!cfArray) { return 0; } - const CFIndex count = CFArrayGetCount(cfArray.get()); - const CFIndex n = std::min(count, (CFIndex)tags.size()); - for (CFIndex i = 0; i < n; ++i) { - uintptr_t fontTag = reinterpret_cast<uintptr_t>( - CFArrayGetValueAtIndex(cfArray.get(), i)); - tags[i] = static_cast<SkFontTableTag>(fontTag); + CFIndex count = CFArrayGetCount(cfArray.get()); + if (tags) { + for (CFIndex i = 0; i < count; ++i) { + uintptr_t fontTag = reinterpret_cast<uintptr_t>( + CFArrayGetValueAtIndex(cfArray.get(), i)); + tags[i] = static_cast<SkFontTableTag>(fontTag); + } } return count; } @@ -1030,10 +1031,7 @@ void SkTypeface_Mac::onGetFontDescriptor(SkFontDescriptor* desc, *isLocalStream = fIsFromStream; } -void SkTypeface_Mac::onCharsToGlyphs(SkSpan<const SkUnichar> uni, SkSpan<SkGlyphID> glyphs) const { - SkASSERT(uni.size() == glyphs.size()); - const int count = SkToInt(uni.size()); - +void SkTypeface_Mac::onCharsToGlyphs(const SkUnichar uni[], int count, SkGlyphID glyphs[]) const { // Undocumented behavior of CTFontGetGlyphsForCharacters with non-bmp code points: // When a surrogate pair is detected, the glyph index used is the index of the high surrogate. // It is documented that if a mapping is unavailable, the glyph will be set to 0. @@ -1041,7 +1039,7 @@ void SkTypeface_Mac::onCharsToGlyphs(SkSpan<const SkUnichar> uni, SkSpan<SkGlyph AutoSTMalloc<1024, UniChar> charStorage; const UniChar* src; // UniChar is a UTF-16 16-bit code unit. int srcCount; - const SkUnichar* utf32 = uni.data(); + const SkUnichar* utf32 = reinterpret_cast<const SkUnichar*>(uni); UniChar* utf16 = charStorage.reset(2 * count); src = utf16; for (int i = 0; i < count; ++i) { @@ -1051,7 +1049,7 @@ void SkTypeface_Mac::onCharsToGlyphs(SkSpan<const SkUnichar> uni, SkSpan<SkGlyph // If there are any non-bmp code points, the provided 'glyphs' storage will be inadequate. AutoSTMalloc<1024, uint16_t> glyphStorage; - uint16_t* macGlyphs = glyphs.data(); + uint16_t* macGlyphs = glyphs; if (srcCount > count) { macGlyphs = glyphStorage.reset(srcCount); } @@ -1062,7 +1060,7 @@ void SkTypeface_Mac::onCharsToGlyphs(SkSpan<const SkUnichar> uni, SkSpan<SkGlyph // If all are bmp, 'glyphs' already contains the compact glyphs. // If some are non-bmp, copy and compact into 'glyphs'. if (srcCount > count) { - SkASSERT(glyphs.data() != macGlyphs); + SkASSERT(glyphs != macGlyphs); int extra = 0; for (int i = 0; i < count; ++i) { glyphs[i] = macGlyphs[i + extra]; @@ -1071,7 +1069,7 @@ void SkTypeface_Mac::onCharsToGlyphs(SkSpan<const SkUnichar> uni, SkSpan<SkGlyph } } } else { - SkASSERT(glyphs.data() == macGlyphs); + SkASSERT(glyphs == macGlyphs); } } @@ -1330,8 +1328,8 @@ sk_sp<SkTypeface> SkTypeface_Mac::MakeFromStream(std::unique_ptr<SkStreamAsset> return SkTypeface_Mac::Make(std::move(ctVariant), ctVariation.opsz, std::move(stream)); } -int SkTypeface_Mac::onGetVariationDesignParameters( - SkSpan<SkFontParameters::Variation::Axis> parameters) const +int SkTypeface_Mac::onGetVariationDesignParameters(SkFontParameters::Variation::Axis parameters[], + int parameterCount) const { CFArrayRef ctAxes = this->getVariationAxes(); if (!ctAxes) { @@ -1339,7 +1337,7 @@ int SkTypeface_Mac::onGetVariationDesignParameters( } CFIndex axisCount = CFArrayGetCount(ctAxes); - if (parameters.size() < (size_t)axisCount) { + if (!parameters || parameterCount < axisCount) { return axisCount; } diff --git a/gfx/skia/skia/src/ports/SkTypeface_mac_ct.h b/gfx/skia/skia/src/ports/SkTypeface_mac_ct.h @@ -106,24 +106,25 @@ protected: std::unique_ptr<SkStreamAsset> onOpenStream(int* ttcIndex) const override; std::unique_ptr<SkStreamAsset> onOpenExistingStream(int* ttcIndex) const override; bool onGlyphMaskNeedsCurrentColor() const override; - int onGetVariationDesignPosition( - SkSpan<SkFontArguments::VariationPosition::Coordinate>) const override; + int onGetVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[], + int coordinateCount) const override; void onGetFamilyName(SkString* familyName) const override; bool onGetPostScriptName(SkString*) const override; SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override; - int onGetTableTags(SkSpan<SkFontTableTag>) const override; + int onGetTableTags(SkFontTableTag tags[]) const override; size_t onGetTableData(SkFontTableTag, size_t offset, size_t length, void* data) const override; sk_sp<SkData> onCopyTableData(SkFontTableTag) const override; std::unique_ptr<SkScalerContext> onCreateScalerContext(const SkScalerContextEffects&, const SkDescriptor*) const override; void onFilterRec(SkScalerContextRec*) const override; void onGetFontDescriptor(SkFontDescriptor*, bool*) const override; - void getGlyphToUnicodeMap(SkSpan<SkUnichar>) const override; + void getGlyphToUnicodeMap(SkUnichar*) const override; std::unique_ptr<SkAdvancedTypefaceMetrics> onGetAdvancedMetrics() const override; - void onCharsToGlyphs(SkSpan<const SkUnichar>, SkSpan<SkGlyphID>) const override; + void onCharsToGlyphs(const SkUnichar* chars, int count, SkGlyphID glyphs[]) const override; int onCountGlyphs() const override; void getPostScriptGlyphNames(SkString*) const override {} - int onGetVariationDesignParameters(SkSpan<SkFontParameters::Variation::Axis>) const override; + int onGetVariationDesignParameters(SkFontParameters::Variation::Axis parameters[], + int parameterCount) const override; sk_sp<SkTypeface> onMakeClone(const SkFontArguments&) const override; void* onGetCTFontRef() const override { return (void*)fFontRef.get(); } diff --git a/gfx/skia/skia/src/ports/SkTypeface_proxy.cpp b/gfx/skia/skia/src/ports/SkTypeface_proxy.cpp @@ -26,13 +26,13 @@ bool SkTypeface_proxy::onGlyphMaskNeedsCurrentColor() const { } int SkTypeface_proxy::onGetVariationDesignPosition( - SkSpan<SkFontArguments::VariationPosition::Coordinate> coordinates) const { - return fRealTypeface->onGetVariationDesignPosition(coordinates); + SkFontArguments::VariationPosition::Coordinate coordinates[], int coordinateCount) const { + return fRealTypeface->onGetVariationDesignPosition(coordinates, coordinateCount); } -int SkTypeface_proxy::onGetVariationDesignParameters( - SkSpan<SkFontParameters::Variation::Axis> parameters) const { - return fRealTypeface->onGetVariationDesignParameters(parameters); +int SkTypeface_proxy::onGetVariationDesignParameters(SkFontParameters::Variation::Axis parameters[], + int parameterCount) const { + return fRealTypeface->onGetVariationDesignParameters(parameters, parameterCount); } SkFontStyle SkTypeface_proxy::onGetFontStyle() const { @@ -59,8 +59,8 @@ SkTypeface::LocalizedStrings* SkTypeface_proxy::onCreateFamilyNameIterator() con return fRealTypeface->createFamilyNameIterator(); } -int SkTypeface_proxy::onGetTableTags(SkSpan<SkFontTableTag> tags) const { - return fRealTypeface->readTableTags(tags); +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 { @@ -84,7 +84,7 @@ void SkTypeface_proxy::onGetFontDescriptor(SkFontDescriptor* desc, bool* seriali fRealTypeface->onGetFontDescriptor(desc, serialize); } -void SkTypeface_proxy::getGlyphToUnicodeMap(SkSpan<SkUnichar> glyphToUnicode) const { +void SkTypeface_proxy::getGlyphToUnicodeMap(SkUnichar* glyphToUnicode) const { fRealTypeface->getGlyphToUnicodeMap(glyphToUnicode); } @@ -96,18 +96,17 @@ std::unique_ptr<SkAdvancedTypefaceMetrics> SkTypeface_proxy::onGetAdvancedMetric return fRealTypeface->onGetAdvancedMetrics(); } -void SkTypeface_proxy::onCharsToGlyphs(SkSpan<const SkUnichar> chars, - SkSpan<SkGlyphID> glyphs) const { - fRealTypeface->unicharsToGlyphs(chars, glyphs); +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(SkSpan<const SkGlyphID> glyphs, - SkSpan<int32_t> adjustments) const { - return fRealTypeface->onGetKerningPairAdjustments(glyphs, adjustments); +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<SkScalerContext> realScalerContext, @@ -126,9 +125,8 @@ void SkScalerContext_proxy::generateImage(const SkGlyph& glyph, void* imageBuffe fRealScalerContext->generateImage(glyph, imageBuffer); } -std::optional<SkScalerContext::GeneratedPath> -SkScalerContext_proxy::generatePath(const SkGlyph& glyph) { - return fRealScalerContext->generatePath(glyph); +bool SkScalerContext_proxy::generatePath(const SkGlyph& glyph, SkPath* path, bool* modified) { + return fRealScalerContext->generatePath(glyph, path, modified); } sk_sp<SkDrawable> SkScalerContext_proxy::generateDrawable(const SkGlyph& glyph) { diff --git a/gfx/skia/skia/src/ports/SkTypeface_proxy.h b/gfx/skia/skia/src/ports/SkTypeface_proxy.h @@ -30,7 +30,7 @@ public: protected: SkScalerContext::GlyphMetrics generateMetrics(const SkGlyph&, SkArenaAlloc* alloc) override; void generateImage(const SkGlyph& glyph, void* imageBuffer) override; - std::optional<GeneratedPath> generatePath(const SkGlyph&) override; + bool generatePath(const SkGlyph& glyph, SkPath* path, bool* modified) override; sk_sp<SkDrawable> generateDrawable(const SkGlyph& glyph) override; void generateFontMetrics(SkFontMetrics* metrics) override; private: @@ -49,29 +49,31 @@ protected: std::unique_ptr<SkStreamAsset> onOpenStream(int* ttcIndex) const override; sk_sp<SkTypeface> onMakeClone(const SkFontArguments& args) const override; bool onGlyphMaskNeedsCurrentColor() const override; - int onGetVariationDesignPosition( - SkSpan<SkFontArguments::VariationPosition::Coordinate>) const override; - int onGetVariationDesignParameters(SkSpan<SkFontParameters::Variation::Axis>) 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(SkSpan<SkFontTableTag>) 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<SkScalerContext> 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(SkSpan<SkUnichar>) const override; + void getGlyphToUnicodeMap(SkUnichar* unichar) const override; void getPostScriptGlyphNames(SkString* glyphNames) const override; std::unique_ptr<SkAdvancedTypefaceMetrics> onGetAdvancedMetrics() const override; - void onCharsToGlyphs(SkSpan<const SkUnichar>, SkSpan<SkGlyphID>) const override; + void onCharsToGlyphs(const SkUnichar* chars, int count, SkGlyphID glyphs[]) const override; int onCountGlyphs() const override; void* onGetCTFontRef() const override; - bool onGetKerningPairAdjustments(SkSpan<const SkGlyphID>, - SkSpan<int32_t> adjustments) const override; + bool onGetKerningPairAdjustments(const SkGlyphID glyphs[], + int count, + int32_t adjustments[]) const override; private: sk_sp<SkTypeface> fRealTypeface; }; diff --git a/gfx/skia/skia/src/ports/SkTypeface_win_dw.cpp b/gfx/skia/skia/src/ports/SkTypeface_win_dw.cpp @@ -326,10 +326,9 @@ void DWriteFontTypeface::onGetFontDescriptor(SkFontDescriptor* desc, *serialize = SkToBool(fLoaders); } -void DWriteFontTypeface::onCharsToGlyphs(SkSpan<const SkUnichar> uni, - SkSpan<SkGlyphID> glyphs) const { - SkASSERT(uni.size() == glyphs.size()); - fDWriteFontFace->GetGlyphIndices((const UINT32*)uni.data(), uni.size(), glyphs.data()); +void DWriteFontTypeface::onCharsToGlyphs(const SkUnichar* uni, int count, + SkGlyphID glyphs[]) const { + fDWriteFontFace->GetGlyphIndices((const UINT32*)uni, count, glyphs); } int DWriteFontTypeface::onCountGlyphs() const { @@ -399,7 +398,7 @@ bool DWriteFontTypeface::onGlyphMaskNeedsCurrentColor() const { } int DWriteFontTypeface::onGetVariationDesignPosition( - SkSpan<SkFontArguments::VariationPosition::Coordinate> coordinates) const + SkFontArguments::VariationPosition::Coordinate coordinates[], int coordinateCount) const { #if defined(NTDDI_WIN10_RS3) && NTDDI_VERSION >= NTDDI_WIN10_RS3 @@ -424,7 +423,7 @@ int DWriteFontTypeface::onGetVariationDesignPosition( } } - if (coordinates.size() < variableAxisCount) { + if (!coordinates || coordinateCount < 0 || (unsigned)coordinateCount < variableAxisCount) { return SkTo<int>(variableAxisCount); } @@ -448,7 +447,7 @@ int DWriteFontTypeface::onGetVariationDesignPosition( } int DWriteFontTypeface::onGetVariationDesignParameters( - SkSpan<SkFontParameters::Variation::Axis> parameters) const + SkFontParameters::Variation::Axis parameters[], int parameterCount) const { #if defined(NTDDI_WIN10_RS3) && NTDDI_VERSION >= NTDDI_WIN10_RS3 @@ -473,7 +472,7 @@ int DWriteFontTypeface::onGetVariationDesignParameters( } } - if (parameters.size() < (unsigned)variableAxisCount) { + if (!parameters || parameterCount < variableAxisCount) { return variableAxisCount; } @@ -503,7 +502,7 @@ int DWriteFontTypeface::onGetVariationDesignParameters( #endif } -int DWriteFontTypeface::onGetTableTags(SkSpan<SkFontTableTag> tags) const { +int DWriteFontTypeface::onGetTableTags(SkFontTableTag tags[]) const { DWRITE_FONT_FACE_TYPE type = fDWriteFontFace->GetType(); if (type != DWRITE_FONT_FACE_TYPE_CFF && type != DWRITE_FONT_FACE_TYPE_TRUETYPE && @@ -723,10 +722,10 @@ static void glyph_to_unicode_map(IDWriteFontFace* fontFace, DWRITE_UNICODE_RANGE } } -void DWriteFontTypeface::getGlyphToUnicodeMap(SkSpan<SkUnichar> glyphToUnicode) const { +void DWriteFontTypeface::getGlyphToUnicodeMap(SkUnichar* glyphToUnicode) const { IDWriteFontFace* face = fDWriteFontFace.get(); - UINT32 numGlyphs = std::min<UINT32>(face->GetGlyphCount(), glyphToUnicode.size()); - sk_bzero(glyphToUnicode.data(), glyphToUnicode.size_bytes()); + UINT32 numGlyphs = face->GetGlyphCount(); + sk_bzero(glyphToUnicode, sizeof(SkUnichar) * numGlyphs); UINT32 remainingGlyphCount = numGlyphs; if (fDWriteFontFace1) { @@ -739,12 +738,10 @@ void DWriteFontTypeface::getGlyphToUnicodeMap(SkSpan<SkUnichar> glyphToUnicode) std::unique_ptr<DWRITE_UNICODE_RANGE[]> ranges(new DWRITE_UNICODE_RANGE[numRanges]); HRVM(face1->GetUnicodeRanges(numRanges, ranges.get(), &numRanges), "Failed to get ranges."); for (UINT32 i = 0; i < numRanges; ++i) { - glyph_to_unicode_map(face1, ranges[i], &remainingGlyphCount, numGlyphs, - glyphToUnicode.data()); + glyph_to_unicode_map(face1, ranges[i], &remainingGlyphCount, numGlyphs, glyphToUnicode); } } else { - glyph_to_unicode_map(face, {0, 0x10FFFF}, &remainingGlyphCount, numGlyphs, - glyphToUnicode.data()); + glyph_to_unicode_map(face, {0, 0x10FFFF}, &remainingGlyphCount, numGlyphs, glyphToUnicode); } } diff --git a/gfx/skia/skia/src/ports/SkTypeface_win_dw.h b/gfx/skia/skia/src/ports/SkTypeface_win_dw.h @@ -148,10 +148,10 @@ protected: std::unique_ptr<SkScalerContext> onCreateScalerContext(const SkScalerContextEffects&, const SkDescriptor*) const override; void onFilterRec(SkScalerContextRec*) const override; - void getGlyphToUnicodeMap(SkSpan<SkUnichar>) const override; + void getGlyphToUnicodeMap(SkUnichar* glyphToUnicode) const override; std::unique_ptr<SkAdvancedTypefaceMetrics> onGetAdvancedMetrics() const override; void onGetFontDescriptor(SkFontDescriptor*, bool*) const override; - void onCharsToGlyphs(SkSpan<const SkUnichar>, SkSpan<SkGlyphID>) const override; + void onCharsToGlyphs(const SkUnichar* chars, int count, SkGlyphID glyphs[]) const override; int onCountGlyphs() const override; void getPostScriptGlyphNames(SkString*) const override; int onGetUPEM() const override; @@ -160,10 +160,11 @@ protected: int onGetResourceName(SkString*) const override; SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override; bool onGlyphMaskNeedsCurrentColor() const override; - int onGetVariationDesignPosition( - SkSpan<SkFontArguments::VariationPosition::Coordinate>) const override; - int onGetVariationDesignParameters(SkSpan<SkFontParameters::Variation::Axis>) const override; - int onGetTableTags(SkSpan<SkFontTableTag>) const override; + int onGetVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[], + int coordinateCount) const override; + int onGetVariationDesignParameters(SkFontParameters::Variation::Axis parameters[], + int parameterCount) const override; + int onGetTableTags(SkFontTableTag tags[]) const override; size_t onGetTableData(SkFontTableTag, size_t offset, size_t length, void* data) const override; sk_sp<SkData> onCopyTableData(SkFontTableTag) const override; diff --git a/gfx/skia/skia/src/shaders/SkBitmapProcShader.cpp b/gfx/skia/skia/src/shaders/SkBitmapProcShader.cpp @@ -77,13 +77,14 @@ SkShaderBase::Context* SkBitmapProcLegacyShader::MakeContext( const SkShaderBase& shader, SkTileMode tmx, SkTileMode tmy, const SkSamplingOptions& sampling, const SkImage_Base* image, const ContextRec& rec, SkArenaAlloc* alloc) { - auto totalInverse = rec.fMatrixRec.totalInverse(); - if (!totalInverse) { + SkMatrix totalInverse; + // Do this first, so we know the matrix can be inverted. + if (!rec.fMatrixRec.totalInverse(&totalInverse)) { return nullptr; } SkBitmapProcState* state = alloc->make<SkBitmapProcState>(image, tmx, tmy); - if (!state->setup(*totalInverse, rec.fPaintAlpha, sampling)) { + if (!state->setup(totalInverse, rec.fPaintAlpha, sampling)) { return nullptr; } return alloc->make<BitmapProcShaderContext>(shader, rec, state); diff --git a/gfx/skia/skia/src/shaders/SkColorShader.h b/gfx/skia/skia/src/shaders/SkColorShader.h @@ -29,12 +29,7 @@ public: explicit SkColorShader(const SkColor4f& c) : fColor(c) {} bool isOpaque() const override { return fColor.isOpaque(); } - bool isConstant(SkColor4f* color = nullptr) const override { - if (color) { - *color = fColor; - } - return true; - } + bool isConstant() const override { return true; } ShaderType type() const override { return ShaderType::kColor; } diff --git a/gfx/skia/skia/src/shaders/SkGainmapShader.cpp b/gfx/skia/skia/src/shaders/SkGainmapShader.cpp @@ -8,6 +8,7 @@ #include "include/private/SkGainmapShader.h" #include "include/core/SkColor.h" +#include "include/core/SkColorFilter.h" #include "include/core/SkColorSpace.h" #include "include/core/SkImage.h" #include "include/core/SkMatrix.h" @@ -16,6 +17,7 @@ #include "include/effects/SkRuntimeEffect.h" #include "include/private/SkGainmapInfo.h" #include "include/private/base/SkAssert.h" +#include "src/core/SkColorFilterPriv.h" #include "src/core/SkImageInfoPriv.h" #include <cmath> @@ -95,19 +97,6 @@ sk_sp<SkShader> SkGainmapShader::Make(const sk_sp<const SkImage>& baseImage, const SkRect& dstRect, float dstHdrRatio, sk_sp<SkColorSpace> dstColorSpace) { - return Make(baseImage, baseRect, baseSamplingOptions, gainmapImage, gainmapRect, - gainmapSamplingOptions, gainmapInfo, dstRect, dstHdrRatio); -} - -sk_sp<SkShader> SkGainmapShader::Make(const sk_sp<const SkImage>& baseImage, - const SkRect& baseRect, - const SkSamplingOptions& baseSamplingOptions, - const sk_sp<const SkImage>& gainmapImage, - const SkRect& gainmapRect, - const SkSamplingOptions& gainmapSamplingOptions, - const SkGainmapInfo& gainmapInfo, - const SkRect& dstRect, - float dstHdrRatio) { sk_sp<SkColorSpace> baseColorSpace = baseImage->colorSpace() ? baseImage->refColorSpace() : SkColorSpace::MakeSRGB(); @@ -116,10 +105,13 @@ sk_sp<SkShader> SkGainmapShader::Make(const sk_sp<const SkImage>& baseImage, gainmapInfo.fGainmapMathColorSpace ? gainmapInfo.fGainmapMathColorSpace->makeLinearGamma() : baseColorSpace->makeLinearGamma(); + if (!dstColorSpace) { + dstColorSpace = SkColorSpace::MakeSRGB(); + } // Compute the sampling transformation matrices. - const SkMatrix baseRectToDstRect = SkMatrix::RectToRectOrIdentity(baseRect, dstRect); - const SkMatrix gainmapRectToDstRect = SkMatrix::RectToRectOrIdentity(gainmapRect, dstRect); + const SkMatrix baseRectToDstRect = SkMatrix::RectToRect(baseRect, dstRect); + const SkMatrix gainmapRectToDstRect = SkMatrix::RectToRect(gainmapRect, dstRect); // Compute the weight parameter that will be used to blend between the images. float W = 0.f; @@ -143,14 +135,25 @@ sk_sp<SkShader> SkGainmapShader::Make(const sk_sp<const SkImage>& baseImage, return baseImage->makeShader(baseSamplingOptions, &baseRectToDstRect); } - // The base image will have color space conversion performed. - auto baseImageShader = baseImage->makeShader(baseSamplingOptions, &baseRectToDstRect); + // Create a color filter to transform from the base image's color space to the color space in + // which the gainmap is to be applied. + auto colorXformSdrToGainmap = + SkColorFilterPriv::MakeColorSpaceXform(baseColorSpace, gainmapMathColorSpace); + + // Create a color filter to transform from the color space in which the gainmap is applied to + // the destination color space. + auto colorXformGainmapToDst = + SkColorFilterPriv::MakeColorSpaceXform(gainmapMathColorSpace, dstColorSpace); + + // The base image shader will convert into the color space in which the gainmap is applied. + auto baseImageShader = baseImage->makeRawShader(baseSamplingOptions, &baseRectToDstRect) + ->makeWithColorFilter(colorXformSdrToGainmap); // The gainmap image shader will ignore any color space that the gainmap has. auto gainmapImageShader = gainmapImage->makeRawShader(gainmapSamplingOptions, &gainmapRectToDstRect); - // Create the shader to apply the gainmap in the gain application color space. + // Create the shader to apply the gainmap. sk_sp<SkShader> gainmapMathShader; { SkRuntimeShaderBuilder builder(gainmap_apply_effect()); @@ -205,5 +208,6 @@ sk_sp<SkShader> SkGainmapShader::Make(const sk_sp<const SkImage>& baseImage, SkASSERT(gainmapMathShader); } - return gainmapMathShader->makeWithWorkingColorSpace(gainmapMathColorSpace); + // Return a shader that will apply the gainmap and then convert to the destination color space. + return gainmapMathShader->makeWithColorFilter(colorXformGainmapToDst); } diff --git a/gfx/skia/skia/src/shaders/SkImageShader.cpp b/gfx/skia/skia/src/shaders/SkImageShader.cpp @@ -159,7 +159,7 @@ sk_sp<SkFlattenable> SkImageShader::CreateProc(SkReadBuffer& buffer) { bool raw = buffer.isVersionLT(SkPicturePriv::Version::kRawImageShaders) ? false : buffer.readBool(); - // TODO(skbug.com/40043877): Subset is not serialized yet; it's only used by special images so it + // TODO(skbug.com/12784): Subset is not serialized yet; it's only used by special images so it // will never be written to an SKP. return raw ? SkImageShader::MakeRaw(std::move(img), tmx, tmy, sampling, &localMatrix) @@ -175,7 +175,7 @@ void SkImageShader::flatten(SkWriteBuffer& buffer) const { buffer.writeImage(fImage.get()); SkASSERT(fClampAsIfUnpremul == false); - // TODO(skbug.com/40043877): Subset is not serialized yet; it's only used by special images so it + // TODO(skbug.com/12784): Subset is not serialized yet; it's only used by special images so it // will never be written to an SKP. SkASSERT(!needs_subset(fImage.get(), fSubset)); @@ -215,7 +215,7 @@ static bool legacy_shader_can_handle(const SkMatrix& inv) { SkShaderBase::Context* SkImageShader::onMakeContext(const ContextRec& rec, SkArenaAlloc* alloc) const { - SkASSERT(!needs_subset(fImage.get(), fSubset)); // TODO(skbug.com/40043877) + SkASSERT(!needs_subset(fImage.get(), fSubset)); // TODO(skbug.com/12784) if (fImage->alphaType() == kUnpremul_SkAlphaType) { return nullptr; } @@ -266,8 +266,8 @@ SkShaderBase::Context* SkImageShader::onMakeContext(const ContextRec& rec, return nullptr; } - auto inv = rec.fMatrixRec.totalInverse(); - if (!inv || !legacy_shader_can_handle(*inv)) { + SkMatrix inv; + if (!rec.fMatrixRec.totalInverse(&inv) || !legacy_shader_can_handle(inv)) { return nullptr; } @@ -388,7 +388,7 @@ SkRect SkModifyPaintAndDstForDrawImageRect(const SkImage* image, SkRect imgBounds = SkRect::Make(image->bounds()); SkASSERT(src.isFinite() && dst.isFinite() && dst.isSorted()); - SkMatrix localMatrix = SkMatrix::RectToRectOrIdentity(src, dst); + SkMatrix localMatrix = SkMatrix::RectToRect(src, dst); if (!imgBounds.contains(src)) { if (!src.intersect(imgBounds)) { return SkRect::MakeEmpty(); // Nothing to draw for this entry @@ -507,7 +507,7 @@ static SkSamplingOptions tweak_sampling(SkSamplingOptions sampling, const SkMatr } bool SkImageShader::appendStages(const SkStageRec& rec, const SkShaders::MatrixRec& mRec) const { - SkASSERT(!needs_subset(fImage.get(), fSubset)); // TODO(skbug.com/40043877) + SkASSERT(!needs_subset(fImage.get(), fSubset)); // TODO(skbug.com/12784) // We only support certain sampling options in stages so far auto sampling = fSampling; @@ -521,11 +521,9 @@ bool SkImageShader::appendStages(const SkStageRec& rec, const SkShaders::MatrixR SkMatrix baseInv; // If the total matrix isn't valid then we will always access the base MIP level. if (mRec.totalMatrixIsValid()) { - auto inv = mRec.totalInverse(); - if (!inv) { + if (!mRec.totalInverse(&baseInv)) { return false; } - baseInv = *inv; baseInv.normalizePerspective(); } @@ -717,28 +715,8 @@ bool SkImageShader::appendStages(const SkStageRec& rec, const SkShaders::MatrixR && !sampling.useCubic && sampling.filter == SkFilterMode::kLinear && sampling.mipmap != SkMipmapMode::kLinear && fTileModeX == SkTileMode::kClamp && fTileModeY == SkTileMode::kClamp) { - // Check bounding box of points we will sample to see if we can use lowp - // and not over/under flow. - bool shouldUseHighPBilerp = false; - if (!rec.fDstBounds.isEmpty()) { - std::array<SkPoint, 4> quad = rec.fDstBounds.toQuad(); - baseInv.mapPoints(quad); - SkRect deviceImageSpace; - deviceImageSpace.setBounds(quad); - for (float val : SkSpan<const float>(deviceImageSpace.asScalars(), 4)) { - if (val > INT16_MAX || val < INT16_MIN || !std::isfinite(val)) { - shouldUseHighPBilerp = true; - break; - } - } - } - - if (shouldUseHighPBilerp) { - p->append(SkRasterPipelineOp::bilerp_clamp_8888_force_highp, upper.gather); - } else { - p->append(SkRasterPipelineOp::bilerp_clamp_8888, upper.gather); - } + p->append(SkRasterPipelineOp::bilerp_clamp_8888, upper.gather); if (ct == kBGRA_8888_SkColorType) { p->append(SkRasterPipelineOp::swap_rb); } diff --git a/gfx/skia/skia/src/shaders/SkImageShader.h b/gfx/skia/skia/src/shaders/SkImageShader.h @@ -40,7 +40,7 @@ public: const SkSamplingOptions&, const SkMatrix* localMatrix); - // TODO(skbug.com/40043877): Requires SkImage to be texture backed, and created SkShader can only + // TODO(skbug.com/12784): Requires SkImage to be texture backed, and created SkShader can only // be used on GPU-backed surfaces. static sk_sp<SkShader> MakeSubset(sk_sp<SkImage>, const SkRect& subset, @@ -86,7 +86,7 @@ private: const SkTileMode fTileModeX; const SkTileMode fTileModeY; - // TODO(skbug.com/40043877): This is only supported for GPU images currently. + // TODO(skbug.com/12784): This is only supported for GPU images currently. // If subset == (0,0,w,h) of the image, then no subset is applied. Subset will not be empty. const SkRect fSubset; diff --git a/gfx/skia/skia/src/shaders/SkLocalMatrixShader.cpp b/gfx/skia/skia/src/shaders/SkLocalMatrixShader.cpp @@ -13,8 +13,8 @@ class SkImage; enum class SkTileMode; struct SkStageRec; -bool SkLocalMatrixShader::isConstant(SkColor4f* color) const { - return as_SB(fWrappedShader)->isConstant(color); +bool SkLocalMatrixShader::isConstant() const { + return as_SB(fWrappedShader)->isConstant(); } SkShaderBase::GradientType SkLocalMatrixShader::asGradient(GradientInfo* info, @@ -72,8 +72,8 @@ bool SkLocalMatrixShader::appendStages(const SkStageRec& rec, SkCTMShader::SkCTMShader(sk_sp<SkShader> proxy, const SkMatrix& ctm) : fProxyShader(std::move(proxy)), fCTM(ctm) {} -bool SkCTMShader::isConstant(SkColor4f* color) const { - return as_SB(fProxyShader)->isConstant(color); +bool SkCTMShader::isConstant() const { + return as_SB(fProxyShader)->isConstant(); } SkShaderBase::GradientType SkCTMShader::asGradient(GradientInfo* info, diff --git a/gfx/skia/skia/src/shaders/SkLocalMatrixShader.h b/gfx/skia/skia/src/shaders/SkLocalMatrixShader.h @@ -43,7 +43,7 @@ public: bool isOpaque() const override { return as_SB(fWrappedShader)->isOpaque(); } - bool isConstant(SkColor4f* color = nullptr) const override; + bool isConstant() const override; GradientType asGradient(GradientInfo* info, SkMatrix* localMatrix) const override; ShaderType type() const override { return ShaderType::kLocalMatrix; } @@ -88,7 +88,7 @@ public: bool isOpaque() const override { return fProxyShader->isOpaque(); } - bool isConstant(SkColor4f* color = nullptr) const override; + bool isConstant() const override; GradientType asGradient(GradientInfo* info, SkMatrix* localMatrix) const override; ShaderType type() const override { return ShaderType::kCTM; } diff --git a/gfx/skia/skia/src/shaders/SkPictureShader.cpp b/gfx/skia/skia/src/shaders/SkPictureShader.cpp @@ -38,7 +38,7 @@ class SkDiscardableMemory; sk_sp<SkShader> SkPicture::makeShader(SkTileMode tmx, SkTileMode tmy, SkFilterMode filter, const SkMatrix* localMatrix, const SkRect* tile) const { - if (localMatrix && !localMatrix->invert()) { + if (localMatrix && !localMatrix->invert(nullptr)) { return nullptr; } return SkPictureShader::Make(sk_ref_sp(this), tmx, tmy, filter, localMatrix, tile); @@ -235,8 +235,7 @@ SkPictureShader::CachedImageInfo SkPictureShader::CachedImageInfo::Make( return {true, tileScale, - SkMatrix::RectToRectOrIdentity(bounds, - SkRect::MakeIWH(tileSize.width(), tileSize.height())), + SkMatrix::RectToRect(bounds, SkRect::MakeIWH(tileSize.width(), tileSize.height())), SkImageInfo::Make(tileSize, imgCT, kPremul_SkAlphaType, imgCS), props}; } diff --git a/gfx/skia/skia/src/shaders/SkRuntimeShader.cpp b/gfx/skia/skia/src/shaders/SkRuntimeShader.cpp @@ -152,11 +152,11 @@ sk_sp<SkFlattenable> SkRuntimeShader::CreateProc(SkReadBuffer& buffer) { sk_sp<SkData> uniforms = buffer.readByteArrayAsData(); - std::optional<SkMatrix> localM; + SkTLazy<SkMatrix> localM; if (buffer.isVersionLT(SkPicturePriv::kNoShaderLocalMatrix)) { uint32_t flags = buffer.read32(); if (flags & kHasLegacyLocalMatrix_Flag) { - buffer.readMatrix(&localM.emplace()); + buffer.readMatrix(localM.init()); } } @@ -182,5 +182,5 @@ sk_sp<SkFlattenable> SkRuntimeShader::CreateProc(SkReadBuffer& buffer) { } } - return effect->makeShader(std::move(uniforms), SkSpan(children), SkOptAddressOrNull(localM)); + return effect->makeShader(std::move(uniforms), SkSpan(children), localM.getMaybeNull()); } diff --git a/gfx/skia/skia/src/shaders/SkShader.cpp b/gfx/skia/skia/src/shaders/SkShader.cpp @@ -7,6 +7,7 @@ #include "include/core/SkShader.h" #include "include/core/SkColorFilter.h" +#include "include/core/SkColorSpace.h" #include "include/core/SkMatrix.h" #include "include/core/SkRefCnt.h" #include "src/shaders/SkColorFilterShader.h" @@ -44,8 +45,6 @@ sk_sp<SkShader> SkShader::makeWithColorFilter(sk_sp<SkColorFilter> filter) const return SkColorFilterShader::Make(sk_ref_sp(this), 1.0f, std::move(filter)); } -sk_sp<SkShader> SkShader::makeWithWorkingColorSpace(sk_sp<SkColorSpace> inputCS, - sk_sp<SkColorSpace> outputCS) const { - return SkWorkingColorSpaceShader::Make( - sk_ref_sp(this), std::move(inputCS), std::move(outputCS), /*workInUnpremul=*/false); +sk_sp<SkShader> SkShader::makeWithWorkingColorSpace(sk_sp<SkColorSpace> workingSpace) const { + return SkWorkingColorSpaceShader::Make(sk_ref_sp(this), std::move(workingSpace)); } diff --git a/gfx/skia/skia/src/shaders/SkShaderBase.cpp b/gfx/skia/skia/src/shaders/SkShaderBase.cpp @@ -16,26 +16,8 @@ #include "src/core/SkRasterPipelineOpList.h" #include "src/shaders/SkLocalMatrixShader.h" -#include <atomic> - class SkWriteBuffer; -namespace { - -// The 32-bit shader ID counter is not expected to wrap. In the unlikely event that it does, we -// assume that the associated shaders will be sufficiently temporally separated such that shaders -// with the same recycled IDs are no longer in use. -uint32_t next_unique_id() { - static std::atomic<uint32_t> gNextUniqueID{SK_InvalidUniqueID + 1}; - uint32_t id = SK_InvalidUniqueID; - do { - id = gNextUniqueID.fetch_add(1, std::memory_order_relaxed); - } while (id == SK_InvalidUniqueID); - return id; -} - -} // anonymous namespace - namespace SkShaders { MatrixRec::MatrixRec(const SkMatrix& ctm) : fCTM(ctm) {} @@ -44,11 +26,10 @@ std::optional<MatrixRec> MatrixRec::apply(const SkStageRec& rec, const SkMatrix& if (!fCTMApplied) { total = SkMatrix::Concat(fCTM, total); } - if (auto inv = total.invert()) { - total = SkMatrix::Concat(postInv, *inv); - } else { + if (!total.invert(&total)) { return {}; } + total = SkMatrix::Concat(postInv, total); if (!fCTMApplied) { rec.fPipeline->append(SkRasterPipelineOp::seed_shader); } @@ -63,11 +44,11 @@ std::optional<MatrixRec> MatrixRec::apply(const SkStageRec& rec, const SkMatrix& std::tuple<SkMatrix, bool> MatrixRec::applyForFragmentProcessor(const SkMatrix& postInv) const { SkASSERT(!fCTMApplied); - if (auto total = fPendingLocalMatrix.invert()) { - return {SkMatrix::Concat(postInv, *total), true}; - } else { + SkMatrix total; + if (!fPendingLocalMatrix.invert(&total)) { return {SkMatrix::I(), false}; } + return {SkMatrix::Concat(postInv, total), true}; } MatrixRec MatrixRec::applied() const { @@ -92,7 +73,7 @@ MatrixRec MatrixRec::concat(const SkMatrix& m) const { /////////////////////////////////////////////////////////////////////////////////////// -SkShaderBase::SkShaderBase() : fUniqueID(next_unique_id()) {} +SkShaderBase::SkShaderBase() = default; SkShaderBase::~SkShaderBase() = default; @@ -114,7 +95,7 @@ SkShaderBase::Context* SkShaderBase::makeContext(const ContextRec& rec, SkArenaA #ifdef SK_ENABLE_LEGACY_SHADERCONTEXT // We always fall back to raster pipeline when perspective is present. auto totalMatrix = rec.fMatrixRec.totalMatrix(); - if (totalMatrix.hasPerspective() || !totalMatrix.invert()) { + if (totalMatrix.hasPerspective() || !totalMatrix.invert(nullptr)) { return nullptr; } @@ -129,6 +110,10 @@ SkShaderBase::Context::Context(const SkShaderBase& shader, const ContextRec& rec // We should never use a context with perspective. SkASSERT(!rec.fMatrixRec.totalMatrix().hasPerspective()); + // Because the context parameters must be valid at this point, we know that the matrix is + // invertible. + SkAssertResult(rec.fMatrixRec.totalInverse(&fTotalInverse)); + fPaintAlpha = rec.fPaintAlpha; } diff --git a/gfx/skia/skia/src/shaders/SkShaderBase.h b/gfx/skia/skia/src/shaders/SkShaderBase.h @@ -117,8 +117,8 @@ public: SkMatrix totalMatrix() const { return SkMatrix::Concat(fCTM, fTotalLocalMatrix); } /** Gets the inverse of totalMatrix(), if invertible. */ - std::optional<SkMatrix> totalInverse() const { - return this->totalMatrix().invert(); + [[nodiscard]] bool totalInverse(SkMatrix* out) const { + return this->totalMatrix().invert(out); } /** Is there a transform that has not yet been applied by a parent shader? */ @@ -186,17 +186,14 @@ class SkShaderBase : public SkShader { public: ~SkShaderBase() override; - uint32_t uniqueID() const { return fUniqueID; } - sk_sp<SkShader> makeInvertAlpha() const; sk_sp<SkShader> makeWithCTM(const SkMatrix&) const; // owns its own ctm /** - * Returns true if the shader is guaranteed to produce only a single color - * If the color parameter is non-null, it is filled in with that color. + * Returns true if the shader is guaranteed to produce only a single color. * Subclasses can override this to allow loop-hoisting optimization. */ - virtual bool isConstant(SkColor4f* color = nullptr) const { return false; } + virtual bool isConstant() const { return false; } enum class ShaderType { #define M(type) k##type, @@ -247,7 +244,7 @@ public: // of fColors/fColorOffsets on input, and // actual number of colors/offsets on // output. - SkColor4f* fColors = nullptr; //!< The colors in the gradient. + SkColor* fColors = nullptr; //!< The colors in the gradient. SkScalar* fColorOffsets = nullptr; //!< The unit offset for color transitions. SkPoint fPoint[2]; //!< Type specific, see above. SkScalar fRadius[2]; //!< Type specific, see above. @@ -323,10 +320,12 @@ public: // Reference to shader, so we don't have to dupe information. const SkShaderBase& fShader; - uint8_t getPaintAlpha() const { return fPaintAlpha; } + uint8_t getPaintAlpha() const { return fPaintAlpha; } + const SkMatrix& getTotalInverse() const { return fTotalInverse; } private: - uint8_t fPaintAlpha; + SkMatrix fTotalInverse; + uint8_t fPaintAlpha; }; /** @@ -376,7 +375,7 @@ public: } static void RegisterFlattenables(); - /** DEPRECATED. skbug.com/40040221 + /** DEPRECATED. skbug.com/8941 * If this shader can be represented by another shader + a localMatrix, return that shader and * the localMatrix. If not, return nullptr and ignore the localMatrix parameter. */ @@ -408,9 +407,6 @@ protected: return false; } -private: - const uint32_t fUniqueID; - friend class SkShaders::MatrixRec; }; inline SkShaderBase* as_SB(SkShader* shader) { diff --git a/gfx/skia/skia/src/shaders/SkWorkingColorSpaceShader.cpp b/gfx/skia/skia/src/shaders/SkWorkingColorSpaceShader.cpp @@ -21,29 +21,6 @@ #include <utility> -sk_sp<SkShader> SkWorkingColorSpaceShader::Make(sk_sp<SkShader> shader, - sk_sp<SkColorSpace> inputCS, - sk_sp<SkColorSpace> outputCS, - bool workInUnpremul) { - if (!shader) { - return nullptr; - } - - if (!inputCS && !outputCS && !workInUnpremul) { - // A null input is the final dst CS, and a null output is the input CS, so if both are null - // then there's no additional conversion for children and no additional conversion - // applied to the shader's output. - return shader; - } else { - // Otherwise there's some conversion that has to happen on the input side or the output side - // that makes this not a no-op. - return sk_sp(new SkWorkingColorSpaceShader(std::move(shader), - std::move(inputCS), - std::move(outputCS), - workInUnpremul)); - } -} - bool SkWorkingColorSpaceShader::appendStages(const SkStageRec& rec, const SkShaders::MatrixRec& mRec) const { sk_sp<SkColorSpace> dstCS = sk_ref_sp(rec.fDstCS); @@ -51,92 +28,48 @@ bool SkWorkingColorSpaceShader::appendStages(const SkStageRec& rec, dstCS = SkColorSpace::MakeSRGB(); } - // TODO(b/431253455): Should get the dstAT from `rec` - const SkAlphaType dstAT = kPremul_SkAlphaType; - auto [inputCS, outputCS, workingAT] = this->workingSpace(dstCS, dstAT); - - SkColorInfo dst = {rec.fDstColorType, dstAT, dstCS}, - input = {rec.fDstColorType, workingAT, inputCS}, - output = {rec.fDstColorType, workingAT, outputCS}; + SkColorInfo dst = {rec.fDstColorType, kPremul_SkAlphaType, dstCS}, + working = {rec.fDstColorType, kPremul_SkAlphaType, fWorkingSpace}; - const auto* dstToInput = rec.fAlloc->make<SkColorSpaceXformSteps>(dst, input); - const auto* outputToDst = rec.fAlloc->make<SkColorSpaceXformSteps>(output, dst); - // NOTE: There is no inputToOutput steps to apply because it is assumed that the child shader - // is responsible for such conversion (or input == output and it's a no-op). + const auto* dstToWorking = rec.fAlloc->make<SkColorSpaceXformSteps>(dst, working); + const auto* workingToDst = rec.fAlloc->make<SkColorSpaceXformSteps>(working, dst); // Alpha-only image shaders reference the paint color, which is already in the destination // color space. We need to transform it to the working space for consistency. SkColor4f paintColorInWorkingSpace = rec.fPaintColor.makeOpaque(); - dstToInput->apply(paintColorInWorkingSpace.vec()); + dstToWorking->apply(paintColorInWorkingSpace.vec()); - // TODO(b/431253455): The working rec should have its alpha type set to `workingAT` SkStageRec workingRec = {rec.fPipeline, rec.fAlloc, rec.fDstColorType, - fInputSpace.get(), + fWorkingSpace.get(), paintColorInWorkingSpace, - rec.fSurfaceProps, - rec.fDstBounds}; + rec.fSurfaceProps}; if (!as_SB(fShader)->appendStages(workingRec, mRec)) { return false; } - outputToDst->apply(rec.fPipeline); + workingToDst->apply(rec.fPipeline); return true; } void SkWorkingColorSpaceShader::flatten(SkWriteBuffer& buffer) const { buffer.writeFlattenable(fShader.get()); - buffer.writeBool(fWorkInUnpremul); - - buffer.writeBool(SkToBool(fInputSpace)); - if (fInputSpace) { - buffer.writeDataAsByteArray(fInputSpace->serialize().get()); - } - - buffer.writeBool(SkToBool(fOutputSpace)); - if (fOutputSpace) { - buffer.writeDataAsByteArray(fOutputSpace->serialize().get()); - } + buffer.writeDataAsByteArray(fWorkingSpace->serialize().get()); } sk_sp<SkFlattenable> SkWorkingColorSpaceShader::CreateProc(SkReadBuffer& buffer) { sk_sp<SkShader> shader(buffer.readShader()); - - // If true, will not work in unpremul and assume inputSpace will be non-null and the outputSpace - // will be null. - const bool legacyWorkingCS = buffer.isVersionLT(SkPicturePriv::kWorkingColorSpaceOutput); - - bool workInUnpremul = !legacyWorkingCS && buffer.readBool(); - - // The input/output spaces are allowed to be null, but if we think we have a non-null CS, then - // it better be deserializable. - sk_sp<SkColorSpace> inputSpace; - if (legacyWorkingCS || buffer.readBool()) { - auto data = buffer.readByteArrayAsData(); - if (!buffer.validate(data != nullptr)) { - return nullptr; - } - inputSpace = SkColorSpace::Deserialize(data->data(), data->size()); - if (!buffer.validate(inputSpace != nullptr)) { - return nullptr; - } + auto data = buffer.readByteArrayAsData(); + if (!buffer.validate(data != nullptr)) { + return nullptr; } - - sk_sp<SkColorSpace> outputSpace; - if (!legacyWorkingCS && buffer.readBool()) { - auto data = buffer.readByteArrayAsData(); - if (!buffer.validate(data != nullptr)) { - return nullptr; - } - outputSpace = SkColorSpace::Deserialize(data->data(), data->size()); - if (!buffer.validate(outputSpace != nullptr)) { - return nullptr; - } + sk_sp<SkColorSpace> workingSpace = SkColorSpace::Deserialize(data->data(), data->size()); + if (!buffer.validate(workingSpace != nullptr)) { + return nullptr; } - - return Make(std::move(shader), std::move(inputSpace), std::move(outputSpace), workInUnpremul); + return Make(std::move(shader), std::move(workingSpace)); } void SkRegisterWorkingColorSpaceShaderFlattenable() { diff --git a/gfx/skia/skia/src/shaders/SkWorkingColorSpaceShader.h b/gfx/skia/skia/src/shaders/SkWorkingColorSpaceShader.h @@ -22,37 +22,26 @@ struct SkStageRec; class SkWorkingColorSpaceShader final : public SkShaderBase { public: - // NOTE: `workInUnpremul` is not exposed to the public API yet as many shader implementations - // across CPU, Ganesh, and Graphite have to be updated to convert to unpremul. - static sk_sp<SkShader> Make(sk_sp<SkShader> shader, - sk_sp<SkColorSpace> inputCS, - sk_sp<SkColorSpace> outputCS, - bool workInUnpremul); + static sk_sp<SkShader> Make(sk_sp<SkShader> shader, sk_sp<SkColorSpace> workingSpace) { + if (!shader) { + return nullptr; + } else if (!workingSpace) { + return shader; + } else { + return sk_sp(new SkWorkingColorSpaceShader(std::move(shader), std::move(workingSpace))); + } + } ShaderType type() const override { return ShaderType::kWorkingColorSpace; } sk_sp<SkShader> shader() const { return fShader; } - - std::tuple</*inputCS=*/sk_sp<SkColorSpace>, - /*outputCS=*/sk_sp<SkColorSpace>, - /*workingAT=*/SkAlphaType> - workingSpace(sk_sp<SkColorSpace> dstCS, SkAlphaType dstAT) const { - sk_sp<SkColorSpace> inputSpace = fInputSpace ? fInputSpace : dstCS; - sk_sp<SkColorSpace> outputSpace = fOutputSpace ? fOutputSpace : inputSpace; - return {inputSpace, outputSpace, fWorkInUnpremul ? kUnpremul_SkAlphaType : dstAT}; - } + sk_sp<SkColorSpace> workingSpace() const { return fWorkingSpace; } private: - SkWorkingColorSpaceShader(sk_sp<SkShader> shader, - sk_sp<SkColorSpace> inputCS, - sk_sp<SkColorSpace> outputCS, - bool workInUnpremul) - : fShader(std::move(shader)) - , fInputSpace(std::move(inputCS)) - , fOutputSpace(std::move(outputCS)) - , fWorkInUnpremul(workInUnpremul) { + SkWorkingColorSpaceShader(sk_sp<SkShader> shader, sk_sp<SkColorSpace> workingSpace) + : fShader(std::move(shader)), fWorkingSpace(std::move(workingSpace)) { SkASSERT(fShader); - SkASSERT(fInputSpace || fOutputSpace || fWorkInUnpremul); + SkASSERT(fWorkingSpace); } bool appendStages(const SkStageRec& rec, const SkShaders::MatrixRec&) const override; @@ -63,9 +52,7 @@ private: void flatten(SkWriteBuffer& buffer) const override; sk_sp<SkShader> fShader; - sk_sp<SkColorSpace> fInputSpace; - sk_sp<SkColorSpace> fOutputSpace; - bool fWorkInUnpremul; + sk_sp<SkColorSpace> fWorkingSpace; }; #endif diff --git a/gfx/skia/skia/src/shaders/gradients/SkConicalGradient.cpp b/gfx/skia/skia/src/shaders/gradients/SkConicalGradient.cpp @@ -44,11 +44,11 @@ bool SkConicalGradient::FocalData::set(SkScalar r0, SkScalar r1, SkMatrix* matri // Map {focal point, (1, 0)} to {(0, 0), (1, 0)} const SkPoint from[2] = { {fFocalX, 0}, {1, 0} }; const SkPoint to[2] = { {0, 0}, {1, 0} }; - const auto focalMatrix = SkMatrix::PolyToPoly(from, to); - if (!focalMatrix) { + SkMatrix focalMatrix; + if (!focalMatrix.setPolyToPoly(from, to, 2)) { return false; } - matrix->postConcat(*focalMatrix); + matrix->postConcat(focalMatrix); fR1 = r1 / SkScalarAbs(1 - fFocalX); // focalMatrix has a scale of 1/(1-f) // The following transformations are just to accelerate the shader computation by saving @@ -62,11 +62,13 @@ bool SkConicalGradient::FocalData::set(SkScalar r0, SkScalar r1, SkMatrix* matri return true; } -std::optional<SkMatrix> SkConicalGradient::MapToUnitX(const SkPoint &startCenter, - const SkPoint &endCenter) { +bool SkConicalGradient::MapToUnitX(const SkPoint &startCenter, + const SkPoint &endCenter, + SkMatrix* dstMatrix) { const SkPoint centers[2] = { startCenter, endCenter }; const SkPoint unitvec[2] = { {0, 0}, {1, 0} }; - return SkMatrix::PolyToPoly(centers, unitvec); + + return dstMatrix->setPolyToPoly(centers, unitvec, 2); } sk_sp<SkShader> SkConicalGradient::Create(const SkPoint& c0, @@ -91,12 +93,10 @@ sk_sp<SkShader> SkConicalGradient::Create(const SkPoint& c0, gradientType = Type::kRadial; } else { - auto mx = MapToUnitX(c0, c1); - if (!mx) { + if (!MapToUnitX(c0, c1, &gradientMatrix)) { // Degenerate case. return nullptr; } - gradientMatrix = *mx; gradientType = SkScalarNearlyZero(r1 - r0) ? Type::kStrip : Type::kFocal; } diff --git a/gfx/skia/skia/src/shaders/gradients/SkConicalGradient.h b/gfx/skia/skia/src/shaders/gradients/SkConicalGradient.h @@ -13,8 +13,6 @@ #include "include/core/SkScalar.h" #include "src/shaders/gradients/SkGradientBaseShader.h" -#include <optional> - class SkArenaAlloc; class SkMatrix; class SkRasterPipeline; @@ -52,8 +50,9 @@ public: enum class Type { kRadial, kStrip, kFocal }; - static std::optional<SkMatrix> MapToUnitX(const SkPoint& startCenter, - const SkPoint& endCenter); + static bool MapToUnitX(const SkPoint& startCenter, + const SkPoint& endCenter, + SkMatrix* dstMatrix); static sk_sp<SkShader> Create(const SkPoint& start, SkScalar startRadius, diff --git a/gfx/skia/skia/src/shaders/gradients/SkGradientBaseShader.cpp b/gfx/skia/skia/src/shaders/gradients/SkGradientBaseShader.cpp @@ -109,12 +109,12 @@ void SkGradientBaseShader::flatten(SkWriteBuffer& buffer) const { colorCount--; } - buffer.writeColor4fArray({colors, colorCount}); + buffer.writeColor4fArray(colors, colorCount); if (colorSpaceData) { buffer.writeDataAsByteArray(colorSpaceData.get()); } if (positions) { - buffer.writeScalarArray({positions, colorCount}); + buffer.writeScalarArray(positions, colorCount); } } @@ -145,7 +145,7 @@ bool SkGradientBaseShader::DescriptorScope::unflatten(SkReadBuffer& buffer, fColorCount = buffer.getArrayCount(); if (!(validate_array(buffer, fColorCount, &fColorStorage) && - buffer.readColor4fArray({fColorStorage.begin(), fColorCount}))) { + buffer.readColor4fArray(fColorStorage.begin(), fColorCount))) { return false; } fColors = fColorStorage.begin(); @@ -158,7 +158,7 @@ bool SkGradientBaseShader::DescriptorScope::unflatten(SkReadBuffer& buffer, } if (SkToBool(flags & kHasPosition_GSF)) { if (!(validate_array(buffer, fColorCount, &fPositionStorage) && - buffer.readScalarArray({fPositionStorage.begin(), fColorCount}))) { + buffer.readScalarArray(fPositionStorage.begin(), fColorCount))) { return false; } fPositions = fPositionStorage.begin(); @@ -546,7 +546,7 @@ void SkGradientBaseShader::AppendInterpolatedToDstStages(SkRasterPipeline* p, dstColorSpace = sk_srgb_singleton(); } SkAlphaType intermediateAlphaType = colorIsPremul ? kPremul_SkAlphaType : kUnpremul_SkAlphaType; - // TODO(skbug.com/40044213): Get dst alpha type correctly + // TODO(skia:13108): Get dst alpha type correctly SkAlphaType dstAlphaType = kPremul_SkAlphaType; if (colorsAreOpaque) { @@ -669,7 +669,7 @@ static sk_sp<SkColorSpace> intermediate_color_space(SkGradientShader::Interpolat // The "standard" conversion to these spaces starts with XYZD65. That requires extra // effort to conjure. The author also has reference code for going directly from linear // sRGB, so we use that. - // TODO(skbug.com/40044213): Even better would be to have an LMS color space, because the first + // TODO(skia:13108): Even better would be to have an LMS color space, because the first // part of the conversion is a matrix multiply, which could be absorbed into the // color space xform. return SkColorSpace::MakeSRGBLinear(); @@ -681,12 +681,10 @@ static sk_sp<SkColorSpace> intermediate_color_space(SkGradientShader::Interpolat case ColorSpace::kRec2020: return SkColorSpace::MakeRGB(SkNamedTransferFn::kRec2020, SkNamedGamut::kRec2020); - case ColorSpace::kProphotoRGB: { - static SkOnce once; + case ColorSpace::kProphotoRGB: static skcms_Matrix3x3 lin_proPhoto_to_XYZ_D50; - once([] { SkNamedPrimaries::kProPhotoRGB.toXYZD50(&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); @@ -950,7 +948,7 @@ SkColor4fXformer::SkColor4fXformer(const SkGradientBaseShader* shader, if ((i == 0 && shader->fFirstStopIsImplicit) || (i == colorCount - 2 && shader->fLastStopIsImplicit)) { // Do nothing. We don't want to introduce a full revolution for these stops - // Full rationale at skbug.com/40044215 + // Full rationale at skbug.com/13941 } else if (0 < h2 - h1 && h2 - h1 < 180) { h2 -= 360; // i.e. h1 += 360 delta -= 360; @@ -1023,7 +1021,7 @@ void SkGradientBaseShader::commonAsAGradient(GradientInfo* info) const { if (info->fColorCount >= fColorCount) { if (info->fColors) { for (int i = 0; i < fColorCount; ++i) { - info->fColors[i] = fColors[i]; + info->fColors[i] = this->getLegacyColor(i); } } if (info->fColorOffsets) { diff --git a/gfx/skia/skia/src/sksl/SkSLCompiler.cpp b/gfx/skia/skia/src/sksl/SkSLCompiler.cpp @@ -70,6 +70,8 @@ const Module* Compiler::moduleForProgramKind(ProgramKind kind) { case ProgramKind::kCompute: return m.loadComputeModule(this); case ProgramKind::kGraphiteFragment: return m.loadGraphiteFragmentModule(this); case ProgramKind::kGraphiteVertex: return m.loadGraphiteVertexModule(this); + case ProgramKind::kGraphiteFragmentES2: return m.loadGraphiteFragmentES2Module(this); + case ProgramKind::kGraphiteVertexES2: return m.loadGraphiteVertexES2Module(this); case ProgramKind::kPrivateRuntimeBlender: case ProgramKind::kPrivateRuntimeColorFilter: case ProgramKind::kPrivateRuntimeShader: return m.loadPrivateRTShaderModule(this); @@ -350,7 +352,7 @@ bool Compiler::optimize(Program& program) { program.fUsage.get()); #endif - // Unreachable code can confuse some drivers, so it's worth removing. (skbug.com/40043094) + // Unreachable code can confuse some drivers, so it's worth removing. (skia:12012) Transform::EliminateUnreachableCode(program); while (Transform::EliminateDeadFunctions(program)) { diff --git a/gfx/skia/skia/src/sksl/SkSLGraphiteModules.cpp b/gfx/skia/skia/src/sksl/SkSLGraphiteModules.cpp @@ -13,9 +13,13 @@ #if defined(SK_ENABLE_OPTIMIZE_SIZE) || !defined(SK_DEBUG) #include "src/sksl/generated/sksl_graphite_frag.minified.sksl" #include "src/sksl/generated/sksl_graphite_vert.minified.sksl" +#include "src/sksl/generated/sksl_graphite_frag_es2.minified.sksl" +#include "src/sksl/generated/sksl_graphite_vert_es2.minified.sksl" #else #include "src/sksl/generated/sksl_graphite_frag.unoptimized.sksl" +#include "src/sksl/generated/sksl_graphite_frag_es2.unoptimized.sksl" #include "src/sksl/generated/sksl_graphite_vert.unoptimized.sksl" +#include "src/sksl/generated/sksl_graphite_vert_es2.unoptimized.sksl" #endif namespace SkSL::Loader { @@ -24,7 +28,9 @@ GraphiteModules GetGraphiteModules() { #define M(name) SKSL_MINIFIED_##name return GraphiteModules{ M(sksl_graphite_frag), + M(sksl_graphite_frag_es2), M(sksl_graphite_vert), + M(sksl_graphite_vert_es2), }; } diff --git a/gfx/skia/skia/src/sksl/SkSLGraphiteModules.h b/gfx/skia/skia/src/sksl/SkSLGraphiteModules.h @@ -11,7 +11,9 @@ namespace SkSL::Loader { struct GraphiteModules { const char* fFragmentShader; + const char* fFragmentShaderES2; const char* fVertexShader; + const char* fVertexShaderES2; }; // These need to be two different functions so we can implement them in two different files. diff --git a/gfx/skia/skia/src/sksl/SkSLInliner.cpp b/gfx/skia/skia/src/sksl/SkSLInliner.cpp @@ -77,6 +77,8 @@ using namespace skia_private; namespace SkSL { namespace { +static constexpr int kInlinedStatementLimit = 2500; + static bool is_scopeless_block(Statement* stmt) { return stmt->is<Block>() && !stmt->as<Block>().isScope(); } @@ -647,14 +649,6 @@ Inliner::InlinedCall Inliner::inlineCall(const FunctionCall& call, return inlinedCall; } -bool Inliner::overInlineStatementLimit() const { - // Enforce a limit on inlining to avoid pathological cases. (inliner/ExponentialGrowth.sksl) - // Modules, which are entirely built-in SkSL, do not need to be limited in this way. - static constexpr int kInlinedStatementLimit = 2500; - return !fContext->fConfig->isBuiltinCode() && - fInlinedStatementCounter >= kInlinedStatementLimit; -} - bool Inliner::isSafeToInline(const FunctionDefinition* functionDef, const ProgramUsage& usage) { // A threshold of zero indicates that the inliner is completely disabled, so we can just return. if (this->settings().fInlineThreshold <= 0) { @@ -662,7 +656,7 @@ bool Inliner::isSafeToInline(const FunctionDefinition* functionDef, const Progra } // Enforce a limit on inlining to avoid pathological cases. (inliner/ExponentialGrowth.sksl) - if (this->overInlineStatementLimit()) { + if (fInlinedStatementCounter >= kInlinedStatementLimit) { return false; } @@ -678,7 +672,7 @@ bool Inliner::isSafeToInline(const FunctionDefinition* functionDef, const Progra for (const Variable* param : functionDef->declaration().parameters()) { // We don't allow inlining functions with parameters that are written-to, if they... - // - are `out` parameters (see skbug.com/40042700 for rationale.) + // - are `out` parameters (see skia:11326 for rationale.) // - are arrays or structures (introducing temporary copies is non-trivial) if ((param->modifierFlags() & ModifierFlag::kOut) || param->type().isArray() || @@ -1011,7 +1005,7 @@ bool Inliner::candidateCanBeInlined(const InlineCandidate& candidate, // Even if the function is safe, the arguments we are passing may not be. In particular, we // can't make copies of opaque values, so we need to reject inline candidates that would need to - // do this. Every call has different arguments, so this part is not cacheable. (skbug.com/40044923) + // do this. Every call has different arguments, so this part is not cacheable. (skia:13824) const FunctionCall& call = candidate.fCandidateExpr->get()->as<FunctionCall>(); const ExpressionArray& arguments = call.arguments(); for (int i = 0; i < arguments.size(); ++i) { @@ -1110,7 +1104,7 @@ bool Inliner::analyze(const std::vector<std::unique_ptr<ProgramElement>>& elemen } // Enforce a limit on inlining to avoid pathological cases. (inliner/ExponentialGrowth.sksl) - if (this->overInlineStatementLimit()) { + if (fInlinedStatementCounter >= kInlinedStatementLimit) { return false; } @@ -1173,7 +1167,7 @@ bool Inliner::analyze(const std::vector<std::unique_ptr<ProgramElement>>& elemen statementRemappingTable.set(enclosingStmt,&(*enclosingStmt)->as<Block>().children().back()); // Stop inlining if we've reached our hard cap on new statements. - if (this->overInlineStatementLimit()) { + if (fInlinedStatementCounter >= kInlinedStatementLimit) { break; } diff --git a/gfx/skia/skia/src/sksl/SkSLInliner.h b/gfx/skia/skia/src/sksl/SkSLInliner.h @@ -112,8 +112,6 @@ private: /** Checks whether inlining is viable for a FunctionCall, modulo recursion and function size. */ bool isSafeToInline(const FunctionDefinition* functionDef, const ProgramUsage& usage); - bool overInlineStatementLimit() const; - const Context* fContext = nullptr; Mangler fMangler; int fInlinedStatementCounter = 0; diff --git a/gfx/skia/skia/src/sksl/SkSLModule.h b/gfx/skia/skia/src/sksl/SkSLModule.h @@ -34,7 +34,9 @@ namespace SkSL { M(sksl_rt_shader) \ M(sksl_vert) \ M(sksl_graphite_frag) \ - M(sksl_graphite_vert) + M(sksl_graphite_frag_es2) \ + M(sksl_graphite_vert) \ + M(sksl_graphite_vert_es2) enum class ModuleType : int8_t { // `program` code is not in a module at all. diff --git a/gfx/skia/skia/src/sksl/SkSLModuleDataDefault.cpp b/gfx/skia/skia/src/sksl/SkSLModuleDataDefault.cpp @@ -34,7 +34,9 @@ // build with Graphite's modules. These will be filled in during initialization of the // Graphite backend. static const char* sdata_sksl_graphite_frag = ""; +static const char* sdata_sksl_graphite_frag_es2 = ""; static const char* sdata_sksl_graphite_vert = ""; +static const char* sdata_sksl_graphite_vert_es2 = ""; namespace SkSL { @@ -59,7 +61,9 @@ std::string GetModuleData(ModuleType type, const char* /*filename*/) { M(sksl_vert) G(sksl_graphite_frag) + G(sksl_graphite_frag_es2) G(sksl_graphite_vert) + G(sksl_graphite_vert_es2) default: SkUNREACHABLE; } @@ -70,10 +74,13 @@ namespace Loader { void SetGraphiteModuleData(const GraphiteModules& modules) { SkASSERTF(sdata_sksl_graphite_frag[0] == '\0', "We should only initialize this once"); sdata_sksl_graphite_frag = modules.fFragmentShader; + sdata_sksl_graphite_frag_es2 = modules.fFragmentShaderES2; sdata_sksl_graphite_vert = modules.fVertexShader; + sdata_sksl_graphite_vert_es2 = modules.fVertexShaderES2; SkASSERT(sdata_sksl_graphite_frag[0] != '\0'); SkASSERT(sdata_sksl_graphite_vert[0] != '\0'); + // TODO(jamesgk, kjlubick) Add check when ES2 code is included } } // namespace Loader diff --git a/gfx/skia/skia/src/sksl/SkSLModuleLoader.cpp b/gfx/skia/skia/src/sksl/SkSLModuleLoader.cpp @@ -57,7 +57,7 @@ static constexpr BuiltinTypePtr kRootTypes[] = { TYPE(SquareMat), TYPE(SquareHMat), TYPE(Mat), TYPE(HMat), - // TODO(skbug.com/40043431): generic short/ushort + // TODO(skia:12349): generic short/ushort TYPE(GenType), TYPE(GenIType), TYPE(GenUType), TYPE(GenHType), /* (GenSType) (GenUSType) */ TYPE(GenBType), @@ -108,6 +108,8 @@ struct ModuleLoader::Impl { std::unique_ptr<const Module> fComputeModule; // [GPU] + Compute stage decls std::unique_ptr<const Module> fGraphiteVertexModule; // [Vert] + Graphite vertex helpers std::unique_ptr<const Module> fGraphiteFragmentModule; // [Frag] + Graphite fragment helpers + std::unique_ptr<const Module> fGraphiteVertexES2Module; // [Vert] + Graphite vertex ES2 helpers + std::unique_ptr<const Module> fGraphiteFragmentES2Module;//[Frag] + Graphite fragment ES2 " " std::unique_ptr<const Module> fPublicModule; // [Shared] minus Private types + // Runtime effect intrinsics @@ -326,6 +328,18 @@ const Module* ModuleLoader::loadGraphiteFragmentModule(SkSL::Compiler* compiler) return fModuleLoader.fGraphiteFragmentModule.get(); } +const Module* ModuleLoader::loadGraphiteFragmentES2Module(SkSL::Compiler* compiler) { + if (!fModuleLoader.fGraphiteFragmentES2Module) { + const Module* fragmentModule = this->loadFragmentModule(compiler); + fModuleLoader.fGraphiteFragmentES2Module = + compile_and_shrink(compiler, + ProgramKind::kGraphiteFragmentES2, + MODULE_DATA(sksl_graphite_frag_es2), + fragmentModule); + } + return fModuleLoader.fGraphiteFragmentES2Module.get(); +} + const Module* ModuleLoader::loadGraphiteVertexModule(SkSL::Compiler* compiler) { if (!fModuleLoader.fGraphiteVertexModule) { const Module* vertexModule = this->loadVertexModule(compiler); @@ -337,6 +351,18 @@ const Module* ModuleLoader::loadGraphiteVertexModule(SkSL::Compiler* compiler) { return fModuleLoader.fGraphiteVertexModule.get(); } +const Module* ModuleLoader::loadGraphiteVertexES2Module(SkSL::Compiler* compiler) { + if (!fModuleLoader.fGraphiteVertexES2Module) { + const Module* vertexModule = this->loadVertexModule(compiler); + fModuleLoader.fGraphiteVertexES2Module = + compile_and_shrink(compiler, + ProgramKind::kGraphiteVertexES2, + MODULE_DATA(sksl_graphite_vert_es2), + vertexModule); + } + return fModuleLoader.fGraphiteVertexES2Module.get(); +} + void ModuleLoader::Impl::makeRootSymbolTable() { auto rootModule = std::make_unique<Module>(); rootModule->fSymbols = std::make_unique<SymbolTable>(/*builtin=*/true); diff --git a/gfx/skia/skia/src/sksl/SkSLModuleLoader.h b/gfx/skia/skia/src/sksl/SkSLModuleLoader.h @@ -50,6 +50,8 @@ public: const Module* loadComputeModule(SkSL::Compiler* compiler); const Module* loadGraphiteVertexModule(SkSL::Compiler* compiler); const Module* loadGraphiteFragmentModule(SkSL::Compiler* compiler); + const Module* loadGraphiteVertexES2Module(SkSL::Compiler* compiler); + const Module* loadGraphiteFragmentES2Module(SkSL::Compiler* compiler); const Module* loadPublicModule(SkSL::Compiler* compiler); const Module* loadPrivateRTShaderModule(SkSL::Compiler* compiler); diff --git a/gfx/skia/skia/src/sksl/SkSLParser.cpp b/gfx/skia/skia/src/sksl/SkSLParser.cpp @@ -1356,6 +1356,9 @@ bool Parser::interfaceBlock(const Modifiers& modifiers) { } this->expect(Token::Kind::TK_RBRACKET, "']'"); } + if (!this->expect(Token::Kind::TK_SEMICOLON, "';'")) { + return false; + } fields.push_back(SkSL::Field(this->rangeFrom(fieldPos), fieldModifiers.fLayout, @@ -1363,10 +1366,6 @@ bool Parser::interfaceBlock(const Modifiers& modifiers) { this->text(fieldName), actualType)); } while (this->checkNext(Token::Kind::TK_COMMA)); - - if (!this->expect(Token::Kind::TK_SEMICOLON, "';'")) { - return false; - } } std::string_view instanceName; Token instanceNameToken; @@ -1701,7 +1700,7 @@ std::unique_ptr<Statement> Parser::continueStatement() { /* DISCARD SEMICOLON */ std::unique_ptr<Statement> Parser::discardStatement() { Token start; - if (!this->expect(Token::Kind::TK_DISCARD, "'discard'", &start)) { + if (!this->expect(Token::Kind::TK_DISCARD, "'continue'", &start)) { return nullptr; } if (!this->expect(Token::Kind::TK_SEMICOLON, "';'")) { diff --git a/gfx/skia/skia/src/sksl/SkSLProgramKind.h b/gfx/skia/skia/src/sksl/SkSLProgramKind.h @@ -21,6 +21,8 @@ enum class ProgramKind : int8_t { kCompute, kGraphiteFragment, kGraphiteVertex, + kGraphiteFragmentES2, + kGraphiteVertexES2, kRuntimeColorFilter, // Runtime effect only suitable as SkColorFilter kRuntimeShader, // " " " " " SkShader kRuntimeBlender, // " " " " " SkBlender diff --git a/gfx/skia/skia/src/sksl/SkSLProgramSettings.h b/gfx/skia/skia/src/sksl/SkSLProgramSettings.h @@ -66,7 +66,7 @@ struct ProgramSettings { bool fValidateSPIRV = true; // If true, any synthetic uniforms must use push constant syntax bool fUseVulkanPushConstantsForGaneshRTAdjust = false; - // TODO(skbug.com/40042585) - Replace this with a "promised" capabilities? + // 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 // `for` loops at version 100, but any loop structure is allowed at version 300. @@ -104,7 +104,7 @@ struct ProgramConfig { } bool strictES2Mode() const { - // TODO(skbug.com/40042585): Remove the first condition - so this is just based on #version. + // TODO(skia:11209): Remove the first condition - so this is just based on #version. // Make it more generic (eg, isVersionLT) checking. return fSettings.fMaxVersionAllowed == Version::k100 && fRequiredSkSLVersion == Version::k100 && @@ -123,12 +123,14 @@ struct ProgramConfig { static bool IsFragment(ProgramKind kind) { return kind == ProgramKind::kFragment || - kind == ProgramKind::kGraphiteFragment; + kind == ProgramKind::kGraphiteFragment || + kind == ProgramKind::kGraphiteFragmentES2; } static bool IsVertex(ProgramKind kind) { return kind == ProgramKind::kVertex || - kind == ProgramKind::kGraphiteVertex; + kind == ProgramKind::kGraphiteVertex || + kind == ProgramKind::kGraphiteVertexES2; } static bool IsCompute(ProgramKind kind) { diff --git a/gfx/skia/skia/src/sksl/SkSLUtil.h b/gfx/skia/skia/src/sksl/SkSLUtil.h @@ -111,7 +111,7 @@ struct ShaderCaps { bool fCanUseMinAndAbsTogether = true; bool fCanUseFractForNegativeValues = true; bool fMustForceNegatedAtanParamToFloat = false; - bool fMustForceNegatedLdexpParamToMultiply = false; // skbug.com/40043167 + bool fMustForceNegatedLdexpParamToMultiply = false; // http://skbug.com/12076 // Returns whether a device incorrectly implements atan(y,x) as atan(y/x) bool fAtan2ImplementedAsAtanYOverX = false; // If this returns true some operation (could be a no op) must be called between floor and abs @@ -136,11 +136,11 @@ struct ShaderCaps { // statement is made for the type. bool fNoDefaultPrecisionForExternalSamplers = false; // ARM GPUs calculate `matrix * vector` in SPIR-V at full precision, even when the inputs are - // RelaxedPrecision. Rewriting the multiply as a sum of vector*scalar fixes this. (skbug.com/40042841) + // RelaxedPrecision. Rewriting the multiply as a sum of vector*scalar fixes this. (skia:11769) bool fRewriteMatrixVectorMultiply = false; - // Rewrites matrix equality comparisons to avoid an Adreno driver bug. (skbug.com/40042682) + // Rewrites matrix equality comparisons to avoid an Adreno driver bug. (skia:11308) bool fRewriteMatrixComparisons = false; - // Strips const from function parameters in the GLSL code generator. (skbug.com/40044949) + // Strips const from function parameters in the GLSL code generator. (skia:13858) bool fRemoveConstFromFunctionParameters = false; // On some Android devices colors aren't accurate enough for the double lookup in the // Perlin noise shader. This workaround aggressively snaps colors to multiples of 1/255. @@ -153,13 +153,6 @@ struct ShaderCaps { // array function parameter, so fForceStd430ArrayLayout will make all array strides conform // to std430 stride alignment rules. bool fForceStd430ArrayLayout = false; - // Some NVIDIA drivers fail to create a pipeline if RelaxedPrecision is applied to - // OpImageSampleImplicitLod when sampling from a YCbCr image. This workaround simply disables - // RelaxedPrecision for that op regardless of image kind. (skbug.com/421927604) - bool fCannotUseRelaxedPrecisionOnImageSample = false; - // Clamp, Min, Max, intrinsics all appear to be broken on Intel UHD630, so shaders for these - // devices will break these intrinsics into individual scalar commands. - bool fVectorClampMinMaxSupport = true; const char* fVersionDeclString = ""; @@ -175,7 +168,7 @@ struct ShaderCaps { class ShaderCapsFactory { public: static const ShaderCaps* Default() { - static const SkSL::ShaderCaps* const sCaps = [] { + static const SkSL::ShaderCaps* sCaps = [] { std::unique_ptr<ShaderCaps> caps = MakeShaderCaps(); caps->fVersionDeclString = "#version 400"; caps->fShaderDerivativeSupport = true; @@ -185,7 +178,7 @@ public: } static const ShaderCaps* Standalone() { - static const SkSL::ShaderCaps* const sCaps = MakeShaderCaps().release(); + static const SkSL::ShaderCaps* sCaps = MakeShaderCaps().release(); return sCaps; } diff --git a/gfx/skia/skia/src/sksl/analysis/SkSLFinalizationChecks.cpp b/gfx/skia/skia/src/sksl/analysis/SkSLFinalizationChecks.cpp @@ -55,7 +55,7 @@ public: this->checkGlobalVariableSizeLimit(pe.as<GlobalVarDeclaration>()); break; case ProgramElement::Kind::kInterfaceBlock: - // TODO(skbug.com/40044753): Enforce duplicate checks universally. This is currently not + // TODO(skia:13664): Enforce duplicate checks universally. This is currently not // possible without changes to the binding index assignment logic in graphite. this->checkBindUniqueness(pe.as<InterfaceBlock>()); break; @@ -93,7 +93,7 @@ public: int32_t set = var->layout().fSet; int32_t binding = var->layout().fBinding; if (binding != -1) { - // TODO(skbug.com/40044753): This should map a `set` value of -1 to the default settings value + // TODO(skia:13664): This should map a `set` value of -1 to the default settings value // used by codegen backends to prevent duplicates that may arise from the effective // default set value. uint64_t key = ((uint64_t)set << 32) + binding; diff --git a/gfx/skia/skia/src/sksl/analysis/SkSLIsConstantExpression.cpp b/gfx/skia/skia/src/sksl/analysis/SkSLIsConstantExpression.cpp @@ -61,7 +61,7 @@ public: return !fLoopIndices || !fLoopIndices->contains(v); } - // ... not a sequence expression (skbug.com/40044392)... + // ... not a sequence expression (skia:13311)... case Expression::Kind::kBinary: if (e.as<BinaryExpression>().getOperator().kind() == Operator::Kind::COMMA) { return true; diff --git a/gfx/skia/skia/src/sksl/codegen/SkSLCodeGenTypes.h b/gfx/skia/skia/src/sksl/codegen/SkSLCodeGenTypes.h @@ -15,33 +15,6 @@ enum class PrettyPrint : bool { kYes = true, }; -namespace spirv { - -enum ReservedId { - // Zero is not a valid ID in SPIR-V. - kIdInvalid = 0, - - // The following are IDs that are fixed in every SPIR-V, if present. If the backend needs to - // transform the SPIR-V, it doesn't have to figure them out. See the transformations in - // VulkanSpirvTransforms.cpp for where and why each of these IDs might be used. - - // %kIdTypeInt = OpTypeInt 32 1 - kIdTypeInt, - // %kIdTypePointerInputInt = OpTypePointer Input %kIdTypeInt - kIdTypePointerInputInt, - - // Input-attachment-related IDs. There can only be one input attachment, so these are unique. - // %kIdTypeImageSubpassData = OpTypeImage %type SubpassData 0 0 0 2 Unknown - kIdTypeImageSubpassData, - // %kIdVariableImageSubpassData = OpVariable %pointer_to_kIdTypeImageSubpassData UniformConstant - kIdVariableImageSubpassData, - - // All other SPIR-V IDs start from this. - kIdFirstUnreserved, -}; - -} // namespace spirv - } // namespace SkSL #endif diff --git a/gfx/skia/skia/src/sksl/codegen/SkSLGLSLCodeGenerator.cpp b/gfx/skia/skia/src/sksl/codegen/SkSLGLSLCodeGenerator.cpp @@ -32,7 +32,6 @@ #include "src/sksl/SkSLUtil.h" #include "src/sksl/codegen/SkSLCodeGenTypes.h" #include "src/sksl/codegen/SkSLCodeGenerator.h" -#include "src/sksl/codegen/SkSLNativeShader.h" #include "src/sksl/ir/SkSLBinaryExpression.h" #include "src/sksl/ir/SkSLBlock.h" #include "src/sksl/ir/SkSLConstructor.h" @@ -79,7 +78,6 @@ #include <cstdint> #include <initializer_list> #include <memory> -#include <string> #include <string_view> #include <vector> @@ -956,7 +954,7 @@ void GLSLCodeGenerator::writeConstructorDiagonalMatrix(const ConstructorDiagonal Precedence parentPrecedence) { if (c.type().columns() == 4 && c.type().rows() == 2) { // Due to a longstanding bug in glslang and Mesa, several GPU drivers generate diagonal 4x2 - // matrices incorrectly. (skbug.com/40043085, https://github.com/KhronosGroup/glslang/pull/2646) + // matrices incorrectly. (skia:12003, https://github.com/KhronosGroup/glslang/pull/2646) // We can work around this issue by multiplying a scalar by the identity matrix. // In practice, this doesn't come up naturally in real code and we don't know every affected // driver, so we just apply this workaround everywhere. @@ -977,7 +975,7 @@ void GLSLCodeGenerator::writeConstructorCompound(const ConstructorCompound& c, // ... and that argument is a vec4... const Expression& expr = *c.arguments().front(); if (expr.type().isVector() && expr.type().columns() == 4) { - // ... let's rewrite the cast to dodge issues on very old GPUs. (skbug.com/40043276) + // ... let's rewrite the cast to dodge issues on very old GPUs. (skia:13559) if (Analysis::IsTrivialExpression(expr)) { this->writeType(c.type()); this->write("("); @@ -1866,7 +1864,7 @@ void GLSLCodeGenerator::writeSwitchStatement(const SwitchStatement& s) { fIndentation++; // If a switch contains only a `default` case and nothing else, this confuses some drivers and // can lead to a crash. Adding a real case before the default seems to work around the bug, - // and doesn't change the meaning of the switch. (skbug.com/40043548) + // and doesn't change the meaning of the switch. (skia:12465) if (s.cases().size() == 1 && s.cases().front()->as<SwitchCase>().isDefault()) { this->writeLine("case 0:"); } @@ -2070,12 +2068,12 @@ bool ToGLSL(Program& program, const ShaderCaps* caps, OutputStream& out) { return ToGLSL(program, caps, out, defaultPrintOpts); } -bool ToGLSL(Program& program, const ShaderCaps* caps, NativeShader* out) { +bool ToGLSL(Program& program, const ShaderCaps* caps, std::string* out) { StringStream buffer; if (!ToGLSL(program, caps, buffer)) { return false; } - out->fText = buffer.str(); + *out = buffer.str(); return true; } diff --git a/gfx/skia/skia/src/sksl/codegen/SkSLGLSLCodeGenerator.h b/gfx/skia/skia/src/sksl/codegen/SkSLGLSLCodeGenerator.h @@ -8,9 +8,10 @@ #ifndef SKSL_GLSLCODEGENERATOR #define SKSL_GLSLCODEGENERATOR +#include <string> + namespace SkSL { -struct NativeShader; enum class PrettyPrint : bool; class OutputStream; struct Program; @@ -19,7 +20,7 @@ struct ShaderCaps; /** Converts a Program into GLSL code. */ bool ToGLSL(Program& program, const ShaderCaps* caps, OutputStream& out, PrettyPrint); bool ToGLSL(Program& program, const ShaderCaps* caps, OutputStream& out); -bool ToGLSL(Program& program, const ShaderCaps* caps, NativeShader* out); +bool ToGLSL(Program& program, const ShaderCaps* caps, std::string* out); } // namespace SkSL diff --git a/gfx/skia/skia/src/sksl/codegen/SkSLHLSLCodeGenerator.cpp b/gfx/skia/skia/src/sksl/codegen/SkSLHLSLCodeGenerator.cpp @@ -41,7 +41,7 @@ bool ToHLSL(Program& program, const ShaderCaps* caps, std::string* out, ValidateSPIRVProc validateSPIRV) { - std::vector<uint32_t> spirv; + std::string spirv; if (!ToSPIRV(program, caps, &spirv, validateSPIRV)) { return false; } diff --git a/gfx/skia/skia/src/sksl/codegen/SkSLHLSLCodeGenerator.h b/gfx/skia/skia/src/sksl/codegen/SkSLHLSLCodeGenerator.h @@ -8,11 +8,8 @@ #ifndef SKSL_HLSLCODEGENERATOR #define SKSL_HLSLCODEGENERATOR -#include "include/core/SkSpan.h" -#include "src/sksl/codegen/SkSLNativeShader.h" - -#include <cstdint> #include <string> +#include <string_view> namespace SkSL { @@ -21,7 +18,7 @@ class OutputStream; struct Program; struct ShaderCaps; -using ValidateSPIRVProc = bool (*)(ErrorReporter&, SkSpan<const uint32_t>); +using ValidateSPIRVProc = bool (*)(ErrorReporter&, std::string_view); /** Converts a Program into HLSL code. */ bool ToHLSL(Program& program, @@ -31,8 +28,8 @@ bool ToHLSL(Program& program, bool ToHLSL(Program& program, const ShaderCaps* caps, std::string* out, ValidateSPIRVProc); // This explicit overload is used by SkSLToBackend. -inline bool ToHLSL(Program& program, const ShaderCaps* caps, NativeShader* out) { - return ToHLSL(program, caps, &out->fText, (ValidateSPIRVProc) nullptr); +inline bool ToHLSL(Program& program, const ShaderCaps* caps, std::string* out) { + return ToHLSL(program, caps, out, nullptr); } } // namespace SkSL diff --git a/gfx/skia/skia/src/sksl/codegen/SkSLMetalCodeGenerator.cpp b/gfx/skia/skia/src/sksl/codegen/SkSLMetalCodeGenerator.cpp @@ -32,7 +32,6 @@ #include "src/sksl/analysis/SkSLProgramVisitor.h" #include "src/sksl/codegen/SkSLCodeGenTypes.h" #include "src/sksl/codegen/SkSLCodeGenerator.h" -#include "src/sksl/codegen/SkSLNativeShader.h" #include "src/sksl/ir/SkSLBinaryExpression.h" #include "src/sksl/ir/SkSLBlock.h" #include "src/sksl/ir/SkSLConstructor.h" @@ -241,8 +240,6 @@ protected: void writeSimpleIntrinsic(const FunctionCall& c); - void writeScalarizedIntrinsicCall(const FunctionCall& c); - bool writeIntrinsicCall(const FunctionCall& c, IntrinsicKind kind); void writeConstructorCompound(const ConstructorCompound& c, Precedence parentPrecedence); @@ -355,7 +352,7 @@ protected: // If we might use an index expression more than once, we need to capture the result in a // temporary variable to avoid double-evaluation. This should generally only occur when emitting - // a function call, since we need to polyfill GLSL-style out-parameter support. (skbug.com/40045204) + // a function call, since we need to polyfill GLSL-style out-parameter support. (skia:14130) // The map holds <index-expression, temp-variable name>. using IndexSubstitutionMap = skia_private::THashMap<const Expression*, std::string>; @@ -878,36 +875,6 @@ void MetalCodeGenerator::writeArgumentList(const ExpressionArray& arguments) { this->write(")"); } -void MetalCodeGenerator::writeScalarizedIntrinsicCall(const FunctionCall& c){ - SkASSERT(!c.arguments().empty()); - const ExpressionArray& arguments = c.arguments(); - const Expression& primaryArg = *arguments[0]; - int columns = primaryArg.type().columns(); - - static constexpr std::array<const char*, 4> kSwizzleChars = { "x", "y", "z", "w" }; - this->writeWithIndexSubstitution([&]() { - this->writeType(primaryArg.type()); - this->write("("); - for (int i = 0; i < columns; ++i) { - if (i) { this->write(", "); } - this->write(c.function().mangledName()); - this->write("("); - for (int32_t j = 0; j < arguments.size(); ++j) { - if (j) { this->write(", "); } - if (arguments[j]->type().isScalar()) { - this->writeExpression(*arguments[j], Precedence::kSequence); - } else { - this->writeIndexInnerExpression(*arguments[j]); - this->write("."); - this->write(kSwizzleChars[i]); - } - } - this->write(")"); - } - this->write(")"); - }); -} - bool MetalCodeGenerator::writeIntrinsicCall(const FunctionCall& c, IntrinsicKind kind) { const ExpressionArray& arguments = c.arguments(); switch (kind) { @@ -1353,21 +1320,6 @@ bool MetalCodeGenerator::writeIntrinsicCall(const FunctionCall& c, IntrinsicKind this->writeExpression(*c.arguments()[1], Precedence::kSequence); this->write(", memory_order_relaxed)"); return true; - case k_min_IntrinsicKind: - [[fallthrough]]; - case k_max_IntrinsicKind: - [[fallthrough]]; - case k_clamp_IntrinsicKind: { - SkASSERT(c.function().mangledName() == "min" || c.function().mangledName() == "max" || - c.function().mangledName() == "clamp"); - SkASSERT(!c.type().isMatrix()); - if (fCaps.fVectorClampMinMaxSupport || !c.type().isVector()) { - this->writeSimpleIntrinsic(c); - } else { - this->writeScalarizedIntrinsicCall(c); - } - return true; - } default: return false; } @@ -3722,12 +3674,12 @@ bool ToMetal(Program& program, const ShaderCaps* caps, OutputStream& out) { return ToMetal(program, caps, out, defaultPrintOpts); } -bool ToMetal(Program& program, const ShaderCaps* caps, NativeShader* out) { +bool ToMetal(Program& program, const ShaderCaps* caps, std::string* out) { StringStream buffer; if (!ToMetal(program, caps, buffer)) { return false; } - out->fText = buffer.str(); + *out = buffer.str(); return true; } diff --git a/gfx/skia/skia/src/sksl/codegen/SkSLMetalCodeGenerator.h b/gfx/skia/skia/src/sksl/codegen/SkSLMetalCodeGenerator.h @@ -8,9 +8,10 @@ #ifndef SKSL_METALCODEGENERATOR #define SKSL_METALCODEGENERATOR +#include <string> + namespace SkSL { -struct NativeShader; enum class PrettyPrint : bool; class OutputStream; struct Program; @@ -21,7 +22,7 @@ struct ShaderCaps; */ bool ToMetal(Program& program, const ShaderCaps* caps, OutputStream& out, PrettyPrint); bool ToMetal(Program& program, const ShaderCaps* caps, OutputStream& out); -bool ToMetal(Program& program, const ShaderCaps* caps, NativeShader* out); +bool ToMetal(Program& program, const ShaderCaps* caps, std::string* out); } // namespace SkSL diff --git a/gfx/skia/skia/src/sksl/codegen/SkSLNativeShader.h b/gfx/skia/skia/src/sksl/codegen/SkSLNativeShader.h @@ -1,28 +0,0 @@ -/* - * 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 SKSL_NATIVESHADER -#define SKSL_NATIVESHADER - -#include <cstdint> -#include <string> -#include <vector> - -namespace SkSL { - -// After compiling SkSL to native, the result is either in text or binary form. Currently, only -// SPIR-V is in binary form. -struct NativeShader { - std::string fText; - std::vector<uint32_t> fBinary; - - bool isBinary() const { return !fBinary.empty(); } -}; - -} // namespace SkSL - -#endif // SKSL_NATIVESHADER diff --git a/gfx/skia/skia/src/sksl/codegen/SkSLSPIRVCodeGenerator.cpp b/gfx/skia/skia/src/sksl/codegen/SkSLSPIRVCodeGenerator.cpp @@ -30,9 +30,9 @@ #include "src/sksl/SkSLPool.h" #include "src/sksl/SkSLPosition.h" #include "src/sksl/SkSLProgramSettings.h" +#include "src/sksl/SkSLStringStream.h" #include "src/sksl/SkSLUtil.h" #include "src/sksl/analysis/SkSLSpecialization.h" -#include "src/sksl/codegen/SkSLCodeGenTypes.h" #include "src/sksl/codegen/SkSLCodeGenerator.h" #include "src/sksl/ir/SkSLBinaryExpression.h" #include "src/sksl/ir/SkSLBlock.h" @@ -151,12 +151,6 @@ static SpvStorageClass get_storage_class_spv_id(StorageClass storageClass) { SkUNREACHABLE; } -using SPIRVBlob = std::vector<uint32_t>; - -static void append_blob(const SPIRVBlob& src, SPIRVBlob& dst) { - dst.insert(dst.end(), src.begin(), src.end()); -} - class SPIRVCodeGenerator : public CodeGenerator { public: // We reserve an impossible SpvId as a sentinel. (NA meaning none, n/a, etc.) @@ -184,33 +178,16 @@ public: // Returns the storage class of the lvalue. virtual StorageClass storageClass() const = 0; - virtual SpvId load(SPIRVBlob& out) = 0; + virtual SpvId load(OutputStream& out) = 0; - virtual void store(SpvId value, SPIRVBlob& out) = 0; + virtual void store(SpvId value, OutputStream& out) = 0; }; SPIRVCodeGenerator(const Context* context, const ShaderCaps* caps, const Program* program, - SPIRVBlob* out) - : CodeGenerator(context, caps, program, nullptr), fOutBuffer(out) { - - // Preallocate something reasonable for the different SPIR-V sections. - // Based on statistics from dm tests: - // - // Total word count of the shader is ~2000 on average, ~12000 max. - // fGlobalInitializersBuffer: Nearly empty on average, ~10 max. - // fConstantBuffer: ~250 average, ~1000 max. - // fVariableBuffer: ~30 average, ~500 max. - // fNameBuffer: ~200 average, ~1000 max. - // fDecorationBuffer: ~250 average, ~2000 max. - fOutBuffer->reserve(2048); - fGlobalInitializersBuffer.reserve(16); - fConstantBuffer.reserve(256); - fVariableBuffer.reserve(32); - fNameBuffer.reserve(128); - fDecorationBuffer.reserve(256); - } + OutputStream* out) + : CodeGenerator(context, caps, program, out) {} bool generateCode() override; @@ -286,7 +263,7 @@ private: StorageClass getStorageClass(const Expression& expr); - TArray<SpvId> getAccessChain(const Expression& expr, SPIRVBlob& out); + TArray<SpvId> getAccessChain(const Expression& expr, OutputStream& out); void writeLayout(const Layout& layout, SpvId target, Position pos); @@ -294,54 +271,54 @@ private: SpvId writeStruct(const Type& type, const MemoryLayout& memoryLayout); - void writeProgramElement(const ProgramElement& pe, SPIRVBlob& out); + void writeProgramElement(const ProgramElement& pe, OutputStream& out); SpvId writeInterfaceBlock(const InterfaceBlock& intf, bool appendRTFlip = true); - void writeFunctionStart(const FunctionDeclaration& f, SPIRVBlob& out); + void writeFunctionStart(const FunctionDeclaration& f, OutputStream& out); - SpvId writeFunctionDeclaration(const FunctionDeclaration& f, SPIRVBlob& out); + SpvId writeFunctionDeclaration(const FunctionDeclaration& f, OutputStream& out); - void writeFunction(const FunctionDefinition& f, SPIRVBlob& out); + void writeFunction(const FunctionDefinition& f, OutputStream& out); // Writes the function with the defined specializationIndex, if the index is -1, then it is // assumed that the function has no specializations. void writeFunctionInstantiation(const FunctionDefinition& f, Analysis::SpecializationIndex specializationIndex, const Analysis::SpecializedParameters* specializedParams, - SPIRVBlob& out); + OutputStream& out); bool writeGlobalVarDeclaration(ProgramKind kind, const VarDeclaration& v); SpvId writeGlobalVar(ProgramKind kind, StorageClass, const Variable& v); - void writeVarDeclaration(const VarDeclaration& var, SPIRVBlob& out); + void writeVarDeclaration(const VarDeclaration& var, OutputStream& out); - SpvId writeVariableReference(const VariableReference& ref, SPIRVBlob& out); + SpvId writeVariableReference(const VariableReference& ref, OutputStream& out); int findUniformFieldIndex(const Variable& var) const; - std::unique_ptr<LValue> getLValue(const Expression& value, SPIRVBlob& out); + std::unique_ptr<LValue> getLValue(const Expression& value, OutputStream& out); - SpvId writeExpression(const Expression& expr, SPIRVBlob& out); + SpvId writeExpression(const Expression& expr, OutputStream& out); - SpvId writeIntrinsicCall(const FunctionCall& c, SPIRVBlob& out); + SpvId writeIntrinsicCall(const FunctionCall& c, OutputStream& out); void writeFunctionCallArgument(TArray<SpvId>& argumentList, const FunctionCall& call, int argIndex, std::vector<TempVar>* tempVars, const SkBitSet* specializedParams, - SPIRVBlob& out); + OutputStream& out); - void copyBackTempVars(const std::vector<TempVar>& tempVars, SPIRVBlob& out); + void copyBackTempVars(const std::vector<TempVar>& tempVars, OutputStream& out); - SpvId writeFunctionCall(const FunctionCall& c, SPIRVBlob& out); + SpvId writeFunctionCall(const FunctionCall& c, OutputStream& out); void writeGLSLExtendedInstruction(const Type& type, SpvId id, SpvId floatInst, SpvId signedInst, SpvId unsignedInst, - const TArray<SpvId>& args, SPIRVBlob& out); + const TArray<SpvId>& args, OutputStream& out); /** * Promotes an expression to a vector. If the expression is already a vector with vectorSize @@ -349,7 +326,7 @@ private: * vector (if vectorSize > 1) or returns it unmodified (if vectorSize == 1). Asserts if the * expression is already a vector and it does not have vectorSize columns. */ - SpvId vectorize(const Expression& expr, int vectorSize, SPIRVBlob& out); + SpvId vectorize(const Expression& expr, int vectorSize, OutputStream& out); /** * Given a list of potentially mixed scalars and vectors, promotes the scalars to match the @@ -357,74 +334,74 @@ private: * returns (vec2(float), vec2). It is an error to use mismatched vector sizes, e.g. (float, * vec2, vec3). */ - TArray<SpvId> vectorize(const ExpressionArray& args, SPIRVBlob& out); + TArray<SpvId> vectorize(const ExpressionArray& args, OutputStream& out); /** * Given a SpvId of a scalar, splats it across the passed-in type (scalar, vector or matrix) and * returns the SpvId of the new value. */ - SpvId splat(const Type& type, SpvId id, SPIRVBlob& out); + SpvId splat(const Type& type, SpvId id, OutputStream& out); - SpvId writeSpecialIntrinsic(const FunctionCall& c, SpecialIntrinsic kind, SPIRVBlob& out); + SpvId writeSpecialIntrinsic(const FunctionCall& c, SpecialIntrinsic kind, OutputStream& out); SpvId writeAtomicIntrinsic(const FunctionCall& c, SpecialIntrinsic kind, SpvId resultId, - SPIRVBlob& out); + OutputStream& out); SpvId castScalarToFloat(SpvId inputId, const Type& inputType, const Type& outputType, - SPIRVBlob& out); + OutputStream& out); SpvId castScalarToSignedInt(SpvId inputId, const Type& inputType, const Type& outputType, - SPIRVBlob& out); + OutputStream& out); SpvId castScalarToUnsignedInt(SpvId inputId, const Type& inputType, const Type& outputType, - SPIRVBlob& out); + OutputStream& out); SpvId castScalarToBoolean(SpvId inputId, const Type& inputType, const Type& outputType, - SPIRVBlob& out); + OutputStream& out); SpvId castScalarToType(SpvId inputExprId, const Type& inputType, const Type& outputType, - SPIRVBlob& out); + OutputStream& out); /** * Writes a potentially-different-sized copy of a matrix. Entries which do not exist in the * source matrix are filled with zero; entries which do not exist in the destination matrix are * ignored. */ - SpvId writeMatrixCopy(SpvId src, const Type& srcType, const Type& dstType, SPIRVBlob& out); + SpvId writeMatrixCopy(SpvId src, const Type& srcType, const Type& dstType, OutputStream& out); void addColumnEntry(const Type& columnType, TArray<SpvId>* currentColumn, TArray<SpvId>* columnIds, int rows, SpvId entry, - SPIRVBlob& out); + OutputStream& out); - SpvId writeConstructorCompound(const ConstructorCompound& c, SPIRVBlob& out); + SpvId writeConstructorCompound(const ConstructorCompound& c, OutputStream& out); - SpvId writeMatrixConstructor(const ConstructorCompound& c, SPIRVBlob& out); + SpvId writeMatrixConstructor(const ConstructorCompound& c, OutputStream& out); - SpvId writeVectorConstructor(const ConstructorCompound& c, SPIRVBlob& out); + SpvId writeVectorConstructor(const ConstructorCompound& c, OutputStream& out); - SpvId writeCompositeConstructor(const AnyConstructor& c, SPIRVBlob& out); + SpvId writeCompositeConstructor(const AnyConstructor& c, OutputStream& out); - SpvId writeConstructorDiagonalMatrix(const ConstructorDiagonalMatrix& c, SPIRVBlob& out); + SpvId writeConstructorDiagonalMatrix(const ConstructorDiagonalMatrix& c, OutputStream& out); - SpvId writeConstructorMatrixResize(const ConstructorMatrixResize& c, SPIRVBlob& out); + SpvId writeConstructorMatrixResize(const ConstructorMatrixResize& c, OutputStream& out); - SpvId writeConstructorScalarCast(const ConstructorScalarCast& c, SPIRVBlob& out); + SpvId writeConstructorScalarCast(const ConstructorScalarCast& c, OutputStream& out); - SpvId writeConstructorSplat(const ConstructorSplat& c, SPIRVBlob& out); + SpvId writeConstructorSplat(const ConstructorSplat& c, OutputStream& out); - SpvId writeConstructorCompoundCast(const ConstructorCompoundCast& c, SPIRVBlob& out); + SpvId writeConstructorCompoundCast(const ConstructorCompoundCast& c, OutputStream& out); - SpvId writeFieldAccess(const FieldAccess& f, SPIRVBlob& out); + SpvId writeFieldAccess(const FieldAccess& f, OutputStream& out); SpvId writeSwizzle(const Expression& baseExpr, const ComponentArray& components, - SPIRVBlob& out); + OutputStream& out); - SpvId writeSwizzle(const Swizzle& swizzle, SPIRVBlob& out); + SpvId writeSwizzle(const Swizzle& swizzle, OutputStream& out); /** * Folds the potentially-vector result of a logical operation down to a single bool. If @@ -432,23 +409,23 @@ private: * same dimensions, and applys all() to it to fold it down to a single bool value. Otherwise, * returns the original id value. */ - SpvId foldToBool(SpvId id, const Type& operandType, SpvOp op, SPIRVBlob& out); + SpvId foldToBool(SpvId id, const Type& operandType, SpvOp op, OutputStream& out); SpvId writeMatrixComparison(const Type& operandType, SpvId lhs, SpvId rhs, SpvOp_ floatOperator, SpvOp_ intOperator, SpvOp_ vectorMergeOperator, - SpvOp_ mergeOperator, SPIRVBlob& out); + SpvOp_ mergeOperator, OutputStream& out); SpvId writeStructComparison(const Type& structType, SpvId lhs, Operator op, SpvId rhs, - SPIRVBlob& out); + OutputStream& out); SpvId writeArrayComparison(const Type& structType, SpvId lhs, Operator op, SpvId rhs, - SPIRVBlob& out); + OutputStream& out); // Used by writeStructComparison and writeArrayComparison to logically combine field-by-field // comparisons into an overall comparison result. // - `a.x == b.x` merged with `a.y == b.y` generates `(a.x == b.x) && (a.y == b.y)` // - `a.x != b.x` merged with `a.y != b.y` generates `(a.x != b.x) || (a.y != b.y)` - SpvId mergeComparisons(SpvId comparison, SpvId allComparisons, Operator op, SPIRVBlob& out); + SpvId mergeComparisons(SpvId comparison, SpvId allComparisons, Operator op, OutputStream& out); // When the RewriteMatrixVectorMultiply caps bit is set, we manually decompose the M*V // multiplication into a sum of vector-scalar products. @@ -457,117 +434,117 @@ private: const Type& rightType, SpvId rhs, const Type& resultType, - SPIRVBlob& out); + OutputStream& out); SpvId writeComponentwiseMatrixUnary(const Type& operandType, SpvId operand, SpvOp_ op, - SPIRVBlob& out); + OutputStream& out); SpvId writeComponentwiseMatrixBinary(const Type& operandType, SpvId lhs, SpvId rhs, - SpvOp_ op, SPIRVBlob& out); + SpvOp_ op, OutputStream& out); SpvId writeBinaryOperationComponentwiseIfMatrix(const Type& resultType, const Type& operandType, SpvId lhs, SpvId rhs, SpvOp_ ifFloat, SpvOp_ ifInt, SpvOp_ ifUInt, SpvOp_ ifBool, - SPIRVBlob& out); + OutputStream& out); SpvId writeBinaryOperation(const Type& resultType, const Type& operandType, SpvId lhs, SpvId rhs, SpvOp_ ifFloat, SpvOp_ ifInt, SpvOp_ ifUInt, - SpvOp_ ifBool, SPIRVBlob& out); + SpvOp_ ifBool, OutputStream& out); SpvId writeBinaryOperation(const Type& resultType, const Type& operandType, SpvId lhs, SpvId rhs, bool writeComponentwiseIfMatrix, SpvOp_ ifFloat, - SpvOp_ ifInt, SpvOp_ ifUInt, SpvOp_ ifBool, SPIRVBlob& out); + SpvOp_ ifInt, SpvOp_ ifUInt, SpvOp_ ifBool, OutputStream& out); - SpvId writeReciprocal(const Type& type, SpvId value, SPIRVBlob& out); + SpvId writeReciprocal(const Type& type, SpvId value, OutputStream& out); SpvId writeBinaryExpression(const Type& leftType, SpvId lhs, Operator op, const Type& rightType, SpvId rhs, const Type& resultType, - SPIRVBlob& out); + OutputStream& out); - SpvId writeBinaryExpression(const BinaryExpression& b, SPIRVBlob& out); + SpvId writeBinaryExpression(const BinaryExpression& b, OutputStream& out); - SpvId writeTernaryExpression(const TernaryExpression& t, SPIRVBlob& out); + SpvId writeTernaryExpression(const TernaryExpression& t, OutputStream& out); - SpvId writeIndexExpression(const IndexExpression& expr, SPIRVBlob& out); + SpvId writeIndexExpression(const IndexExpression& expr, OutputStream& out); - SpvId writeLogicalAnd(const Expression& left, const Expression& right, SPIRVBlob& out); + SpvId writeLogicalAnd(const Expression& left, const Expression& right, OutputStream& out); - SpvId writeLogicalOr(const Expression& left, const Expression& right, SPIRVBlob& out); + SpvId writeLogicalOr(const Expression& left, const Expression& right, OutputStream& out); - SpvId writePrefixExpression(const PrefixExpression& p, SPIRVBlob& out); + SpvId writePrefixExpression(const PrefixExpression& p, OutputStream& out); - SpvId writePostfixExpression(const PostfixExpression& p, SPIRVBlob& out); + SpvId writePostfixExpression(const PostfixExpression& p, OutputStream& out); SpvId writeLiteral(const Literal& f); SpvId writeLiteral(double value, const Type& type); - void writeStatement(const Statement& s, SPIRVBlob& out); + void writeStatement(const Statement& s, OutputStream& out); - void writeBlock(const Block& b, SPIRVBlob& out); + void writeBlock(const Block& b, OutputStream& out); - void writeIfStatement(const IfStatement& stmt, SPIRVBlob& out); + void writeIfStatement(const IfStatement& stmt, OutputStream& out); - void writeForStatement(const ForStatement& f, SPIRVBlob& out); + void writeForStatement(const ForStatement& f, OutputStream& out); - void writeDoStatement(const DoStatement& d, SPIRVBlob& out); + void writeDoStatement(const DoStatement& d, OutputStream& out); - void writeSwitchStatement(const SwitchStatement& s, SPIRVBlob& out); + void writeSwitchStatement(const SwitchStatement& s, OutputStream& out); - void writeReturnStatement(const ReturnStatement& r, SPIRVBlob& out); + void writeReturnStatement(const ReturnStatement& r, OutputStream& out); - void writeCapabilities(SPIRVBlob& out); + void writeCapabilities(OutputStream& out); - void writeInstructions(const Program& program, SPIRVBlob& out); + void writeInstructions(const Program& program, OutputStream& out); - void writeOpCode(SpvOp_ opCode, int length, SPIRVBlob& out); + void writeOpCode(SpvOp_ opCode, int length, OutputStream& out); - void writeWord(int32_t word, SPIRVBlob& out); + void writeWord(int32_t word, OutputStream& out); - void writeString(std::string_view s, SPIRVBlob& out); + void writeString(std::string_view s, OutputStream& out); - void writeInstruction(SpvOp_ opCode, SPIRVBlob& out); + void writeInstruction(SpvOp_ opCode, OutputStream& out); - void writeInstruction(SpvOp_ opCode, std::string_view string, SPIRVBlob& out); + void writeInstruction(SpvOp_ opCode, std::string_view string, OutputStream& out); - void writeInstruction(SpvOp_ opCode, int32_t word1, SPIRVBlob& out); + void writeInstruction(SpvOp_ opCode, int32_t word1, OutputStream& out); void writeInstruction(SpvOp_ opCode, int32_t word1, std::string_view string, - SPIRVBlob& out); + OutputStream& out); void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, std::string_view string, - SPIRVBlob& out); + OutputStream& out); - void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, SPIRVBlob& out); + void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, OutputStream& out); void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, int32_t word3, - SPIRVBlob& out); + OutputStream& out); void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, int32_t word3, int32_t word4, - SPIRVBlob& out); + OutputStream& out); void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, int32_t word3, int32_t word4, - int32_t word5, SPIRVBlob& out); + int32_t word5, OutputStream& out); void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, int32_t word3, int32_t word4, - int32_t word5, int32_t word6, SPIRVBlob& out); + int32_t word5, int32_t word6, OutputStream& out); void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, int32_t word3, int32_t word4, - int32_t word5, int32_t word6, int32_t word7, SPIRVBlob& out); + int32_t word5, int32_t word6, int32_t word7, OutputStream& out); void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, int32_t word3, int32_t word4, int32_t word5, int32_t word6, int32_t word7, int32_t word8, - SPIRVBlob& out); + OutputStream& out); // This form of writeInstruction can deduplicate redundant ops. struct Word; // 8 Words is enough for nearly all instructions (except variable-length instructions like // OpAccessChain or OpConstantComposite). using Words = STArray<8, Word, true>; - SpvId writeInstruction(SpvOp_ opCode, const TArray<Word, true>& words, SPIRVBlob& out); + SpvId writeInstruction(SpvOp_ opCode, const TArray<Word, true>& words, OutputStream& out); struct Instruction { SpvId fOp; @@ -578,21 +555,19 @@ private: struct Hash; }; - static Instruction BuildInstructionKey(SpvOp_ opCode, - const TArray<Word, true>& words, - SpvId* predefinedResultId); + static Instruction BuildInstructionKey(SpvOp_ opCode, const TArray<Word, true>& words); // The writeOpXxxxx calls will simplify and deduplicate ops where possible. SpvId writeOpConstantTrue(const Type& type); SpvId writeOpConstantFalse(const Type& type); SpvId writeOpConstant(const Type& type, int32_t valueBits); SpvId writeOpConstantComposite(const Type& type, const TArray<SpvId>& values); - SpvId writeOpCompositeConstruct(const Type& type, const TArray<SpvId>&, SPIRVBlob& out); - SpvId writeOpCompositeExtract(const Type& type, SpvId base, int component, SPIRVBlob& out); + SpvId writeOpCompositeConstruct(const Type& type, const TArray<SpvId>&, OutputStream& out); + SpvId writeOpCompositeExtract(const Type& type, SpvId base, int component, OutputStream& out); SpvId writeOpCompositeExtract(const Type& type, SpvId base, int componentA, int componentB, - SPIRVBlob& out); - SpvId writeOpLoad(SpvId type, Precision precision, SpvId pointer, SPIRVBlob& out); - void writeOpStore(StorageClass storageClass, SpvId pointer, SpvId value, SPIRVBlob& out); + OutputStream& out); + SpvId writeOpLoad(SpvId type, Precision precision, SpvId pointer, OutputStream& out); + void writeOpStore(StorageClass storageClass, SpvId pointer, SpvId value, OutputStream& out); // Converts the provided SpvId(s) into an array of scalar OpConstants, if it can be done. bool toConstants(SpvId value, TArray<SpvId>* constants); @@ -636,9 +611,9 @@ private: // Use "BranchesOnBothSides" for labels which have branches coming from both directions. kBranchesOnBothSides, }; - void writeLabel(SpvId label, StraightLineLabelType type, SPIRVBlob& out); + void writeLabel(SpvId label, StraightLineLabelType type, OutputStream& out); void writeLabel(SpvId label, BranchingLabelType type, ConditionalOpCounts ops, - SPIRVBlob& out); + OutputStream& out); MemoryLayout memoryLayoutForStorageClass(StorageClass storageClass); MemoryLayout memoryLayoutForVariable(const Variable&) const; @@ -668,7 +643,7 @@ private: const MemoryLayout fDefaultMemoryLayout{MemoryLayout::Standard::k140}; uint64_t fCapabilities = 0; - SpvId fIdCount = spirv::kIdFirstUnreserved; + SpvId fIdCount = 1; SpvId fGLSLExtendedInstructions; struct Intrinsic { IntrinsicOpcodeKind opKind; @@ -688,15 +663,11 @@ private: THashMap<const Variable*, SpvId> fVariableMap; THashMap<const Type*, SpvId> fStructMap; - - // SPIR-V is defined in 32-bit words, so this generator directly uses std::vector<uint32_t> - // instead of an OutputStream. - SPIRVBlob* fOutBuffer; - SPIRVBlob fGlobalInitializersBuffer; - SPIRVBlob fConstantBuffer; - SPIRVBlob fVariableBuffer; - SPIRVBlob fNameBuffer; - SPIRVBlob fDecorationBuffer; + StringStream fGlobalInitializersBuffer; + StringStream fConstantBuffer; + StringStream fVariableBuffer; + StringStream fNameBuffer; + StringStream fDecorationBuffer; // Mapping from combined sampler declarations to synthesized texture/sampler variables. // This is used when the sampler is declared as `layout(webgpu)` or `layout(direct3d)`. @@ -785,7 +756,6 @@ struct SPIRVCodeGenerator::Word { kRelaxedPrecisionResult, kUniqueResult, kKeyedResult, - kReservedResult, }; Word(SpvId id) : fValue(id), fKind(Kind::kSpvId) {} @@ -811,8 +781,6 @@ struct SPIRVCodeGenerator::Word { return Word{(int32_t)NA, kDefaultPrecisionResult}; } - static Word ReservedResult(spirv::ReservedId id) { return Word{(int32_t)id, kReservedResult}; } - // Unlike a Result (where the result ID is always deduplicated to its first instruction) or a // UniqueResult (which always produces a new instruction), a KeyedResult allows an instruction // to be deduplicated among those that share the same `key`. @@ -991,8 +959,8 @@ SPIRVCodeGenerator::Intrinsic SPIRVCodeGenerator::getIntrinsic(IntrinsicKind ik) } } -void SPIRVCodeGenerator::writeWord(int32_t word, SPIRVBlob& out) { - out.push_back(word); +void SPIRVCodeGenerator::writeWord(int32_t word, OutputStream& out) { + out.write((const char*) &word, sizeof(word)); } static bool is_float(const Type& type) { @@ -1097,7 +1065,7 @@ static bool is_globally_reachable_op(SpvOp_ op) { } } -void SPIRVCodeGenerator::writeOpCode(SpvOp_ opCode, int length, SPIRVBlob& out) { +void SPIRVCodeGenerator::writeOpCode(SpvOp_ opCode, int length, OutputStream& out) { SkASSERT(opCode != SpvOpLoad || &out != &fConstantBuffer); SkASSERT(opCode != SpvOpUndef); bool foundDeadCode = false; @@ -1118,7 +1086,7 @@ void SPIRVCodeGenerator::writeOpCode(SpvOp_ opCode, int length, SPIRVBlob& out) this->writeWord((length << 16) | opCode, out); } -void SPIRVCodeGenerator::writeLabel(SpvId label, StraightLineLabelType, SPIRVBlob& out) { +void SPIRVCodeGenerator::writeLabel(SpvId label, StraightLineLabelType, OutputStream& out) { // The straight-line label type is not important; in any case, no caches are invalidated. SkASSERT(!fCurrentBlock); fCurrentBlock = label; @@ -1126,7 +1094,7 @@ void SPIRVCodeGenerator::writeLabel(SpvId label, StraightLineLabelType, SPIRVBlo } void SPIRVCodeGenerator::writeLabel(SpvId label, BranchingLabelType type, - ConditionalOpCounts ops, SPIRVBlob& out) { + ConditionalOpCounts ops, OutputStream& out) { switch (type) { case kBranchIsBelow: case kBranchesOnBothSides: @@ -1148,38 +1116,48 @@ void SPIRVCodeGenerator::writeLabel(SpvId label, BranchingLabelType type, this->writeLabel(label, kBranchlessBlock, out); } -void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, SPIRVBlob& out) { +void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, OutputStream& out) { this->writeOpCode(opCode, 1, out); } -void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, SPIRVBlob& out) { +void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, OutputStream& out) { this->writeOpCode(opCode, 2, out); this->writeWord(word1, out); } -void SPIRVCodeGenerator::writeString(std::string_view s, SPIRVBlob& out) { - size_t offset = out.size(); - // Add storage for the string + space for the nul character. Memory is zero-initialized so - // nul-termination is not needed later. - out.resize(offset + s.size() / 4 + 1, 0); - memcpy(out.data() + offset, s.data(), s.size()); +void SPIRVCodeGenerator::writeString(std::string_view s, OutputStream& out) { + out.write(s.data(), s.length()); + switch (s.length() % 4) { + case 1: + out.write8(0); + [[fallthrough]]; + case 2: + out.write8(0); + [[fallthrough]]; + case 3: + out.write8(0); + break; + default: + this->writeWord(0, out); + break; + } } void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, std::string_view string, - SPIRVBlob& out) { + OutputStream& out) { this->writeOpCode(opCode, 1 + (string.length() + 4) / 4, out); this->writeString(string, out); } void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, std::string_view string, - SPIRVBlob& out) { + OutputStream& out) { this->writeOpCode(opCode, 2 + (string.length() + 4) / 4, out); this->writeWord(word1, out); this->writeString(string, out); } void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, - std::string_view string, SPIRVBlob& out) { + std::string_view string, OutputStream& out) { this->writeOpCode(opCode, 3 + (string.length() + 4) / 4, out); this->writeWord(word1, out); this->writeWord(word2, out); @@ -1187,14 +1165,14 @@ void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t } void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, - SPIRVBlob& out) { + OutputStream& out) { this->writeOpCode(opCode, 3, out); this->writeWord(word1, out); this->writeWord(word2, out); } void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, - int32_t word3, SPIRVBlob& out) { + int32_t word3, OutputStream& out) { this->writeOpCode(opCode, 4, out); this->writeWord(word1, out); this->writeWord(word2, out); @@ -1202,7 +1180,7 @@ void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t } void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, - int32_t word3, int32_t word4, SPIRVBlob& out) { + int32_t word3, int32_t word4, OutputStream& out) { this->writeOpCode(opCode, 5, out); this->writeWord(word1, out); this->writeWord(word2, out); @@ -1212,7 +1190,7 @@ void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, int32_t word3, int32_t word4, int32_t word5, - SPIRVBlob& out) { + OutputStream& out) { this->writeOpCode(opCode, 6, out); this->writeWord(word1, out); this->writeWord(word2, out); @@ -1223,7 +1201,7 @@ void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, int32_t word3, int32_t word4, int32_t word5, - int32_t word6, SPIRVBlob& out) { + int32_t word6, OutputStream& out) { this->writeOpCode(opCode, 7, out); this->writeWord(word1, out); this->writeWord(word2, out); @@ -1235,7 +1213,7 @@ void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, int32_t word3, int32_t word4, int32_t word5, - int32_t word6, int32_t word7, SPIRVBlob& out) { + int32_t word6, int32_t word7, OutputStream& out) { this->writeOpCode(opCode, 8, out); this->writeWord(word1, out); this->writeWord(word2, out); @@ -1249,7 +1227,7 @@ void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, int32_t word3, int32_t word4, int32_t word5, int32_t word6, int32_t word7, int32_t word8, - SPIRVBlob& out) { + OutputStream& out) { this->writeOpCode(opCode, 9, out); this->writeWord(word1, out); this->writeWord(word2, out); @@ -1262,23 +1240,19 @@ void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t } SPIRVCodeGenerator::Instruction SPIRVCodeGenerator::BuildInstructionKey(SpvOp_ opCode, - const TArray<Word>& words, - SpvId* predefinedResultId) { + const TArray<Word>& words) { // Assemble a cache key for this instruction. Instruction key; key.fOp = opCode; key.fWords.resize(words.size()); key.fResultKind = Word::Kind::kNone; - *predefinedResultId = NA; - for (int index = 0; index < words.size(); ++index) { const Word& word = words[index]; key.fWords[index] = word.fValue; if (word.isResult()) { SkASSERT(key.fResultKind == Word::Kind::kNone); key.fResultKind = word.fKind; - *predefinedResultId = word.fValue; } } @@ -1287,14 +1261,13 @@ SPIRVCodeGenerator::Instruction SPIRVCodeGenerator::BuildInstructionKey(SpvOp_ o SpvId SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, const TArray<Word>& words, - SPIRVBlob& out) { + OutputStream& out) { // writeOpLoad and writeOpStore have dedicated code. SkASSERT(opCode != SpvOpLoad); SkASSERT(opCode != SpvOpStore); // If this instruction exists in our op cache, return the cached SpvId. - SpvId predefinedResultId = NA; - Instruction key = BuildInstructionKey(opCode, words, &predefinedResultId); + Instruction key = BuildInstructionKey(opCode, words); if (SpvId* cachedOp = fOpCache.find(key)) { return *cachedOp; } @@ -1333,17 +1306,6 @@ SpvId SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, } break; - case Word::Kind::kReservedResult: - // The SpvId is predetermined in spirv::ReservedId. - SkASSERT(predefinedResultId != NA); - result = predefinedResultId; - fOpCache.set(key, result); - fSpvIdCache.set(result, key); - // Reserved IDs only make sense for globally-reachable ops; the ops in function bodies - // can never guarantee uniqueness. - SkASSERT(is_globally_reachable_op(opCode)); - break; - default: SkDEBUGFAIL("unexpected result kind"); break; @@ -1367,7 +1329,7 @@ SpvId SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, SpvId SPIRVCodeGenerator::writeOpLoad(SpvId type, Precision precision, SpvId pointer, - SPIRVBlob& out) { + OutputStream& out) { // Look for this pointer in our load-cache. if (SpvId* cachedOp = fStoreCache.find(pointer)) { return *cachedOp; @@ -1382,7 +1344,7 @@ SpvId SPIRVCodeGenerator::writeOpLoad(SpvId type, void SPIRVCodeGenerator::writeOpStore(StorageClass storageClass, SpvId pointer, SpvId value, - SPIRVBlob& out) { + OutputStream& out) { // Write the uncached SpvOpStore directly. this->writeInstruction(SpvOpStore, pointer, value, out); @@ -1463,7 +1425,7 @@ bool SPIRVCodeGenerator::toConstants(SkSpan<const SpvId> values, TArray<SpvId>* SpvId SPIRVCodeGenerator::writeOpCompositeConstruct(const Type& type, const TArray<SpvId>& values, - SPIRVBlob& out) { + OutputStream& out) { // If this is a vector composed entirely of literals, write a constant-composite instead. if (type.isVector()) { STArray<4, SpvId> constants; @@ -1597,7 +1559,7 @@ SpvId SPIRVCodeGenerator::toComponent(SpvId id, int component) { SpvId SPIRVCodeGenerator::writeOpCompositeExtract(const Type& type, SpvId base, int component, - SPIRVBlob& out) { + OutputStream& out) { // If the base op is a composite, we can extract from it directly. SpvId result = this->toComponent(base, component); if (result != NA) { @@ -1613,7 +1575,7 @@ SpvId SPIRVCodeGenerator::writeOpCompositeExtract(const Type& type, SpvId base, int componentA, int componentB, - SPIRVBlob& out) { + OutputStream& out) { // If the base op is a composite, we can extract from it directly. SpvId result = this->toComponent(base, componentA); if (result != NA) { @@ -1628,7 +1590,7 @@ SpvId SPIRVCodeGenerator::writeOpCompositeExtract(const Type& type, out); } -void SPIRVCodeGenerator::writeCapabilities(SPIRVBlob& out) { +void SPIRVCodeGenerator::writeCapabilities(OutputStream& out) { for (uint64_t i = 0, bit = 1; i <= kLast_Capability; i++, bit <<= 1) { if (fCapabilities & bit) { this->writeInstruction(SpvOpCapability, (SpvId) i, out); @@ -1760,8 +1722,10 @@ SpvId SPIRVCodeGenerator::getType(const Type& rawType, return this->writeInstruction(SpvOpTypeBool, {Word::Result()}, fConstantBuffer); } if (type->isSigned()) { - // Predefined type - return spirv::kIdTypeInt; + return this->writeInstruction( + SpvOpTypeInt, + Words{Word::Result(), Word::Number(32), Word::Number(1)}, + fConstantBuffer); } if (type->isUnsigned()) { return this->writeInstruction( @@ -1848,16 +1812,8 @@ SpvId SPIRVCodeGenerator::getType(const Type& rawType, ? layout_flags_to_image_format(typeLayout.fFlags) : SpvImageFormatUnknown; - // Input attachments have a reserved ID. - Word result = Word::Result(); - if (type->dimensions() == SpvDimSubpassData) { - // Only a single input attachment is currently supported. - SkASSERT(typeLayout.fInputAttachmentIndex == 0); - result = Word::ReservedResult(spirv::kIdTypeImageSubpassData); - } - return this->writeInstruction(SpvOpTypeImage, - Words{result, + Words{Word::Result(), floatTypeId, Word::Number(type->dimensions()), Word::Number(type->isDepth()), @@ -1968,7 +1924,7 @@ SpvId SPIRVCodeGenerator::getPointerType(const Type& type, fConstantBuffer); } -SpvId SPIRVCodeGenerator::writeExpression(const Expression& expr, SPIRVBlob& out) { +SpvId SPIRVCodeGenerator::writeExpression(const Expression& expr, OutputStream& out) { switch (expr.kind()) { case Expression::Kind::kBinary: return this->writeBinaryExpression(expr.as<BinaryExpression>(), out); @@ -2018,7 +1974,7 @@ SpvId SPIRVCodeGenerator::writeExpression(const Expression& expr, SPIRVBlob& out return NA; } -SpvId SPIRVCodeGenerator::writeIntrinsicCall(const FunctionCall& c, SPIRVBlob& out) { +SpvId SPIRVCodeGenerator::writeIntrinsicCall(const FunctionCall& c, OutputStream& out) { const FunctionDeclaration& function = c.function(); Intrinsic intrinsic = this->getIntrinsic(function.intrinsicKind()); if (intrinsic.opKind == kInvalid_IntrinsicOpcodeKind) { @@ -2093,7 +2049,7 @@ SpvId SPIRVCodeGenerator::writeIntrinsicCall(const FunctionCall& c, SPIRVBlob& o } } -SpvId SPIRVCodeGenerator::vectorize(const Expression& arg, int vectorSize, SPIRVBlob& out) { +SpvId SPIRVCodeGenerator::vectorize(const Expression& arg, int vectorSize, OutputStream& out) { SkASSERT(vectorSize >= 1 && vectorSize <= 4); const Type& argType = arg.type(); if (argType.isScalar() && vectorSize > 1) { @@ -2105,7 +2061,7 @@ SpvId SPIRVCodeGenerator::vectorize(const Expression& arg, int vectorSize, SPIRV return this->writeExpression(arg, out); } -TArray<SpvId> SPIRVCodeGenerator::vectorize(const ExpressionArray& args, SPIRVBlob& out) { +TArray<SpvId> SPIRVCodeGenerator::vectorize(const ExpressionArray& args, OutputStream& out) { int vectorSize = 1; for (const auto& a : args) { if (a->type().isVector()) { @@ -2127,7 +2083,7 @@ TArray<SpvId> SPIRVCodeGenerator::vectorize(const ExpressionArray& args, SPIRVBl void SPIRVCodeGenerator::writeGLSLExtendedInstruction(const Type& type, SpvId id, SpvId floatInst, SpvId signedInst, SpvId unsignedInst, const TArray<SpvId>& args, - SPIRVBlob& out) { + OutputStream& out) { this->writeOpCode(SpvOpExtInst, 5 + args.size(), out); this->writeWord(this->getType(type), out); this->writeWord(id, out); @@ -2139,15 +2095,12 @@ void SPIRVCodeGenerator::writeGLSLExtendedInstruction(const Type& type, SpvId id } SpvId SPIRVCodeGenerator::writeSpecialIntrinsic(const FunctionCall& c, SpecialIntrinsic kind, - SPIRVBlob& out) { + OutputStream& out) { const ExpressionArray& arguments = c.arguments(); const Type& callType = c.type(); - // Note: MatrixCompMult creates its own result ID, avoid calling nextId as it could generate a - // RelaxedPrecision decoration on an ID that would never be used. - SpvId result = 0; + SpvId result = this->nextId(nullptr); switch (kind) { case kAtan_SpecialIntrinsic: { - result = this->nextId(&callType); STArray<2, SpvId> argumentIds; for (const std::unique_ptr<Expression>& arg : arguments) { argumentIds.push_back(this->writeExpression(*arg, out)); @@ -2163,7 +2116,6 @@ SpvId SPIRVCodeGenerator::writeSpecialIntrinsic(const FunctionCall& c, SpecialIn break; } case kSampledImage_SpecialIntrinsic: { - result = this->nextId(&callType); SkASSERT(arguments.size() == 2); SpvId img = this->writeExpression(*arguments[0], out); SpvId sampler = this->writeExpression(*arguments[1], out); @@ -2176,7 +2128,6 @@ SpvId SPIRVCodeGenerator::writeSpecialIntrinsic(const FunctionCall& c, SpecialIn break; } case kSubpassLoad_SpecialIntrinsic: { - result = this->nextId(&callType); SpvId img = this->writeExpression(*arguments[0], out); ExpressionArray args; args.reserve_exact(2); @@ -2236,12 +2187,6 @@ SpvId SPIRVCodeGenerator::writeSpecialIntrinsic(const FunctionCall& c, SpecialIn case SpvDimSubpassData: break; } - // Work around Nvidia bug when RelaxedPrecision is applied to - // YCbCr texture sampling op. (skbug.com/421927604) - const bool avoidRelaxedPrecision = op == SpvOpImageSampleImplicitLod && - fCaps.fCannotUseRelaxedPrecisionOnImageSample; - result = this->nextId(avoidRelaxedPrecision ? nullptr : &callType); - SpvId type = this->getType(callType); SpvId sampler = this->writeExpression(*arguments[0], out); SpvId uv = this->writeExpression(*arguments[1], out); @@ -2265,7 +2210,6 @@ SpvId SPIRVCodeGenerator::writeSpecialIntrinsic(const FunctionCall& c, SpecialIn break; } case kTextureGrad_SpecialIntrinsic: { - result = this->nextId(&callType); SpvOp_ op = SpvOpImageSampleExplicitLod; SkASSERT(arguments.size() == 4); SkASSERT(arguments[0]->type().dimensions() == SpvDim2D); @@ -2282,7 +2226,6 @@ SpvId SPIRVCodeGenerator::writeSpecialIntrinsic(const FunctionCall& c, SpecialIn break; } case kTextureLod_SpecialIntrinsic: { - result = this->nextId(&callType); SpvOp_ op = SpvOpImageSampleExplicitLod; SkASSERT(arguments.size() == 3); SkASSERT(arguments[0]->type().dimensions() == SpvDim2D); @@ -2303,7 +2246,6 @@ SpvId SPIRVCodeGenerator::writeSpecialIntrinsic(const FunctionCall& c, SpecialIn break; } case kTextureRead_SpecialIntrinsic: { - result = this->nextId(&callType); SkASSERT(arguments[0]->type().dimensions() == SpvDim2D); SkASSERT(arguments[1]->type().matches(*fContext.fTypes.fUInt2)); @@ -2337,7 +2279,6 @@ SpvId SPIRVCodeGenerator::writeSpecialIntrinsic(const FunctionCall& c, SpecialIn SkASSERT(arguments[0]->type().dimensions() == SpvDim2D); SkASSERT(arguments[1]->type().matches(*fContext.fTypes.fUInt2)); SkASSERT(arguments[2]->type().matches(*fContext.fTypes.fHalf4)); - SkASSERT(!callType.hasPrecision()); SpvId image = this->writeExpression(*arguments[0], out); SpvId coord = this->writeExpression(*arguments[1], out); @@ -2348,12 +2289,11 @@ SpvId SPIRVCodeGenerator::writeSpecialIntrinsic(const FunctionCall& c, SpecialIn } case kTextureWidth_SpecialIntrinsic: case kTextureHeight_SpecialIntrinsic: { - result = this->nextId(&callType); SkASSERT(arguments[0]->type().dimensions() == SpvDim2D); fCapabilities |= 1ULL << SpvCapabilityImageQuery; SpvId dimsType = this->getType(*fContext.fTypes.fUInt2); - SpvId dims = this->nextId(&callType); + SpvId dims = this->nextId(nullptr); SpvId image = this->writeExpression(*arguments[0], out); this->writeInstruction(SpvOpImageQuerySize, dimsType, dims, image, out); @@ -2363,7 +2303,6 @@ SpvId SPIRVCodeGenerator::writeSpecialIntrinsic(const FunctionCall& c, SpecialIn break; } case kMod_SpecialIntrinsic: { - result = this->nextId(&callType); TArray<SpvId> args = this->vectorize(arguments, out); SkASSERT(args.size() == 2); const Type& operandType = arguments[0]->type(); @@ -2377,7 +2316,6 @@ SpvId SPIRVCodeGenerator::writeSpecialIntrinsic(const FunctionCall& c, SpecialIn break; } case kDFdy_SpecialIntrinsic: { - result = this->nextId(&callType); SpvId fn = this->writeExpression(*arguments[0], out); this->writeOpCode(SpvOpDPdy, 4, out); this->writeWord(this->getType(callType), out); @@ -2399,7 +2337,6 @@ SpvId SPIRVCodeGenerator::writeSpecialIntrinsic(const FunctionCall& c, SpecialIn break; } case kClamp_SpecialIntrinsic: { - result = this->nextId(&callType); TArray<SpvId> args = this->vectorize(arguments, out); SkASSERT(args.size() == 3); this->writeGLSLExtendedInstruction(callType, result, GLSLstd450FClamp, GLSLstd450SClamp, @@ -2407,7 +2344,6 @@ SpvId SPIRVCodeGenerator::writeSpecialIntrinsic(const FunctionCall& c, SpecialIn break; } case kMax_SpecialIntrinsic: { - result = this->nextId(&callType); TArray<SpvId> args = this->vectorize(arguments, out); SkASSERT(args.size() == 2); this->writeGLSLExtendedInstruction(callType, result, GLSLstd450FMax, GLSLstd450SMax, @@ -2415,7 +2351,6 @@ SpvId SPIRVCodeGenerator::writeSpecialIntrinsic(const FunctionCall& c, SpecialIn break; } case kMin_SpecialIntrinsic: { - result = this->nextId(&callType); TArray<SpvId> args = this->vectorize(arguments, out); SkASSERT(args.size() == 2); this->writeGLSLExtendedInstruction(callType, result, GLSLstd450FMin, GLSLstd450SMin, @@ -2423,7 +2358,6 @@ SpvId SPIRVCodeGenerator::writeSpecialIntrinsic(const FunctionCall& c, SpecialIn break; } case kMix_SpecialIntrinsic: { - result = this->nextId(&callType); TArray<SpvId> args = this->vectorize(arguments, out); SkASSERT(args.size() == 3); if (arguments[2]->type().componentType().isBoolean()) { @@ -2440,7 +2374,6 @@ SpvId SPIRVCodeGenerator::writeSpecialIntrinsic(const FunctionCall& c, SpecialIn break; } case kSaturate_SpecialIntrinsic: { - result = this->nextId(&callType); SkASSERT(arguments.size() == 1); int width = arguments[0]->type().columns(); STArray<3, SpvId> spvArgs{ @@ -2453,7 +2386,6 @@ SpvId SPIRVCodeGenerator::writeSpecialIntrinsic(const FunctionCall& c, SpecialIn break; } case kSmoothStep_SpecialIntrinsic: { - result = this->nextId(&callType); TArray<SpvId> args = this->vectorize(arguments, out); SkASSERT(args.size() == 3); this->writeGLSLExtendedInstruction(callType, result, GLSLstd450SmoothStep, SpvOpUndef, @@ -2461,7 +2393,6 @@ SpvId SPIRVCodeGenerator::writeSpecialIntrinsic(const FunctionCall& c, SpecialIn break; } case kStep_SpecialIntrinsic: { - result = this->nextId(&callType); TArray<SpvId> args = this->vectorize(arguments, out); SkASSERT(args.size() == 2); this->writeGLSLExtendedInstruction(callType, result, GLSLstd450Step, SpvOpUndef, @@ -2478,14 +2409,12 @@ SpvId SPIRVCodeGenerator::writeSpecialIntrinsic(const FunctionCall& c, SpecialIn case kAtomicAdd_SpecialIntrinsic: case kAtomicLoad_SpecialIntrinsic: case kAtomicStore_SpecialIntrinsic: - result = this->nextId(&callType); result = this->writeAtomicIntrinsic(c, kind, result, out); break; case kStorageBarrier_SpecialIntrinsic: case kWorkgroupBarrier_SpecialIntrinsic: { // Both barrier types operate in the workgroup execution and memory scope and differ // only in memory semantics. storageBarrier() is not a device-scope barrier. - SkASSERT(!callType.hasPrecision()); SpvId scopeId = this->writeOpConstant(*fContext.fTypes.fUInt, (int32_t)SpvScopeWorkgroup); int32_t memSemMask = (kind == kStorageBarrier_SpecialIntrinsic) @@ -2501,8 +2430,6 @@ SpvId SPIRVCodeGenerator::writeSpecialIntrinsic(const FunctionCall& c, SpecialIn out); break; } - default: - SkUNREACHABLE; } return result; } @@ -2510,7 +2437,7 @@ SpvId SPIRVCodeGenerator::writeSpecialIntrinsic(const FunctionCall& c, SpecialIn SpvId SPIRVCodeGenerator::writeAtomicIntrinsic(const FunctionCall& c, SpecialIntrinsic kind, SpvId resultId, - SPIRVBlob& out) { + OutputStream& out) { const ExpressionArray& arguments = c.arguments(); SkASSERT(!arguments.empty()); @@ -2592,7 +2519,7 @@ void SPIRVCodeGenerator::writeFunctionCallArgument(TArray<SpvId>& argumentList, int argIndex, std::vector<TempVar>* tempVars, const SkBitSet* specializedParams, - SPIRVBlob& out) { + OutputStream& out) { const FunctionDeclaration& funcDecl = call.function(); const Expression& arg = *call.arguments()[argIndex]; const Variable* param = funcDecl.parameters()[argIndex]; @@ -2681,7 +2608,7 @@ void SPIRVCodeGenerator::writeFunctionCallArgument(TArray<SpvId>& argumentList, argumentList.push_back(tmpVar); } -void SPIRVCodeGenerator::copyBackTempVars(const std::vector<TempVar>& tempVars, SPIRVBlob& out) { +void SPIRVCodeGenerator::copyBackTempVars(const std::vector<TempVar>& tempVars, OutputStream& out) { for (const TempVar& tempVar : tempVars) { SpvId load = this->nextId(tempVar.type); this->writeInstruction(SpvOpLoad, this->getType(*tempVar.type), load, tempVar.spvId, out); @@ -2689,7 +2616,7 @@ void SPIRVCodeGenerator::copyBackTempVars(const std::vector<TempVar>& tempVars, } } -SpvId SPIRVCodeGenerator::writeFunctionCall(const FunctionCall& c, SPIRVBlob& out) { +SpvId SPIRVCodeGenerator::writeFunctionCall(const FunctionCall& c, OutputStream& out) { // Handle intrinsics. const FunctionDeclaration& function = c.function(); if (function.isIntrinsic() && !function.definition()) { @@ -2735,7 +2662,7 @@ SpvId SPIRVCodeGenerator::writeFunctionCall(const FunctionCall& c, SPIRVBlob& ou SpvId SPIRVCodeGenerator::castScalarToType(SpvId inputExprId, const Type& inputType, const Type& outputType, - SPIRVBlob& out) { + OutputStream& out) { if (outputType.isFloat()) { return this->castScalarToFloat(inputExprId, inputType, outputType, out); } @@ -2755,7 +2682,7 @@ SpvId SPIRVCodeGenerator::castScalarToType(SpvId inputExprId, } SpvId SPIRVCodeGenerator::castScalarToFloat(SpvId inputId, const Type& inputType, - const Type& outputType, SPIRVBlob& out) { + const Type& outputType, OutputStream& out) { // Casting a float to float is a no-op. if (inputType.isFloat()) { return inputId; @@ -2781,7 +2708,7 @@ SpvId SPIRVCodeGenerator::castScalarToFloat(SpvId inputId, const Type& inputType } SpvId SPIRVCodeGenerator::castScalarToSignedInt(SpvId inputId, const Type& inputType, - const Type& outputType, SPIRVBlob& out) { + const Type& outputType, OutputStream& out) { // Casting a signed int to signed int is a no-op. if (inputType.isSigned()) { return inputId; @@ -2808,7 +2735,7 @@ SpvId SPIRVCodeGenerator::castScalarToSignedInt(SpvId inputId, const Type& input } SpvId SPIRVCodeGenerator::castScalarToUnsignedInt(SpvId inputId, const Type& inputType, - const Type& outputType, SPIRVBlob& out) { + const Type& outputType, OutputStream& out) { // Casting an unsigned int to unsigned int is a no-op. if (inputType.isUnsigned()) { return inputId; @@ -2835,7 +2762,7 @@ SpvId SPIRVCodeGenerator::castScalarToUnsignedInt(SpvId inputId, const Type& inp } SpvId SPIRVCodeGenerator::castScalarToBoolean(SpvId inputId, const Type& inputType, - const Type& outputType, SPIRVBlob& out) { + const Type& outputType, OutputStream& out) { // Casting a bool to bool is a no-op. if (inputType.isBoolean()) { return inputId; @@ -2866,7 +2793,7 @@ SpvId SPIRVCodeGenerator::castScalarToBoolean(SpvId inputId, const Type& inputTy } SpvId SPIRVCodeGenerator::writeMatrixCopy(SpvId src, const Type& srcType, const Type& dstType, - SPIRVBlob& out) { + OutputStream& out) { SkASSERT(srcType.isMatrix()); SkASSERT(dstType.isMatrix()); SkASSERT(srcType.componentType().matches(dstType.componentType())); @@ -2927,7 +2854,7 @@ void SPIRVCodeGenerator::addColumnEntry(const Type& columnType, TArray<SpvId>* columnIds, int rows, SpvId entry, - SPIRVBlob& out) { + OutputStream& out) { SkASSERT(currentColumn->size() < rows); currentColumn->push_back(entry); if (currentColumn->size() == rows) { @@ -2938,7 +2865,7 @@ void SPIRVCodeGenerator::addColumnEntry(const Type& columnType, } } -SpvId SPIRVCodeGenerator::writeMatrixConstructor(const ConstructorCompound& c, SPIRVBlob& out) { +SpvId SPIRVCodeGenerator::writeMatrixConstructor(const ConstructorCompound& c, OutputStream& out) { const Type& type = c.type(); SkASSERT(type.isMatrix()); SkASSERT(!c.arguments().empty()); @@ -2992,12 +2919,12 @@ SpvId SPIRVCodeGenerator::writeMatrixConstructor(const ConstructorCompound& c, S } SpvId SPIRVCodeGenerator::writeConstructorCompound(const ConstructorCompound& c, - SPIRVBlob& out) { + OutputStream& out) { return c.type().isMatrix() ? this->writeMatrixConstructor(c, out) : this->writeVectorConstructor(c, out); } -SpvId SPIRVCodeGenerator::writeVectorConstructor(const ConstructorCompound& c, SPIRVBlob& out) { +SpvId SPIRVCodeGenerator::writeVectorConstructor(const ConstructorCompound& c, OutputStream& out) { const Type& type = c.type(); const Type& componentType = type.componentType(); SkASSERT(type.isVector()); @@ -3032,13 +2959,13 @@ SpvId SPIRVCodeGenerator::writeVectorConstructor(const ConstructorCompound& c, S return this->writeOpCompositeConstruct(type, arguments, out); } -SpvId SPIRVCodeGenerator::writeConstructorSplat(const ConstructorSplat& c, SPIRVBlob& out) { +SpvId SPIRVCodeGenerator::writeConstructorSplat(const ConstructorSplat& c, OutputStream& out) { // Write the splat argument as a scalar, then splat it. SpvId argument = this->writeExpression(*c.argument(), out); return this->splat(c.type(), argument, out); } -SpvId SPIRVCodeGenerator::writeCompositeConstructor(const AnyConstructor& c, SPIRVBlob& out) { +SpvId SPIRVCodeGenerator::writeCompositeConstructor(const AnyConstructor& c, OutputStream& out) { SkASSERT(c.type().isArray() || c.type().isStruct()); auto ctorArgs = c.argumentSpan(); @@ -3051,7 +2978,7 @@ SpvId SPIRVCodeGenerator::writeCompositeConstructor(const AnyConstructor& c, SPI } SpvId SPIRVCodeGenerator::writeConstructorScalarCast(const ConstructorScalarCast& c, - SPIRVBlob& out) { + OutputStream& out) { const Type& type = c.type(); if (type.componentType().numberKind() == c.argument()->type().componentType().numberKind()) { return this->writeExpression(*c.argument(), out); @@ -3063,7 +2990,7 @@ SpvId SPIRVCodeGenerator::writeConstructorScalarCast(const ConstructorScalarCast } SpvId SPIRVCodeGenerator::writeConstructorCompoundCast(const ConstructorCompoundCast& c, - SPIRVBlob& out) { + OutputStream& out) { const Type& ctorType = c.type(); const Type& argType = c.argument()->type(); SkASSERT(ctorType.isVector() || ctorType.isMatrix()); @@ -3094,7 +3021,7 @@ SpvId SPIRVCodeGenerator::writeConstructorCompoundCast(const ConstructorCompound } SpvId SPIRVCodeGenerator::writeConstructorDiagonalMatrix(const ConstructorDiagonalMatrix& c, - SPIRVBlob& out) { + OutputStream& out) { const Type& type = c.type(); SkASSERT(type.isMatrix()); SkASSERT(c.argument()->type().isScalar()); @@ -3119,7 +3046,7 @@ SpvId SPIRVCodeGenerator::writeConstructorDiagonalMatrix(const ConstructorDiagon } SpvId SPIRVCodeGenerator::writeConstructorMatrixResize(const ConstructorMatrixResize& c, - SPIRVBlob& out) { + OutputStream& out) { // Write the input matrix. SpvId argument = this->writeExpression(*c.argument(), out); @@ -3186,7 +3113,7 @@ StorageClass SPIRVCodeGenerator::getStorageClass(const Expression& expr) { } } -TArray<SpvId> SPIRVCodeGenerator::getAccessChain(const Expression& expr, SPIRVBlob& out) { +TArray<SpvId> SPIRVCodeGenerator::getAccessChain(const Expression& expr, OutputStream& out) { switch (expr.kind()) { case Expression::Kind::kIndex: { const IndexExpression& indexExpr = expr.as<IndexExpression>(); @@ -3249,11 +3176,11 @@ public: return fStorageClass; } - SpvId load(SPIRVBlob& out) override { + SpvId load(OutputStream& out) override { return fGen.writeOpLoad(fType, fPrecision, fPointer, out); } - void store(SpvId value, SPIRVBlob& out) override { + void store(SpvId value, OutputStream& out) override { if (!fIsMemoryObject) { // We are going to write into an access chain; this could represent one component of a // vector, or one element of an array. This has the potential to invalidate other, @@ -3304,7 +3231,7 @@ public: return fStorageClass; } - SpvId load(SPIRVBlob& out) override { + SpvId load(OutputStream& out) override { SpvId base = fGen.nextId(fBaseType); fGen.writeInstruction(SpvOpLoad, fGen.getType(*fBaseType), base, fVecPointer, out); SpvId result = fGen.nextId(fBaseType); @@ -3319,7 +3246,7 @@ public: return result; } - void store(SpvId value, SPIRVBlob& out) override { + void store(SpvId value, OutputStream& out) override { // use OpVectorShuffle to mix and match the vector components. We effectively create // a virtual vector out of the concatenation of the left and right vectors, and then // select components from this virtual vector to make the result vector. For @@ -3372,7 +3299,7 @@ int SPIRVCodeGenerator::findUniformFieldIndex(const Variable& var) const { } std::unique_ptr<SPIRVCodeGenerator::LValue> SPIRVCodeGenerator::getLValue(const Expression& expr, - SPIRVBlob& out) { + OutputStream& out) { const Type& type = expr.type(); Precision precision = type.highPrecision() ? Precision::kDefault : Precision::kRelaxed; switch (expr.kind()) { @@ -3500,7 +3427,7 @@ std::unique_ptr<Expression> SPIRVCodeGenerator::identifier(std::string_view name : Poison::Make(Position(), fContext); } -SpvId SPIRVCodeGenerator::writeVariableReference(const VariableReference& ref, SPIRVBlob& out) { +SpvId SPIRVCodeGenerator::writeVariableReference(const VariableReference& ref, OutputStream& out) { const Variable* variable = ref.variable(); switch (variable->layout().fBuiltin) { case DEVICE_FRAGCOORDS_BUILTIN: { @@ -3662,7 +3589,7 @@ SpvId SPIRVCodeGenerator::writeVariableReference(const VariableReference& ref, S } } -SpvId SPIRVCodeGenerator::writeIndexExpression(const IndexExpression& expr, SPIRVBlob& out) { +SpvId SPIRVCodeGenerator::writeIndexExpression(const IndexExpression& expr, OutputStream& out) { if (expr.base()->type().isVector()) { SpvId base = this->writeExpression(*expr.base(), out); SpvId index = this->writeExpression(*expr.index(), out); @@ -3674,13 +3601,13 @@ SpvId SPIRVCodeGenerator::writeIndexExpression(const IndexExpression& expr, SPIR return getLValue(expr, out)->load(out); } -SpvId SPIRVCodeGenerator::writeFieldAccess(const FieldAccess& f, SPIRVBlob& out) { +SpvId SPIRVCodeGenerator::writeFieldAccess(const FieldAccess& f, OutputStream& out) { return getLValue(f, out)->load(out); } SpvId SPIRVCodeGenerator::writeSwizzle(const Expression& baseExpr, const ComponentArray& components, - SPIRVBlob& out) { + OutputStream& out) { size_t count = components.size(); const Type& type = baseExpr.type().componentType().toCompound(fContext, count, /*rows=*/1); SpvId base = this->writeExpression(baseExpr, out); @@ -3700,7 +3627,7 @@ SpvId SPIRVCodeGenerator::writeSwizzle(const Expression& baseExpr, return result; } -SpvId SPIRVCodeGenerator::writeSwizzle(const Swizzle& swizzle, SPIRVBlob& out) { +SpvId SPIRVCodeGenerator::writeSwizzle(const Swizzle& swizzle, OutputStream& out) { return this->writeSwizzle(*swizzle.base(), swizzle.components(), out); } @@ -3708,7 +3635,7 @@ SpvId SPIRVCodeGenerator::writeBinaryOperation(const Type& resultType, const Typ SpvId lhs, SpvId rhs, bool writeComponentwiseIfMatrix, SpvOp_ ifFloat, SpvOp_ ifInt, SpvOp_ ifUInt, - SpvOp_ ifBool, SPIRVBlob& out) { + SpvOp_ ifBool, OutputStream& out) { SpvOp_ op = pick_by_type(operandType, ifFloat, ifInt, ifUInt, ifBool); if (op == SpvOpUndef) { fContext.fErrors->error(operandType.fPosition, @@ -3728,7 +3655,7 @@ SpvId SPIRVCodeGenerator::writeBinaryOperationComponentwiseIfMatrix(const Type& SpvId lhs, SpvId rhs, SpvOp_ ifFloat, SpvOp_ ifInt, SpvOp_ ifUInt, SpvOp_ ifBool, - SPIRVBlob& out) { + OutputStream& out) { return this->writeBinaryOperation(resultType, operandType, lhs, rhs, /*writeComponentwiseIfMatrix=*/true, ifFloat, ifInt, ifUInt, ifBool, out); @@ -3736,14 +3663,14 @@ SpvId SPIRVCodeGenerator::writeBinaryOperationComponentwiseIfMatrix(const Type& SpvId SPIRVCodeGenerator::writeBinaryOperation(const Type& resultType, const Type& operandType, SpvId lhs, SpvId rhs, SpvOp_ ifFloat, SpvOp_ ifInt, - SpvOp_ ifUInt, SpvOp_ ifBool, SPIRVBlob& out) { + SpvOp_ ifUInt, SpvOp_ ifBool, OutputStream& out) { return this->writeBinaryOperation(resultType, operandType, lhs, rhs, /*writeComponentwiseIfMatrix=*/false, ifFloat, ifInt, ifUInt, ifBool, out); } SpvId SPIRVCodeGenerator::foldToBool(SpvId id, const Type& operandType, SpvOp op, - SPIRVBlob& out) { + OutputStream& out) { if (operandType.isVector()) { SpvId result = this->nextId(nullptr); this->writeInstruction(op, this->getType(*fContext.fTypes.fBool), result, id, out); @@ -3755,7 +3682,7 @@ SpvId SPIRVCodeGenerator::foldToBool(SpvId id, const Type& operandType, SpvOp op SpvId SPIRVCodeGenerator::writeMatrixComparison(const Type& operandType, SpvId lhs, SpvId rhs, SpvOp_ floatOperator, SpvOp_ intOperator, SpvOp_ vectorMergeOperator, SpvOp_ mergeOperator, - SPIRVBlob& out) { + OutputStream& out) { SpvOp_ compareOp = is_float(operandType) ? floatOperator : intOperator; SkASSERT(operandType.isMatrix()); const Type& columnType = operandType.componentType().toCompound(fContext, @@ -3787,7 +3714,7 @@ SpvId SPIRVCodeGenerator::writeMatrixComparison(const Type& operandType, SpvId l SpvId SPIRVCodeGenerator::writeComponentwiseMatrixUnary(const Type& operandType, SpvId operand, SpvOp_ op, - SPIRVBlob& out) { + OutputStream& out) { SkASSERT(operandType.isMatrix()); const Type& columnType = operandType.columnType(fContext); SpvId columnTypeId = this->getType(columnType); @@ -3804,7 +3731,7 @@ SpvId SPIRVCodeGenerator::writeComponentwiseMatrixUnary(const Type& operandType, } SpvId SPIRVCodeGenerator::writeComponentwiseMatrixBinary(const Type& operandType, SpvId lhs, - SpvId rhs, SpvOp_ op, SPIRVBlob& out) { + SpvId rhs, SpvOp_ op, OutputStream& out) { SkASSERT(operandType.isMatrix()); const Type& columnType = operandType.columnType(fContext); SpvId columnTypeId = this->getType(columnType); @@ -3819,7 +3746,7 @@ SpvId SPIRVCodeGenerator::writeComponentwiseMatrixBinary(const Type& operandType return this->writeOpCompositeConstruct(operandType, columns, out); } -SpvId SPIRVCodeGenerator::writeReciprocal(const Type& type, SpvId value, SPIRVBlob& out) { +SpvId SPIRVCodeGenerator::writeReciprocal(const Type& type, SpvId value, OutputStream& out) { SkASSERT(type.isFloat()); SpvId one = this->writeLiteral(1.0, type); SpvId reciprocal = this->nextId(&type); @@ -3827,7 +3754,7 @@ SpvId SPIRVCodeGenerator::writeReciprocal(const Type& type, SpvId value, SPIRVBl return reciprocal; } -SpvId SPIRVCodeGenerator::splat(const Type& type, SpvId id, SPIRVBlob& out) { +SpvId SPIRVCodeGenerator::splat(const Type& type, SpvId id, OutputStream& out) { if (type.isScalar()) { // Scalars require no additional work; we can return the passed-in ID as is. } else { @@ -3868,7 +3795,7 @@ SpvId SPIRVCodeGenerator::writeDecomposedMatrixVectorMultiply(const Type& leftTy const Type& rightType, SpvId rhs, const Type& resultType, - SPIRVBlob& out) { + OutputStream& out) { SpvId sum = NA; const Type& columnType = leftType.columnType(fContext); const Type& scalarType = rightType.componentType(); @@ -3900,7 +3827,7 @@ SpvId SPIRVCodeGenerator::writeDecomposedMatrixVectorMultiply(const Type& leftTy SpvId SPIRVCodeGenerator::writeBinaryExpression(const Type& leftType, SpvId lhs, Operator op, const Type& rightType, SpvId rhs, - const Type& resultType, SPIRVBlob& out) { + const Type& resultType, OutputStream& out) { // The comma operator ignores the type of the left-hand side entirely. if (op.kind() == Operator::Kind::COMMA) { return rhs; @@ -4144,7 +4071,7 @@ SpvId SPIRVCodeGenerator::writeBinaryExpression(const Type& leftType, SpvId lhs, } SpvId SPIRVCodeGenerator::writeArrayComparison(const Type& arrayType, SpvId lhs, Operator op, - SpvId rhs, SPIRVBlob& out) { + SpvId rhs, OutputStream& out) { // The inputs must be arrays, and the op must be == or !=. SkASSERT(op.kind() == Operator::Kind::EQEQ || op.kind() == Operator::Kind::NEQ); SkASSERT(arrayType.isArray()); @@ -4169,7 +4096,7 @@ SpvId SPIRVCodeGenerator::writeArrayComparison(const Type& arrayType, SpvId lhs, } SpvId SPIRVCodeGenerator::writeStructComparison(const Type& structType, SpvId lhs, Operator op, - SpvId rhs, SPIRVBlob& out) { + SpvId rhs, OutputStream& out) { // The inputs must be structs containing fields, and the op must be == or !=. SkASSERT(op.kind() == Operator::Kind::EQEQ || op.kind() == Operator::Kind::NEQ); SkASSERT(structType.isStruct()); @@ -4195,7 +4122,7 @@ SpvId SPIRVCodeGenerator::writeStructComparison(const Type& structType, SpvId lh } SpvId SPIRVCodeGenerator::mergeComparisons(SpvId comparison, SpvId allComparisons, Operator op, - SPIRVBlob& out) { + OutputStream& out) { // If this is the first entry, we don't need to merge comparison results with anything. if (allComparisons == NA) { return comparison; @@ -4220,7 +4147,7 @@ SpvId SPIRVCodeGenerator::mergeComparisons(SpvId comparison, SpvId allComparison return logicalOp; } -SpvId SPIRVCodeGenerator::writeBinaryExpression(const BinaryExpression& b, SPIRVBlob& out) { +SpvId SPIRVCodeGenerator::writeBinaryExpression(const BinaryExpression& b, OutputStream& out) { const Expression* left = b.left().get(); const Expression* right = b.right().get(); Operator op = b.getOperator(); @@ -4264,7 +4191,7 @@ SpvId SPIRVCodeGenerator::writeBinaryExpression(const BinaryExpression& b, SPIRV } SpvId SPIRVCodeGenerator::writeLogicalAnd(const Expression& left, const Expression& right, - SPIRVBlob& out) { + OutputStream& out) { SpvId falseConstant = this->writeLiteral(0.0, *fContext.fTypes.fBool); SpvId lhs = this->writeExpression(left, out); @@ -4288,7 +4215,7 @@ SpvId SPIRVCodeGenerator::writeLogicalAnd(const Expression& left, const Expressi } SpvId SPIRVCodeGenerator::writeLogicalOr(const Expression& left, const Expression& right, - SPIRVBlob& out) { + OutputStream& out) { SpvId trueConstant = this->writeLiteral(1.0, *fContext.fTypes.fBool); SpvId lhs = this->writeExpression(left, out); @@ -4311,7 +4238,7 @@ SpvId SPIRVCodeGenerator::writeLogicalOr(const Expression& left, const Expressio return result; } -SpvId SPIRVCodeGenerator::writeTernaryExpression(const TernaryExpression& t, SPIRVBlob& out) { +SpvId SPIRVCodeGenerator::writeTernaryExpression(const TernaryExpression& t, OutputStream& out) { const Type& type = t.type(); SpvId test = this->writeExpression(*t.test(), out); if (t.ifTrue()->type().columns() == 1 && @@ -4351,7 +4278,7 @@ SpvId SPIRVCodeGenerator::writeTernaryExpression(const TernaryExpression& t, SPI return result; } -SpvId SPIRVCodeGenerator::writePrefixExpression(const PrefixExpression& p, SPIRVBlob& out) { +SpvId SPIRVCodeGenerator::writePrefixExpression(const PrefixExpression& p, OutputStream& out) { const Type& type = p.type(); if (p.getOperator().kind() == Operator::Kind::MINUS) { SpvOp_ negateOp = pick_by_type(type, SpvOpFNegate, SpvOpSNegate, SpvOpSNegate, SpvOpUndef); @@ -4413,7 +4340,7 @@ SpvId SPIRVCodeGenerator::writePrefixExpression(const PrefixExpression& p, SPIRV } } -SpvId SPIRVCodeGenerator::writePostfixExpression(const PostfixExpression& p, SPIRVBlob& out) { +SpvId SPIRVCodeGenerator::writePostfixExpression(const PostfixExpression& p, OutputStream& out) { const Type& type = p.type(); std::unique_ptr<LValue> lv = this->getLValue(*p.operand(), out); SpvId result = lv->load(out); @@ -4465,7 +4392,7 @@ SpvId SPIRVCodeGenerator::writeLiteral(double value, const Type& type) { } } -void SPIRVCodeGenerator::writeFunctionStart(const FunctionDeclaration& f, SPIRVBlob& out) { +void SPIRVCodeGenerator::writeFunctionStart(const FunctionDeclaration& f, OutputStream& out) { SpvId result = fFunctionMap[{&f, fActiveSpecializationIndex}]; SpvId returnTypeId = this->getType(f.returnType()); SpvId functionTypeId = this->getFunctionType(f); @@ -4502,7 +4429,7 @@ void SPIRVCodeGenerator::writeFunctionStart(const FunctionDeclaration& f, SPIRVB if (fUseTextureSamplerPairs && parameter->type().isSampler()) { auto [texture, sampler] = this->synthesizeTextureAndSampler(*parameter); - SpvId textureId = this->nextId(&parameter->type()); + SpvId textureId = this->nextId(nullptr); fVariableMap.set(texture, textureId); SpvId textureType = this->getFunctionParameterType(texture->type(), texture->layout()); @@ -4515,7 +4442,7 @@ void SPIRVCodeGenerator::writeFunctionStart(const FunctionDeclaration& f, SPIRVB SkASSERT(uniformId); fVariableMap.set(sampler, *uniformId); } else { - SpvId samplerId = this->nextId(&parameter->type()); + SpvId samplerId = this->nextId(nullptr); fVariableMap.set(sampler, samplerId); SpvId samplerType = @@ -4528,7 +4455,7 @@ void SPIRVCodeGenerator::writeFunctionStart(const FunctionDeclaration& f, SPIRVB SkASSERT(uniformId); fVariableMap.set(parameter, *uniformId); } else { - SpvId id = this->nextId(&parameter->type()); + SpvId id = this->nextId(nullptr); fVariableMap.set(parameter, id); SpvId type = this->getFunctionParameterType(parameter->type(), parameter->layout()); @@ -4538,7 +4465,7 @@ void SPIRVCodeGenerator::writeFunctionStart(const FunctionDeclaration& f, SPIRVB } } -void SPIRVCodeGenerator::writeFunction(const FunctionDefinition& f, SPIRVBlob& out) { +void SPIRVCodeGenerator::writeFunction(const FunctionDefinition& f, OutputStream& out) { if (const Analysis::Specializations* specializations = fSpecializationInfo.fSpecializationMap.find(&f.declaration())) { for (int i = 0; i < specializations->size(); i++) { @@ -4556,24 +4483,24 @@ void SPIRVCodeGenerator::writeFunctionInstantiation( const FunctionDefinition& f, Analysis::SpecializationIndex specializationIndex, const Analysis::SpecializedParameters* specializedParams, - SPIRVBlob& out) { + OutputStream& out) { ConditionalOpCounts conditionalOps = this->getConditionalOpCounts(); - fVariableBuffer.clear(); + fVariableBuffer.reset(); fActiveSpecialization = specializedParams; fActiveSpecializationIndex = specializationIndex; this->writeFunctionStart(f.declaration(), out); fCurrentBlock = 0; this->writeLabel(this->nextId(nullptr), kBranchlessBlock, out); - SPIRVBlob bodyBuffer; + StringStream bodyBuffer; this->writeBlock(f.body()->as<Block>(), bodyBuffer); fActiveSpecialization = nullptr; fActiveSpecializationIndex = Analysis::kUnspecialized; - append_blob(fVariableBuffer, out); + write_stringstream(fVariableBuffer, out); if (f.declaration().isMain()) { - append_blob(fGlobalInitializersBuffer, out); + write_stringstream(fGlobalInitializersBuffer, out); } - append_blob(bodyBuffer, out); + write_stringstream(bodyBuffer, out); if (fCurrentBlock) { if (f.declaration().returnType().isVoid()) { this->writeInstruction(SpvOpReturn, out); @@ -4839,28 +4766,18 @@ SpvId SPIRVCodeGenerator::writeGlobalVar(ProgramKind kind, break; } - // Input attachments have a reserved ID. - const bool isInputAttachment = - type->typeKind() == Type::TypeKind::kTexture && type->dimensions() == SpvDimSubpassData; - // Add this global to the variable map. - SpvId id = isInputAttachment ? (SpvId)spirv::kIdVariableImageSubpassData : this->nextId(type); + SpvId id = this->nextId(type); fVariableMap.set(&var, id); if (layout.fSet < 0 && storageClass == StorageClass::kUniformConstant) { layout.fSet = fProgram.fConfig->fSettings.fDefaultUniformSet; } - // Input int pointers have a reserved ID - SpvId typeId; - if (type->typeKind() == Type::TypeKind::kScalar && type->isSigned() && - storageClass == StorageClass::kInput) { - // Predefined type pointer - typeId = spirv::kIdTypePointerInputInt; - } else { - typeId = this->getPointerType( - *type, layout, this->memoryLayoutForStorageClass(storageClass), storageClass); - } + SpvId typeId = this->getPointerType(*type, + layout, + this->memoryLayoutForStorageClass(storageClass), + storageClass); this->writeInstruction(SpvOpVariable, typeId, id, get_storage_class_spv_id(storageClass), fConstantBuffer); this->writeInstruction(SpvOpName, id, var.name(), fNameBuffer); @@ -4881,7 +4798,7 @@ SpvId SPIRVCodeGenerator::writeGlobalVar(ProgramKind kind, return id; } -void SPIRVCodeGenerator::writeVarDeclaration(const VarDeclaration& varDecl, SPIRVBlob& out) { +void SPIRVCodeGenerator::writeVarDeclaration(const VarDeclaration& varDecl, OutputStream& out) { // If this variable is a compile-time constant then we'll emit OpConstant or // OpConstantComposite later when the variable is referenced. Avoid declaring an OpVariable now. if (is_vardecl_compile_time_constant(varDecl)) { @@ -4900,7 +4817,7 @@ void SPIRVCodeGenerator::writeVarDeclaration(const VarDeclaration& varDecl, SPIR } } -void SPIRVCodeGenerator::writeStatement(const Statement& s, SPIRVBlob& out) { +void SPIRVCodeGenerator::writeStatement(const Statement& s, OutputStream& out) { switch (s.kind()) { case Statement::Kind::kNop: break; @@ -4943,7 +4860,7 @@ void SPIRVCodeGenerator::writeStatement(const Statement& s, SPIRVBlob& out) { } } -void SPIRVCodeGenerator::writeBlock(const Block& b, SPIRVBlob& out) { +void SPIRVCodeGenerator::writeBlock(const Block& b, OutputStream& out) { for (const std::unique_ptr<Statement>& stmt : b.children()) { this->writeStatement(*stmt, out); } @@ -4978,7 +4895,7 @@ void SPIRVCodeGenerator::pruneConditionalOps(ConditionalOpCounts ops) { } } -void SPIRVCodeGenerator::writeIfStatement(const IfStatement& stmt, SPIRVBlob& out) { +void SPIRVCodeGenerator::writeIfStatement(const IfStatement& stmt, OutputStream& out) { SpvId test = this->writeExpression(*stmt.test(), out); SpvId ifTrue = this->nextId(nullptr); SpvId ifFalse = this->nextId(nullptr); @@ -5012,7 +4929,7 @@ void SPIRVCodeGenerator::writeIfStatement(const IfStatement& stmt, SPIRVBlob& ou } } -void SPIRVCodeGenerator::writeForStatement(const ForStatement& f, SPIRVBlob& out) { +void SPIRVCodeGenerator::writeForStatement(const ForStatement& f, OutputStream& out) { if (f.initializer()) { this->writeStatement(*f.initializer(), out); } @@ -5056,7 +4973,7 @@ void SPIRVCodeGenerator::writeForStatement(const ForStatement& f, SPIRVBlob& out fContinueTarget.pop_back(); } -void SPIRVCodeGenerator::writeDoStatement(const DoStatement& d, SPIRVBlob& out) { +void SPIRVCodeGenerator::writeDoStatement(const DoStatement& d, OutputStream& out) { ConditionalOpCounts conditionalOps = this->getConditionalOpCounts(); // The store cache isn't trustworthy in the presence of branches; store caching only makes sense @@ -5089,7 +5006,7 @@ void SPIRVCodeGenerator::writeDoStatement(const DoStatement& d, SPIRVBlob& out) fContinueTarget.pop_back(); } -void SPIRVCodeGenerator::writeSwitchStatement(const SwitchStatement& s, SPIRVBlob& out) { +void SPIRVCodeGenerator::writeSwitchStatement(const SwitchStatement& s, OutputStream& out) { SpvId value = this->writeExpression(*s.value(), out); ConditionalOpCounts conditionalOps = this->getConditionalOpCounts(); @@ -5162,7 +5079,7 @@ void SPIRVCodeGenerator::writeSwitchStatement(const SwitchStatement& s, SPIRVBlo fBreakTarget.pop_back(); } -void SPIRVCodeGenerator::writeReturnStatement(const ReturnStatement& r, SPIRVBlob& out) { +void SPIRVCodeGenerator::writeReturnStatement(const ReturnStatement& r, OutputStream& out) { if (r.expression()) { this->writeInstruction(SpvOpReturnValue, this->writeExpression(*r.expression(), out), out); @@ -5407,29 +5324,13 @@ std::tuple<const Variable*, const Variable*> SPIRVCodeGenerator::synthesizeTextu return {t, s}; } -void SPIRVCodeGenerator::writeInstructions(const Program& program, SPIRVBlob& out) { +void SPIRVCodeGenerator::writeInstructions(const Program& program, OutputStream& out) { Analysis::FindFunctionsToSpecialize(program, &fSpecializationInfo, [](const Variable& param) { return param.type().isSampler() || param.type().isUnsizedArray(); }); - // Pre-declare some types that the SPIR-V transformer expects to always be present. - // int: - this->writeInstruction( - SpvOpTypeInt, - Words{Word::ReservedResult(spirv::kIdTypeInt), Word::Number(32), Word::Number(1)}, - fConstantBuffer); - // int pointer: - this->writeInstruction(SpvOpTypePointer, - Words{Word::ReservedResult(spirv::kIdTypePointerInputInt), - Word::Number(SpvStorageClassInput), - (SpvId)spirv::kIdTypeInt}, - fConstantBuffer); - fGLSLExtendedInstructions = this->nextId(nullptr); - SPIRVBlob body; - // Based on statistics from dm tests: - // Total word count of the shader's function bodies is ~1300 on average, ~10000 max. - body.reserve(1024); + StringStream body; // Do an initial pass over the program elements to establish some baseline info. const FunctionDeclaration* main = nullptr; @@ -5445,11 +5346,10 @@ void SPIRVCodeGenerator::writeInstructions(const Program& program, SPIRVBlob& ou if (const Analysis::Specializations* specializations = fSpecializationInfo.fSpecializationMap.find(&funcDecl)) { for (int i = 0; i < specializations->size(); i++) { - fFunctionMap.set({&funcDecl, i}, this->nextId(&funcDecl.returnType())); + fFunctionMap.set({&funcDecl, i}, this->nextId(nullptr)); } } else { - fFunctionMap.set({&funcDecl, Analysis::kUnspecialized}, - this->nextId(&funcDecl.returnType())); + fFunctionMap.set({&funcDecl, Analysis::kUnspecialized}, this->nextId(nullptr)); } if (funcDecl.isMain()) { main = &funcDecl; @@ -5621,24 +5521,22 @@ void SPIRVCodeGenerator::writeInstructions(const Program& program, SPIRVBlob& ou } } - append_blob(fNameBuffer, out); - append_blob(fDecorationBuffer, out); - append_blob(fConstantBuffer, out); - append_blob(body, out); + write_stringstream(fNameBuffer, out); + write_stringstream(fDecorationBuffer, out); + write_stringstream(fConstantBuffer, out); + write_stringstream(body, out); } bool SPIRVCodeGenerator::generateCode() { SkASSERT(!fContext.fErrors->errorCount()); - this->writeWord(SpvMagicNumber, *fOutBuffer); - this->writeWord(SpvVersion, *fOutBuffer); - this->writeWord(SKSL_MAGIC, *fOutBuffer); - // Placeholder for id count, to be determined after writeInstruction. - this->writeWord(0, *fOutBuffer); - // reserved, always zero - this->writeWord(0, *fOutBuffer); - this->writeInstructions(fProgram, *fOutBuffer); - // Set the id count now that it's determined. - (*fOutBuffer)[3] = fIdCount; + this->writeWord(SpvMagicNumber, *fOut); + this->writeWord(SpvVersion, *fOut); + this->writeWord(SKSL_MAGIC, *fOut); + StringStream buffer; + this->writeInstructions(fProgram, buffer); + this->writeWord(fIdCount, *fOut); + this->writeWord(0, *fOut); // reserved, always zero + write_stringstream(buffer, *fOut); return fContext.fErrors->errorCount() == 0; } @@ -5646,36 +5544,40 @@ bool ToSPIRV(Program& program, const ShaderCaps* caps, OutputStream& out, ValidateSPIRVProc validateSPIRV) { - std::vector<uint32_t> buffer; - if (!ToSPIRV(program, caps, &buffer, validateSPIRV)) { - return false; - } - out.write(buffer.data(), buffer.size() * sizeof(uint32_t)); - return true; -} - -bool ToSPIRV(Program& program, - const ShaderCaps* caps, - std::vector<uint32_t>* out, - ValidateSPIRVProc validateSPIRV) { TRACE_EVENT0("skia.shaders", "SkSL::ToSPIRV"); SkASSERT(caps != nullptr); - out->clear(); program.fContext->fErrors->setSource(*program.fSource); - - SPIRVCodeGenerator cg(program.fContext.get(), caps, &program, out); - bool result = cg.generateCode(); - + bool result; if (validateSPIRV) { + StringStream buffer; + SPIRVCodeGenerator cg(program.fContext.get(), caps, &program, &buffer); + result = cg.generateCode(); + if (result && program.fConfig->fSettings.fValidateSPIRV) { - result = validateSPIRV(*program.fContext->fErrors, *out); + std::string_view binary = buffer.str(); + result = validateSPIRV(*program.fContext->fErrors, binary); + out.write(binary.data(), binary.size()); } + } else { + SPIRVCodeGenerator cg(program.fContext.get(), caps, &program, &out); + result = cg.generateCode(); } - program.fContext->fErrors->setSource(std::string_view()); return result; } +bool ToSPIRV(Program& program, + const ShaderCaps* caps, + std::string* out, + ValidateSPIRVProc validateSPIRV) { + StringStream buffer; + if (!ToSPIRV(program, caps, buffer, validateSPIRV)) { + return false; + } + *out = buffer.str(); + return true; +} + } // namespace SkSL diff --git a/gfx/skia/skia/src/sksl/codegen/SkSLSPIRVCodeGenerator.h b/gfx/skia/skia/src/sksl/codegen/SkSLSPIRVCodeGenerator.h @@ -8,11 +8,8 @@ #ifndef SKSL_SPIRVCODEGENERATOR #define SKSL_SPIRVCODEGENERATOR -#include "include/core/SkSpan.h" -#include "src/sksl/codegen/SkSLNativeShader.h" - -#include <cstdint> -#include <vector> +#include <string> +#include <string_view> namespace SkSL { @@ -21,21 +18,17 @@ class OutputStream; struct Program; struct ShaderCaps; -using ValidateSPIRVProc = bool (*)(ErrorReporter&, SkSpan<const uint32_t>); +using ValidateSPIRVProc = bool (*)(ErrorReporter&, std::string_view); /** - * Converts a Program into a SPIR-V binary. Prefer the std::vector<uint32_t> variant bacause the - * OutputStream variant incurs an additional copy. + * Converts a Program into a SPIR-V binary. */ bool ToSPIRV(Program& program, const ShaderCaps* caps, OutputStream& out, ValidateSPIRVProc = nullptr); -bool ToSPIRV(Program& program, - const ShaderCaps* caps, - std::vector<uint32_t>* out, - ValidateSPIRVProc = nullptr); +bool ToSPIRV(Program& program, const ShaderCaps* caps, std::string* out, ValidateSPIRVProc); // This explicit overload is used by SkSLToBackend. -inline bool ToSPIRV(Program& program, const ShaderCaps* caps, NativeShader* out) { - return ToSPIRV(program, caps, &out->fBinary, nullptr); +inline bool ToSPIRV(Program& program, const ShaderCaps* caps, std::string* out) { + return ToSPIRV(program, caps, out, nullptr); } } // namespace SkSL diff --git a/gfx/skia/skia/src/sksl/codegen/SkSLSPIRVValidator.cpp b/gfx/skia/skia/src/sksl/codegen/SkSLSPIRVValidator.cpp @@ -14,9 +14,11 @@ namespace SkSL { -static bool validate_spirv(ErrorReporter& reporter, - SkSpan<const uint32_t> program, - bool disassemble) { +static bool validate_spirv(ErrorReporter& reporter, std::string_view program, bool disassemble) { + SkASSERT(0 == program.size() % 4); + const uint32_t* programData = reinterpret_cast<const uint32_t*>(program.data()); + size_t programSize = program.size() / 4; + spvtools::SpirvTools tools(SPV_ENV_VULKAN_1_0); std::string errors; auto msgFn = [&errors](spv_message_level_t, const char*, const spv_position_t&, const char* m) { @@ -26,7 +28,7 @@ static bool validate_spirv(ErrorReporter& reporter, }; tools.SetMessageConsumer(msgFn); - bool result = tools.Validate(program.data(), program.size()); + bool result = tools.Validate(programData, programSize); if (result) { return true; } @@ -38,9 +40,8 @@ static bool validate_spirv(ErrorReporter& reporter, // as if were an SkSL compile error message. std::string disassembly; uint32_t options = spvtools::SpirvTools::kDefaultDisassembleOption; - options |= SPV_BINARY_TO_TEXT_OPTION_COMMENT | SPV_BINARY_TO_TEXT_OPTION_INDENT | - SPV_BINARY_TO_TEXT_OPTION_NESTED_INDENT; - if (tools.Disassemble(program.data(), program.size(), &disassembly, options)) { + options |= SPV_BINARY_TO_TEXT_OPTION_INDENT; + if (tools.Disassemble(programData, programSize, &disassembly, options)) { errors.append(disassembly); } reporter.error(Position(), errors); @@ -51,11 +52,11 @@ static bool validate_spirv(ErrorReporter& reporter, return false; } -bool ValidateSPIRV(ErrorReporter& reporter, SkSpan<const uint32_t> program) { +bool ValidateSPIRV(ErrorReporter& reporter, std::string_view program) { return validate_spirv(reporter, program, false); } -bool ValidateSPIRVAndDissassemble(ErrorReporter& reporter, SkSpan<const uint32_t> program) { +bool ValidateSPIRVAndDissassemble(ErrorReporter& reporter, std::string_view program) { return validate_spirv(reporter, program, true); } diff --git a/gfx/skia/skia/src/sksl/codegen/SkSLSPIRVValidator.h b/gfx/skia/skia/src/sksl/codegen/SkSLSPIRVValidator.h @@ -8,9 +8,7 @@ #ifndef SKSL_SPIRVVALIDATOR #define SKSL_SPIRVVALIDATOR -#include "include/core/SkSpan.h" - -#include <cstdint> +#include <string_view> namespace SkSL { @@ -18,11 +16,11 @@ class ErrorReporter; // SPIRV issues will cause an SkDEBUGFAILF to be triggered with the error message // and false will be returned (i.e. invalid SPIRV is a fatal issue in debug builds). -bool ValidateSPIRV(ErrorReporter&, SkSpan<const uint32_t>); +bool ValidateSPIRV(ErrorReporter&, std::string_view); // SPIRV issues will be sent to the provided ErrorReporter along with a disassembly // of the code. This will also return false, but not be fatal in debug builds. -bool ValidateSPIRVAndDissassemble(ErrorReporter&, SkSpan<const uint32_t>); +bool ValidateSPIRVAndDissassemble(ErrorReporter&, std::string_view); } // namespace SkSL diff --git a/gfx/skia/skia/src/sksl/codegen/SkSLSPIRVtoHLSL.cpp b/gfx/skia/skia/src/sksl/codegen/SkSLSPIRVtoHLSL.cpp @@ -17,8 +17,9 @@ namespace SkSL { -void SPIRVtoHLSL(SkSpan<const uint32_t> spirv, std::string* hlsl) { - spirv_cross::CompilerHLSL hlslCompiler(spirv.data(), spirv.size()); +void SPIRVtoHLSL(const std::string& spirv, std::string* hlsl) { + spirv_cross::CompilerHLSL hlslCompiler((const uint32_t*)spirv.c_str(), + spirv.size() / sizeof(uint32_t)); spirv_cross::CompilerGLSL::Options optionsGLSL; // Force all uninitialized variables to be 0, otherwise they will fail to compile diff --git a/gfx/skia/skia/src/sksl/codegen/SkSLSPIRVtoHLSL.h b/gfx/skia/skia/src/sksl/codegen/SkSLSPIRVtoHLSL.h @@ -8,14 +8,11 @@ #ifndef SKSL_SPIRVTOHLSL #define SKSL_SPIRVTOHLSL -#include "include/core/SkSpan.h" - -#include <cstdint> #include <string> namespace SkSL { -void SPIRVtoHLSL(SkSpan<const uint32_t> spirv, std::string* hlsl); +void SPIRVtoHLSL(const std::string& spirv, std::string* hlsl); } // namespace SkSL diff --git a/gfx/skia/skia/src/sksl/codegen/SkSLWGSLCodeGenerator.cpp b/gfx/skia/skia/src/sksl/codegen/SkSLWGSLCodeGenerator.cpp @@ -35,7 +35,6 @@ #include "src/sksl/analysis/SkSLProgramVisitor.h" #include "src/sksl/codegen/SkSLCodeGenTypes.h" #include "src/sksl/codegen/SkSLCodeGenerator.h" -#include "src/sksl/codegen/SkSLNativeShader.h" #include "src/sksl/ir/SkSLBinaryExpression.h" #include "src/sksl/ir/SkSLBlock.h" #include "src/sksl/ir/SkSLConstructor.h" @@ -159,7 +158,6 @@ public: // These flags track extensions that will need to be enabled. bool fPixelLocalExtension = false; - bool fPushConstantExtension = false; }; WGSLCodeGenerator(const Context* context, @@ -938,7 +936,7 @@ std::optional<WGSLCodeGenerator::Builtin> builtin_from_sksl_name(int builtin) { case SK_LASTFRAGCOLOR_BUILTIN: return Builtin::kLastFragColor; case SK_CLOCKWISE_BUILTIN: - // TODO(skbug.com/40044196): While `front_facing` is the corresponding built-in, it does not + // TODO(skia:13092): While `front_facing` is the corresponding built-in, it does not // imply a particular winding order. We correctly compute the face orientation based // on how Skia configured the render pipeline for all references to this built-in // variable (see `SkSL::Program::Interface::fRTFlipUniform`). @@ -1075,14 +1073,6 @@ WGSLCodeGenerator::ProgramRequirements resolve_program_requirements(const Progra } break; } - case ProgramElement::Kind::kInterfaceBlock: { - const InterfaceBlock& ib = e->as<InterfaceBlock>(); - const Layout& layout = ib.var()->layout(); - if (layout.fFlags & LayoutFlag::kPushConstant) { - requirements.fPushConstantExtension = true; - } - break; - } default: break; } @@ -2764,7 +2754,7 @@ std::string WGSLCodeGenerator::assembleBinaryExpression(const Expression& left, // compile such expressions, as WGSL 1.0 has no infinity/nan support. However, the WGSL // compile-time check can be dodged by putting one side into a let-variable. This technically // gives us an indeterminate result, but the vast majority of backends will just calculate an - // infinity or nan here, as we would expect. (skbug.com/40045457) + // infinity or nan here, as we would expect. (skia:14385) bool bothSidesConstant = ConstantFolder::GetConstantValueOrNull(left) && ConstantFolder::GetConstantValueOrNull(right); @@ -2850,7 +2840,7 @@ static bool all_arguments_constant(const ExpressionArray& arguments) { // generally indicates that constant-folding resulted in an infinity or nan. The WGSL compiler // will reject such an expression with a compile-time error. We can dodge the error, taking on // the risk of indeterminate behavior instead, by replacing one of the constant values with a - // scratch let-variable. (skbug.com/40045457) + // scratch let-variable. (skia:14385) for (const std::unique_ptr<Expression>& arg : arguments) { if (!ConstantFolder::GetConstantValueOrNull(*arg)) { return false; @@ -2876,7 +2866,7 @@ std::string WGSLCodeGenerator::assembleSimpleIntrinsic(std::string_view intrinsi expr += '&'; expr += argument; } else if (allConstant && index == 0) { - // We can use a scratch-let for argument 0 to dodge WGSL overflow errors. (skbug.com/40045457) + // We can use a scratch-let for argument 0 to dodge WGSL overflow errors. (skia:14385) expr += this->writeScratchLet(argument); } else { expr += argument; @@ -2914,7 +2904,7 @@ std::string WGSLCodeGenerator::assembleVectorizedIntrinsic(std::string_view intr expr.push_back('('); } - // We can use a scratch-let for argument 0 to dodge WGSL overflow errors. (skbug.com/40045457) + // We can use a scratch-let for argument 0 to dodge WGSL overflow errors. (skia:14385) std::string argument = this->assembleExpression(*args[index], Precedence::kSequence); expr += (allConstant && index == 0) ? this->writeScratchLet(argument) : argument; @@ -2962,7 +2952,7 @@ std::string WGSLCodeGenerator::assembleBinaryOpIntrinsic(Operator op, expr.push_back('('); } - // We can use a scratch-let for argument 0 to dodge WGSL overflow errors. (skbug.com/40045457) + // We can use a scratch-let for argument 0 to dodge WGSL overflow errors. (skia:14385) std::string argument = this->assembleExpression(*call.arguments()[0], precedence); expr += all_arguments_constant(call.arguments()) ? this->writeScratchLet(argument) : argument; @@ -2991,7 +2981,7 @@ std::string WGSLCodeGenerator::assembleOutAssignedIntrinsic(std::string_view int expr += "("; // Invoke the intrinsic with the first parameter. Use a scratch-let if argument is a constant - // to dodge WGSL overflow errors. (skbug.com/40045457) + // to dodge WGSL overflow errors. (skia:14385) std::string argument = this->assembleExpression(*call.arguments()[0], Precedence::kSequence); expr += ConstantFolder::GetConstantValueOrNull(*call.arguments()[0]) ? this->writeScratchLet(argument) : argument; @@ -3147,7 +3137,7 @@ std::string WGSLCodeGenerator::assembleIntrinsicCall(const FunctionCall& call, return this->assembleBinaryOpIntrinsic(OperatorKind::LTEQ, call, parentPrecedence); case k_matrixCompMult_IntrinsicKind: { - // We use a scratch-let for arg0 to avoid the potential for WGSL overflow. (skbug.com/40045457) + // We use a scratch-let for arg0 to avoid the potential for WGSL overflow. (skia:14385) std::string arg0 = all_arguments_constant(arguments) ? this->writeScratchLet(*arguments[0], Precedence::kPostfix) : this->writeNontrivialScratchLet(*arguments[0], Precedence::kPostfix); @@ -3166,7 +3156,7 @@ std::string WGSLCodeGenerator::assembleIntrinsicCall(const FunctionCall& call, case k_mod_IntrinsicKind: { // WGSL has no intrinsic equivalent to `mod`. Synthesize `x - y * floor(x / y)`. // We can use a scratch-let on one side to dodge WGSL overflow errors. In practice, I - // can't find any values of x or y which would overflow, but it can't hurt. (skbug.com/40045457) + // can't find any values of x or y which would overflow, but it can't hurt. (skia:14385) std::string arg0 = all_arguments_constant(arguments) ? this->writeScratchLet(*arguments[0], Precedence::kAdditive) : this->writeNontrivialScratchLet(*arguments[0], Precedence::kAdditive); @@ -3209,7 +3199,7 @@ std::string WGSLCodeGenerator::assembleIntrinsicCall(const FunctionCall& call, case k_reflect_IntrinsicKind: if (arguments[0]->type().isScalar()) { // I - 2 * N * I * N - // We can use a scratch-let for N to dodge WGSL overflow errors. (skbug.com/40045457) + // We can use a scratch-let for N to dodge WGSL overflow errors. (skia:14385) std::string I = this->writeNontrivialScratchLet(*arguments[0], Precedence::kAdditive); std::string N = all_arguments_constant(arguments) @@ -3229,7 +3219,7 @@ std::string WGSLCodeGenerator::assembleIntrinsicCall(const FunctionCall& call, Precedence::kSequence); std::string N = this->writeNontrivialScratchLet(*arguments[1], Precedence::kSequence); - // We can use a scratch-let for Eta to avoid WGSL overflow errors. (skbug.com/40045457) + // We can use a scratch-let for Eta to avoid WGSL overflow errors. (skia:14385) std::string Eta = all_arguments_constant(arguments) ? this->writeScratchLet(*arguments[2], Precedence::kSequence) : this->writeNontrivialScratchLet(*arguments[2], Precedence::kSequence); @@ -4095,7 +4085,7 @@ std::string WGSLCodeGenerator::assembleEqualityExpression(const Expression& left void WGSLCodeGenerator::writeProgramElement(const ProgramElement& e) { switch (e.kind()) { case ProgramElement::Kind::kExtension: - // TODO(skbug.com/40044196): WGSL supports extensions via the "enable" directive + // TODO(skia:13092): WGSL supports extensions via the "enable" directive // (https://www.w3.org/TR/WGSL/#enable-extensions-sec ). While we could easily emit this // directive, we should first ensure that all possible SkSL extension names are // converted to their appropriate WGSL extension. @@ -4239,7 +4229,7 @@ void WGSLCodeGenerator::writeModifiersDeclaration(const ModifiersDeclaration& mo void WGSLCodeGenerator::writeFields(SkSpan<const Field> fields, const MemoryLayout* memoryLayout) { fIndentation++; - // TODO(skbug.com/40045444): array uniforms may need manual fixup for std140 padding. (Those uniforms + // TODO(skia:14370): array uniforms may need manual fixup for std140 padding. (Those uniforms // will also need special handling when they are accessed, or passed to functions.) for (size_t index = 0; index < fields.size(); ++index) { const Field& field = fields[index]; @@ -4300,9 +4290,6 @@ void WGSLCodeGenerator::writeEnables() { if (fRequirements.fPixelLocalExtension) { this->writeLine("enable chromium_experimental_pixel_local;"); } - if (fRequirements.fPushConstantExtension) { - this->writeLine("enable chromium_experimental_immediate;"); - } if (fProgram.fInterface.fUseLastFragColor) { this->writeLine("enable chromium_experimental_framebuffer_fetch;"); } @@ -4468,14 +4455,8 @@ void WGSLCodeGenerator::writeUniformsAndBuffers() { std::string_view addressSpace; std::string_view accessMode; MemoryLayout::Standard nativeLayout; - const bool isPushConstant = - static_cast<bool>(ib.var()->layout().fFlags & LayoutFlag::kPushConstant); if (ib.var()->modifierFlags().isUniform()) { - if (isPushConstant) { - addressSpace = "immediate"; - } else { - addressSpace = "uniform"; - } + addressSpace = "uniform"; nativeLayout = MemoryLayout::Standard::kWGSLUniform_Base; } else if (ib.var()->modifierFlags().isBuffer()) { addressSpace = "storage"; @@ -4512,14 +4493,11 @@ void WGSLCodeGenerator::writeUniformsAndBuffers() { MemoryLayout layout(MemoryLayout::Standard::k140); this->writeFields(ibFields, &layout); this->writeLine("};"); - if (!isPushConstant) { - this->write("@group("); - this->write(std::to_string(std::max(0, ib.var()->layout().fSet))); - this->write(") @binding("); - this->write(std::to_string(std::max(0, ib.var()->layout().fBinding))); - this->write(") "); - } - this->write("var<"); + this->write("@group("); + this->write(std::to_string(std::max(0, ib.var()->layout().fSet))); + this->write(") @binding("); + this->write(std::to_string(std::max(0, ib.var()->layout().fBinding))); + this->write(") var<"); this->write(addressSpace); this->write(accessMode); this->write("> "); @@ -4643,12 +4621,12 @@ bool ToWGSL(Program& program, const ShaderCaps* caps, OutputStream& out) { return ToWGSL(program, caps, out, defaultPrintOpts, IncludeSyntheticCode::kNo, nullptr); } -bool ToWGSL(Program& program, const ShaderCaps* caps, NativeShader* out) { +bool ToWGSL(Program& program, const ShaderCaps* caps, std::string* out) { StringStream buffer; if (!ToWGSL(program, caps, buffer)) { return false; } - out->fText = buffer.str(); + *out = buffer.str(); return true; } diff --git a/gfx/skia/skia/src/sksl/codegen/SkSLWGSLCodeGenerator.h b/gfx/skia/skia/src/sksl/codegen/SkSLWGSLCodeGenerator.h @@ -13,7 +13,6 @@ namespace SkSL { class ErrorReporter; -struct NativeShader; class OutputStream; enum class PrettyPrint : bool; struct Program; @@ -36,7 +35,7 @@ bool ToWGSL(Program& program, IncludeSyntheticCode, ValidateWGSLProc); bool ToWGSL(Program& program, const ShaderCaps* caps, OutputStream& out); -bool ToWGSL(Program& program, const ShaderCaps* caps, NativeShader* out); +bool ToWGSL(Program& program, const ShaderCaps* caps, std::string* out); } // namespace SkSL diff --git a/gfx/skia/skia/src/sksl/codegen/SkSLWGSLValidator.cpp b/gfx/skia/skia/src/sksl/codegen/SkSLWGSLValidator.cpp @@ -10,7 +10,7 @@ #include "src/sksl/SkSLErrorReporter.h" #include "src/sksl/SkSLPosition.h" -#include "src/tint/lang/wgsl/enums.h" +#include "src/tint/lang/wgsl/extension.h" #include "src/tint/lang/wgsl/reader/options.h" #include "tint/tint.h" 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 @@ -1,96 +1,92 @@ 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_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 float3 $k(float3 a,float4 b,float3 c){return mix(pow(b.y*a+b.z" -",b.x.xxx)+c.y,b.w*a+c.z,lessThan(a,c.x.xxx));}$pure float3 $l(float3 a,float3" -" b,float3 c){float3 d=pow(a,b.z.xxx);return pow(max(b.x+b.y*d,0.)/(c.x+c.y*" -"d),c.z.xxx);}$pure float3 $m(float3 a,float3 b,float3 c){return(c.z+1.)*mix" -"(exp((a-c.y)*b.z)+c.x,pow(a*b.x,b.y.xxx),lessThanEqual(a*b.x,1..xxx));}$pure" -" float3 $n(float3 a,float3 b,float3 c){a/=c.z+1.;return mix(b.z*log(a-c.x)+" -"c.y,b.x*pow(a,b.y.xxx),lessThanEqual(a,1..xxx));}$pure half4 sk_color_space_transform" -"(half4 a,half3x3 b,float4 c,float4 d,float4 e,float4 f,float4 g,float4 h){if" -"(d.w<0.)a=unpremul(a);else{half i=1.-half(d.w);half j=half(d.w)*half(f.w);half" -" k=half(d.w)-j;a.w=dot(half3(a.wx,1.),half3(i,k,j));}float3 i=float3(a.xyz)" -";if(c.x>0.)i=sign(i)*$k(abs(i),c,d.xyz);else if(c.x<-1.)i=sign(i)*$l(abs(i)" -",c.yzw,d.xyz);else if(c.x<0.)i=sign(i)*$m(abs(i),c.yzw,d.xyz);if(g.w!=0.){float" -" j=dot(g.xyz,i);i*=sign(j)*pow(abs(j),g.w);}i=float3x3(b)*i;if(h.w!=0.){float" -" j=dot(h.xyz,i);i*=sign(j)*pow(abs(j),h.w);}if(e.x>0.)i=sign(i)*$k(abs(i),e" -",f.xyz);else if(e.x<-1.)i=sign(i)*$l(abs(i),e.yzw,f.xyz);else if(e.x<0.)i=sign" -"(i)*$n(abs(i),e.yzw,f.xyz);half j=half(f.w);a.xyz=half3(i)*max(a.w,j);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," -"float4 c,float4 d,float4 e,float4 f){if(d.w<0.)a=unpremul(a);else{half g=1." -"-half(d.w);half h=half(d.w)*half(f.w);half i=half(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=half(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" -" $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(i<c.x||i>c.z)return half4(0.);}if(e==3&&f==0){float" -" i=floor(a.y)+.5;if(i<c.y||i>c.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,c.zw-g);float2 j=clamp(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=$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 $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]*$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 $q(a,b,c,d,e,f,.50001.xx,g);}$pure" -" half4 sk_image_shader_clamp(float2 a,float2 b,float4 c,sampler2D d){return" -" $p(clamp(a,c.xy,c.zw),b,d);}$pure half4 sk_cubic_image_shader(float2 a,float2" -" b,float4 c,int d,int e,half4x4 f,sampler2D g){return $r(a,b,c,d,e,f,g);}$pure" -" half4 sk_hw_image_shader(float2 a,sampler2D b){return sample(b,a);}$pure half4" -" $s(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 $t(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 $s(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=$q(a,b,d,f,g,h,.50001.xx,p);w=$q(a,c,d,t,u,i,e,q);x=$q(a" -",c,d,t,u,i,e,r);half y;if(m==half4(1.))y=1.;else{half4 z=$q(a,b,d,f,g,h,.50001" -".xx,s);y=dot(m,z);}return $t(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=$r(a,b,d,e,f,g," -"n);u=$r(a,c,d,r,s,g,o);v=$r(a,c,d,r,s,g,p);half w;if(k==half4(1.))w=1.;else" -"{half4 x=$r(a,b,d,e,f,g,q);w=dot(k,x);}return $t(t,u,v,w,h,i,j,l,m);}$pure half4" -" sk_hw_yuv_image_shader(float2 a,float2 b,float2 c,float4 d,float2 e,half4 f" -",half4 g,half4 h,half4 i,half3x3 j,half3 k,sampler2D l,sampler2D m,sampler2D" -" n,sampler2D o){float4 p=d;if(e.x<0.){a=floor(a)+.5;d=float4(floor(d.xy),ceil" -"(d.zw));}float2 q=a;if(e.y<0.){e=abs(e);q=clamp(a,p.xy+e,p.zw-e);a=clamp(a," -"d.xy+.50001,d.zw-.50001);}half4 r;half4 s;half4 t;r=$p(a,b,l);s=$p(q,c,m);t" -"=$p(q,c,n);half v;if(i==half4(1.))v=1.;else{half4 w=$p(a,b,o);v=dot(i,w);}return" -" $t(r,s,t,v,f,g,h,j,k);}$pure half4 sk_hw_yuv_no_swizzle_image_shader(float2" -" a,float2 b,float2 c,float4 d,float2 e,half3x3 f,half4 g,sampler2D h,sampler2D" -" i,sampler2D j,sampler2D k){float4 l=d;if(e.x<0.){a=floor(a)+.5;d=float4(floor" -"(d.xy),ceil(d.zw));}float2 m=a;if(e.y<0.){e=abs(e);m=clamp(a,l.xy+e,l.zw-e)" -";a=clamp(a,d.xy+.50001,d.zw-.50001);}half r=$p(a,b,h).x;half s=$p(m,c,i).x;" -"half t=$p(m,c,j).x;half u=saturate($p(a,b,k).x+g.w);return $s(r,s,t,u,f,g.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 $u(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" +"(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 float3 $k(" +"float3 a,half4 b,half3 c){return mix(pow(float(b.y)*a+float(b.z),float(b.x)" +".xxx)+float(c.y),float(b.w)*a+float(c.z),lessThan(a,float(c.x).xxx));}$pure" +" float3 $l(float3 a,half3 b,half3 c){float3 d=pow(a,float(b.z).xxx);return pow" +"(max(float(b.x)+float(b.y)*d,0.)/(float(c.x)+float(c.y)*d),float(c.z).xxx);" +"}$pure float3 $m(float3 a,half3 b,half3 c){return float(c.z+1.)*mix(exp((a-" +"float(c.y))*float(b.z))+float(c.x),pow(a*float(b.x),float(b.y).xxx),lessThanEqual" +"(a*float(b.x),1..xxx));}$pure float3 $n(float3 a,half3 b,half3 c){a/=float(" +"c.z+1.);return mix(float(b.z)*log(a-float(c.x))+float(c.y),float(b.x)*pow(a" +",float(b.y).xxx),lessThanEqual(a,1..xxx));}$pure half4 sk_color_space_transform" +"(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);if(c.x>0.)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 $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(i<c.x||i>c.z)return half4(0.);}if" +"(e==3&&f==0){float i=floor(a.y)+.5;if(i<c.y||i>c.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=$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=$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 $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" +"]*$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 $q(a,b,c," +"d,e,f,.50001.xx,g);}$pure half4 sk_image_shader_clamp(float2 a,float2 b,float4" +" c,sampler2D d){return $p(clamp(a,c.xy,c.zw),b,d);}$pure half4 sk_cubic_image_shader" +"(float2 a,float2 b,float4 c,int d,int e,half4x4 f,sampler2D g){return $r(a," +"b,c,d,e,f,g);}$pure half4 sk_hw_image_shader(float2 a,float2 b,sampler2D c)" +"{return $p(a,b,c);}$pure half4 $s(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 $t(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 $s(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=$q(a,b,d,f,g,h,.50001." +"xx,p);w=$q(a,c,d,t,u,i,e,q);x=$q(a,c,d,t,u,i,e,r);half y;if(m==half4(1.))y=" +"1.;else{half4 z=$q(a,b,d,f,g,h,.50001.xx,s);y=dot(m,z);}return $t(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=$r(a,b,d,e,f,g,n);u=$r(a,c,d,r,s,g,o);v=$r(" +"a,c,d,r,s,g,p);half w;if(k==half4(1.))w=1.;else{half4 x=$r(a,b,d,e,f,g,q);w" +"=dot(k,x);}return $t(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=$p(a,b,j);o=$p(a,c,k);p=$p(a,c,l);half r;if(g==half4(1.))r=1.;else{half4" +" s=$p(a,b,m);r=dot(g,s);}return $t(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=$p(a,b,f).x;half o=$p(a,c,g).x;half p=$p(a,c,h).x;half" +" q=saturate($p(a,b,i).x+e.w);return $s(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 $u(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 $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<b.y)" "return half4(mix(a[0],a[1],(c.x-b.x)/(b.y-b.x)));else if(c.x<b.z)return half4" @@ -166,96 +162,96 @@ static constexpr char SKSL_MINIFIED_sksl_graphite_frag[] = "(l,h,i);}$pure half4 sk_conical_grad_buf_shader(float2 a,float b,float c,float" " d,float e,int f,int g,int h,int i,int j,float[]k){float2 l=$D(b,c,d,e,a);l" "=$u(h,l);half4 m=$z(k,g,f,l);return $interpolated_to_rgb_unpremul(m,i,j);}$pure" -" half4 sk_hsl_matrix_colorfilter(half4 a,half4x4 b,half4 c){a=$rgb_to_hsl(a" -".xyz,a.w);a=b*a+c;return $hsl_to_rgb(a.xyz,a.w);}$pure half4 sk_matrix_colorfilter" -"(half4 a,half4x4 b,half4 c,half2 d){a=unpremul(a);a=b*a+c;a=clamp(a,half4(d" -".xxx,0.),half4(d.yyy,1.));a.xyz*=a.w;return a;}$pure half4 $E(half2 a,half2" -" b,int c,sampler2D d){half4 f;f.xy=floor(a);f.zw=f.xy+half2(1.);if(bool(c))" -"f-=step(b.xyxy,f)*b.xyxy;half g=sample(d,float2(half2((f.x+.5)*.00390625,.5" -"))).x;half h=sample(d,float2(half2((f.z+.5)*.00390625,.5))).x;half2 i=half2" -"(g,h);if(sk_Caps.PerlinNoiseRoundingFix)i=floor(i*half2(255.)+half2(.5))*half2" -"(.003921569);half4 j=256.*i.xyxy+f.yyww;j*=half4(.00390625);return j;}$pure" -" half4 $F(half2 a,half4 b,sampler2D c){half2 d=fract(a);half2 e=smoothstep(" -"0.,1.,d);const half f=.00390625;half4 g;for(int h=0;h<4;h++){half i=(half(h" -")+.5)*.25;half4 j=sample(c,float2(float(b.x),float(i)));half4 k=sample(c,float2" -"(float(b.y),float(i)));half4 l=sample(c,float2(float(b.w),float(i)));half4 m" -"=sample(c,float2(float(b.z),float(i)));half2 n=d;half o=dot((j.yw+j.xz*f)*2." -"-1.,n);n.x-=1.;half p=dot((k.yw+k.xz*f)*2.-1.,n);half q=mix(o,p,e.x);n.y-=1." -";p=dot((l.yw+l.xz*f)*2.-1.,n);n.x+=1.;o=dot((m.yw+m.xz*f)*2.-1.,n);half r=mix" -"(o,p,e.x);g[h]=mix(q,r,e.y);}return g;}$pure half4 sk_perlin_noise_shader(float2" -" a,float2 b,float2 c,int d,int e,int f,sampler2D g,sampler2D h){half2 k=half2" -"((a+.5)*b);half4 l=half4(0.);half2 m=half2(c);half n=1.;for(int o=0;o<e;++o" -"){half4 p=$E(k,m,f,g);half4 q=$F(k,p,h);if(d!=0)q=abs(q);l+=q*n;k*=half2(2." -");n*=.5;m*=half2(2.);}if(d==0)l=l*half4(.5)+half4(.5);l=saturate(l);return half4" -"(l.xyz*l.w,l.w);}$pure half4 sk_porter_duff_blend(half4 a,half4 b,half4 c){" -"return blend_porter_duff(c,a,b);}$pure half4 sk_hslc_blend(half4 a,half4 b," -"half2 c){return blend_hslc(c,a,b);}$pure half4 sk_table_colorfilter(half4 a" -",sampler2D b){half4 c=unpremul(a)*.99609375+.001953125;half4 d=half4(sample" -"(b,float2(half2(c.x,.375))).x,sample(b,float2(half2(c.y,.625))).x,sample(b," -"float2(half2(c.z,.875))).x,1.);return d*sample(b,float2(half2(c.w,.125))).x" -";}$pure half4 sk_gaussian_colorfilter(half4 a){half b=1.-a.w;b=exp((-b*b)*4." -")-.018;return b.xxxx;}$pure half4 sample_indexed_atlas(float2 a,int b,sampler2D" -" c,sampler2D d,sampler2D e,sampler2D f){switch(b){case 1:return sample(d,a)" -";case 2:return sample(e,a);case 3:return sample(f,a);default:return sample(" -"c,a);}}$pure half3 $G(float2 a,int b,half2 c,sampler2D d,sampler2D e,sampler2D" -" f,sampler2D g){half3 h=half3(1.);switch(b){case 1:h.x=sample(e,float2(half2" -"(a)-c)).x;h.y=sample(e,a).x;h.z=sample(e,float2(half2(a)+c)).x;case 2:h.x=sample" -"(f,float2(half2(a)-c)).x;h.y=sample(f,a).x;h.z=sample(f,float2(half2(a)+c))" -".x;case 3:h.x=sample(g,float2(half2(a)-c)).x;h.y=sample(g,a).x;h.z=sample(g" -",float2(half2(a)+c)).x;default:h.x=sample(d,float2(half2(a)-c)).x;h.y=sample" -"(d,a).x;h.z=sample(d,float2(half2(a)+c)).x;}return h;}$pure half4 bitmap_text_coverage_fn" -"(half4 a,int b){return b==0?a.xxxx:a;}$pure half4 sdf_text_coverage_fn(half" -" a,half2 b,float2 c){half d=7.96875*(a-.5019608);d-=b.x;half2 e=half2(dFdx(" -"d),dFdy(d));half f=dot(e,e);e=f>=.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.);}"; +" half4 sk_matrix_colorfilter(half4 a,float4x4 b,float4 c,int d,int e){if(bool" +"(d))a=$rgb_to_hsl(a.xyz,a.w);else a=unpremul(a);half4 f=half4(b*float4(a)+c" +");if(bool(d))f=$hsl_to_rgb(f.xyz,f.w);else{if(bool(e))f=saturate(f);else f." +"w=saturate(f.w);f.xyz*=f.w;}return f;}$pure half4 $E(half2 a,half2 b,int c," +"sampler2D d){half4 f;f.xy=floor(a);f.zw=f.xy+half2(1.);if(bool(c))f-=step(b" +".xyxy,f)*b.xyxy;half g=sample(d,float2(half2((f.x+.5)*.00390625,.5))).x;half" +" h=sample(d,float2(half2((f.z+.5)*.00390625,.5))).x;half2 i=half2(g,h);if(sk_Caps" +".PerlinNoiseRoundingFix)i=floor(i*half2(255.)+half2(.5))*half2(.003921569);" +"half4 j=256.*i.xyxy+f.yyww;j*=half4(.00390625);return j;}$pure half4 $F(half2" +" a,half4 b,sampler2D c){half2 d=fract(a);half2 e=smoothstep(0.,1.,d);const half" +" f=.00390625;half4 g;for(int h=0;h<4;h++){half i=(half(h)+.5)*.25;half4 j=sample" +"(c,float2(float(b.x),float(i)));half4 k=sample(c,float2(float(b.y),float(i)" +"));half4 l=sample(c,float2(float(b.w),float(i)));half4 m=sample(c,float2(float" +"(b.z),float(i)));half2 n=d;half o=dot((j.yw+j.xz*f)*2.-1.,n);n.x-=1.;half p" +"=dot((k.yw+k.xz*f)*2.-1.,n);half q=mix(o,p,e.x);n.y-=1.;p=dot((l.yw+l.xz*f)" +"*2.-1.,n);n.x+=1.;o=dot((m.yw+m.xz*f)*2.-1.,n);half r=mix(o,p,e.x);g[h]=mix" +"(q,r,e.y);}return g;}$pure half4 sk_perlin_noise_shader(float2 a,float2 b,float2" +" c,int d,int e,int f,sampler2D g,sampler2D h){half2 k=half2((a+.5)*b);half4" +" l=half4(0.);half2 m=half2(c);half n=1.;for(int o=0;o<e;++o){half4 p=$E(k,m" +",f,g);half4 q=$F(k,p,h);if(d!=0)q=abs(q);l+=q*n;k*=half2(2.);n*=.5;m*=half2" +"(2.);}if(d==0)l=l*half4(.5)+half4(.5);l=saturate(l);return half4(l.xyz*l.w," +"l.w);}$pure half4 sk_porter_duff_blend(half4 a,half4 b,half4 c){return blend_porter_duff" +"(c,a,b);}$pure half4 sk_hslc_blend(half4 a,half4 b,half2 c){return blend_hslc" +"(c,a,b);}$pure half4 sk_table_colorfilter(half4 a,sampler2D b){half4 c=unpremul" +"(a)*.99609375+.001953125;half4 d=half4(sample(b,float2(half2(c.x,.375))).x," +"sample(b,float2(half2(c.y,.625))).x,sample(b,float2(half2(c.z,.875))).x,1.)" +";return d*sample(b,float2(half2(c.w,.125))).x;}$pure half4 sk_gaussian_colorfilter" +"(half4 a){half b=1.-a.w;b=exp((-b*b)*4.)-.018;return b.xxxx;}$pure half4 sample_indexed_atlas" +"(float2 a,int b,sampler2D c,sampler2D d,sampler2D e,sampler2D f){switch(b){" +"case 1:return sample(d,a);case 2:return sample(e,a);case 3:return sample(f," +"a);default:return sample(c,a);}}$pure half3 $G(float2 a,int b,half2 c,sampler2D" +" d,sampler2D e,sampler2D f,sampler2D g){half3 h=half3(1.);switch(b){case 1:" +"h.x=sample(e,float2(half2(a)-c)).x;h.y=sample(e,a).x;h.z=sample(e,float2(half2" +"(a)+c)).x;case 2:h.x=sample(f,float2(half2(a)-c)).x;h.y=sample(f,a).x;h.z=sample" +"(f,float2(half2(a)+c)).x;case 3:h.x=sample(g,float2(half2(a)-c)).x;h.y=sample" +"(g,a).x;h.z=sample(g,float2(half2(a)+c)).x;default:h.x=sample(d,float2(half2" +"(a)-c)).x;h.y=sample(d,a).x;h.z=sample(d,float2(half2(a)+c)).x;}return h;}$pure" +" half4 bitmap_text_coverage_fn(half4 a,int b){return b==0?a.xxxx:a;}$pure half4" +" sdf_text_coverage_fn(half a,half2 b,float2 c){half d=7.96875*(a-.5019608);" +"d-=b.x;half2 e=half2(dFdx(d),dFdy(d));half f=dot(e,e);e=f>=.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 @@ -3,91 +3,91 @@ static constexpr char SKSL_MINIFIED_sksl_graphite_frag[] = "=3;const int $kFilterModeNearest=0;const int $kFilterModeLinear=1;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_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 float3 $apply_srgb_xfer_fn(float3 x,float4 gabc" -",float3 def){return mix(pow(gabc.y*x+gabc.z,gabc.x.xxx)+def.y,gabc.w*x+def." -"z,lessThan(x,def.x.xxx));}$pure float3 $apply_pq_xfer_fn(float3 x,float3 abc" -",float3 def){float3 x_C=pow(x,abc.z.xxx);return pow(max(abc.x+abc.y*x_C,0.)" -"/(def.x+def.y*x_C),def.z.xxx);}$pure float3 $apply_hlg_xfer_fn(float3 x,float3" -" abc,float3 def){return(def.z+1.)*mix(exp((x-def.y)*abc.z)+def.x,pow(x*abc." -"x,abc.y.xxx),lessThanEqual(x*abc.x,1..xxx));}$pure float3 $apply_hlg_inv_xfer_fn" -"(float3 x,float3 abc,float3 def){x/=def.z+1.;return mix(abc.z*log(x-def.x)+" -"def.y,abc.x*pow(x,abc.y.xxx),lessThanEqual(x,1..xxx));}$pure half4 sk_color_space_transform" -"(half4 color,half3x3 gamut,float4 srcGABC,float4 srcDEF_args,float4 dstGABC" -",float4 dstDEF_args,float4 srcOOTF_args,float4 dstOOTF_args){if(srcDEF_args" -".w<0.)color=unpremul(color);else{half alphaSwizzleA=1.-half(srcDEF_args.w);" -"half alphaSwizzle1=half(srcDEF_args.w)*half(dstDEF_args.w);half alphaSwizzleR" -"=half(srcDEF_args.w)-alphaSwizzle1;color.w=dot(half3(color.wx,1.),half3(alphaSwizzleA" -",alphaSwizzleR,alphaSwizzle1));}float3 colorF=float3(color.xyz);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);if(srcOOTF_args.w!=0.){float Y=dot" -"(srcOOTF_args.xyz,colorF);colorF*=sign(Y)*pow(abs(Y),srcOOTF_args.w);}colorF" -"=float3x3(gamut)*colorF;if(dstOOTF_args.w!=0.){float Y=dot(dstOOTF_args.xyz" -",colorF);colorF*=sign(Y)*pow(abs(Y),dstOOTF_args.w);}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=half(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,float4 srcGABC,float4 srcDEF_args,float4 dstGABC" -",float4 dstDEF_args){if(srcDEF_args.w<0.)color=unpremul(color);else{half alphaSwizzleA" -"=1.-half(srcDEF_args.w);half alphaSwizzle1=half(srcDEF_args.w)*half(dstDEF_args" -".w);half alphaSwizzleR=half(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=half(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" -"(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 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(snappedX<subset.x||" -"snappedX>subset.z)return half4(0.);}if(tileModeY==$kTileModeDecal&&filterMode" -"==$kFilterModeNearest){float snappedY=floor(pos.y)+.5;if(snappedY<subset.y||" -"snappedY>subset.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" -",subset.zw-linearFilterInset);float2 clampedPos=clamp(pos,insetClamp.xy,insetClamp" -".zw);half4 color=$sample_image(clampedPos,invImgSize,s);if(filterMode==$kFilterModeLinear" -"){half2 error=half2(pos-clampedPos);half2 absError=abs(error);bool sampleExtraX" -"=tileModeX==$kTileModeRepeat;bool sampleExtraY=tileModeY==$kTileModeRepeat;" -"if(sampleExtraX||sampleExtraY){float extraCoordX;float extraCoordY;half4 extraColorX" -";half4 extraColorY;if(sampleExtraX){extraCoordX=error.x>0.?insetClamp.x:insetClamp" -".z;extraColorX=$sample_image(float2(extraCoordX,clampedPos.y),invImgSize,s)" -";}if(sampleExtraY){extraCoordY=error.y>0.?insetClamp.y:insetClamp.w;extraColorY" -"=$sample_image(float2(clampedPos.x,extraCoordY),invImgSize,s);}if(sampleExtraX" -"&&sampleExtraY){half4 extraColorXY=$sample_image(float2(extraCoordX,extraCoordY" -"),invImgSize,s);color=mix(mix(color,extraColorX,absError.x),mix(extraColorY" -",extraColorXY,absError.x),absError.y);}else if(sampleExtraX)color=mix(color" -",extraColorX,absError.x);else if(sampleExtraY)color=mix(color,extraColorY,absError" -".y);}if(tileModeX==$kTileModeDecal)color*=max(1.-absError.x,0.);if(tileModeY" -"==$kTileModeDecal)color*=max(1.-absError.y,0.);}return color;}$pure half4 $cubic_filter_image" +"$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 float3 $apply_srgb_xfer_fn(float3 x,half4 gabc,half3" +" def){return mix(pow(float(gabc.y)*x+float(gabc.z),float(gabc.x).xxx)+float" +"(def.y),float(gabc.w)*x+float(def.z),lessThan(x,float(def.x).xxx));}$pure float3" +" $apply_pq_xfer_fn(float3 x,half3 abc,half3 def){float3 x_C=pow(x,float(abc" +".z).xxx);return pow(max(float(abc.x)+float(abc.y)*x_C,0.)/(float(def.x)+float" +"(def.y)*x_C),float(def.z).xxx);}$pure float3 $apply_hlg_xfer_fn(float3 x,half3" +" abc,half3 def){return float(def.z+1.)*mix(exp((x-float(def.y))*float(abc.z" +"))+float(def.x),pow(x*float(abc.x),float(abc.y).xxx),lessThanEqual(x*float(" +"abc.x),1..xxx));}$pure float3 $apply_hlg_inv_xfer_fn(float3 x,half3 abc,half3" +" def){x/=float(def.z+1.);return mix(float(abc.z)*log(x-float(def.x))+float(" +"def.y),float(abc.x)*pow(x,float(abc.y).xxx),lessThanEqual(x,1..xxx));}$pure" +" half4 sk_color_space_transform(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);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);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(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 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(snappedX<subset.x||snappedX>subset.z)return half4(0.);}if(tileModeY" +"==$kTileModeDecal&&filterMode==$kFilterModeNearest){float snappedY=floor(pos" +".y)+.5;if(snappedY<subset.y||snappedY>subset.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" +"(pos-clampedPos);half2 absError=abs(error);bool sampleExtraX=tileModeX==$kTileModeRepeat" +";bool sampleExtraY=tileModeY==$kTileModeRepeat;if(sampleExtraX||sampleExtraY" +"){float extraCoordX;float extraCoordY;half4 extraColorX;half4 extraColorY;if" +"(sampleExtraX){extraCoordX=error.x>0.?insetClamp.x:insetClamp.z;extraColorX" +"=$sample_image(float2(extraCoordX,clampedPos.y),invImgSize,s);}if(sampleExtraY" +"){extraCoordY=error.y>0.?insetClamp.y:insetClamp.w;extraColorY=$sample_image" +"(float2(clampedPos.x,extraCoordY),invImgSize,s);}if(sampleExtraX&&sampleExtraY" +"){half4 extraColorXY=$sample_image(float2(extraCoordX,extraCoordY),invImgSize" +",s);color=mix(mix(color,extraColorX,absError.x),mix(extraColorY,extraColorXY" +",absError.x),absError.y);}else if(sampleExtraX)color=mix(color,extraColorX," +"absError.x);else if(sampleExtraY)color=mix(color,extraColorY,absError.y);}if" +"(tileModeX==$kTileModeDecal)color*=max(1.-absError.x,0.);if(tileModeY==$kTileModeDecal" +")color*=max(1.-absError.y,0.);}return color;}$pure half4 $cubic_filter_image" "(float2 pos,float2 invImgSize,float4 subset,int tileModeX,int tileModeY,half4x4" " coeffs,sampler2D s){float2 f=fract(pos-.5);pos-=1.5;pos=floor(pos)+.5;half4" " wx=coeffs*half4(1.,half(f.x),half(f.x*f.x),half((f.x*f.x)*f.x));half4 wy=coeffs" @@ -104,73 +104,63 @@ static constexpr char SKSL_MINIFIED_sksl_graphite_frag[] = ".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,sampler2D s){return sample" -"(s,coords);}$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" +",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,float4 subset,float2 linearFilterUVInset" -",half4 channelSelectY,half4 channelSelectU,half4 channelSelectV,half4 channelSelectA" -",half3x3 yuvToRGBMatrix,half3 yuvToRGBTranslate,sampler2D sY,sampler2D sU,sampler2D" -" sV,sampler2D sA){float4 subsetUV=subset;if(linearFilterUVInset.x<0.){coords" -"=floor(coords)+.5;subset=float4(floor(subset.xy),ceil(subset.zw));}float2 coordsUV" -"=coords;if(linearFilterUVInset.y<0.){linearFilterUVInset=abs(linearFilterUVInset" -");coordsUV=clamp(coords,subsetUV.xy+linearFilterUVInset,subsetUV.zw-linearFilterUVInset" -");coords=clamp(coords,subset.xy+$kLinearInset,subset.zw-$kLinearInset);}half4" -" sampleColorY;half4 sampleColorU;half4 sampleColorV;sampleColorY=$sample_image" -"(coords,invImgSizeY,sY);sampleColorU=$sample_image(coordsUV,invImgSizeUV,sU" -");sampleColorV=$sample_image(coordsUV,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,float4 subset,float2 linearFilterUVInset" +");}$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){float4 subsetUV=subset;if(linearFilterUVInset" -".x<0.){coords=floor(coords)+.5;subset=float4(floor(subset.xy),ceil(subset.zw" -"));}float2 coordsUV=coords;if(linearFilterUVInset.y<0.){linearFilterUVInset" -"=abs(linearFilterUVInset);coordsUV=clamp(coords,subsetUV.xy+linearFilterUVInset" -",subsetUV.zw-linearFilterUVInset);coords=clamp(coords,subset.xy+$kLinearInset" -",subset.zw-$kLinearInset);}half Y=$sample_image(coords,invImgSizeY,sY).x;half" -" U=$sample_image(coordsUV,invImgSizeUV,sU).x;half V=$sample_image(coordsUV," -"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" +" 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" @@ -318,55 +308,56 @@ static constexpr char SKSL_MINIFIED_sksl_graphite_frag[] = "[]colorAndOffsetData){float2 t=$conical_grad_layout(radius0Param,dRadiusParam" ",aParam,invAParam,coords);t=$tile_grad(tileMode,t);half4 color=$colorize_grad_buf" "(colorAndOffsetData,bufferOffset,numStops,t);return $interpolated_to_rgb_unpremul" -"(color,colorSpace,doUnpremul);}$pure half4 sk_hsl_matrix_colorfilter(half4 color" -",half4x4 m,half4 v){color=$rgb_to_hsl(color.xyz,color.w);color=m*color+v;return" -" $hsl_to_rgb(color.xyz,color.w);}$pure half4 sk_matrix_colorfilter(half4 color" -",half4x4 m,half4 v,half2 minMaxRGB){color=unpremul(color);color=m*color+v;color" -"=clamp(color,half4(minMaxRGB.xxx,0.),half4(minMaxRGB.yyy,1.));color.xyz*=color" -".w;return color;}$pure half4 $noise_helper(half2 noiseVec,half2 stitchData," -"int stitching,sampler2D permutationSampler){const half kBlockSize=256.;half4" -" floorVal;floorVal.xy=floor(noiseVec);floorVal.zw=floorVal.xy+half2(1.);if(" -"bool(stitching))floorVal-=step(stitchData.xyxy,floorVal)*stitchData.xyxy;half" -" sampleX=sample(permutationSampler,float2(half2((floorVal.x+.5)*.00390625,.5" -"))).x;half sampleY=sample(permutationSampler,float2(half2((floorVal.z+.5)*.00390625" -",.5))).x;half2 latticeIdx=half2(sampleX,sampleY);if(sk_Caps.PerlinNoiseRoundingFix" -")latticeIdx=floor(latticeIdx*half2(255.)+half2(.5))*half2(.003921569);half4" -" noiseXCoords=kBlockSize*latticeIdx.xyxy+floorVal.yyww;noiseXCoords*=half4(" -".00390625);return noiseXCoords;}$pure half4 $noise_function(half2 noiseVec," -"half4 noiseXCoords,sampler2D noiseSampler){half2 fractVal=fract(noiseVec);half2" -" noiseSmooth=smoothstep(0.,1.,fractVal);const half kInv256=.00390625;half4 result" -";for(int channel=0;channel<4;channel++){half chanCoord=(half(channel)+.5)*.25" -";half4 sampleA=sample(noiseSampler,float2(float(noiseXCoords.x),float(chanCoord" -")));half4 sampleB=sample(noiseSampler,float2(float(noiseXCoords.y),float(chanCoord" -")));half4 sampleC=sample(noiseSampler,float2(float(noiseXCoords.w),float(chanCoord" -")));half4 sampleD=sample(noiseSampler,float2(float(noiseXCoords.z),float(chanCoord" -")));half2 tmpFractVal=fractVal;half u=dot((sampleA.yw+sampleA.xz*kInv256)*2." -"-1.,tmpFractVal);tmpFractVal.x-=1.;half v=dot((sampleB.yw+sampleB.xz*kInv256" -")*2.-1.,tmpFractVal);half a=mix(u,v,noiseSmooth.x);tmpFractVal.y-=1.;v=dot(" -"(sampleC.yw+sampleC.xz*kInv256)*2.-1.,tmpFractVal);tmpFractVal.x+=1.;u=dot(" -"(sampleD.yw+sampleD.xz*kInv256)*2.-1.,tmpFractVal);half b=mix(u,v,noiseSmooth" -".x);result[channel]=mix(a,b,noiseSmooth.y);}return result;}$pure half4 sk_perlin_noise_shader" -"(float2 coords,float2 baseFrequency,float2 stitchDataIn,int noiseType,int numOctaves" -",int stitching,sampler2D permutationSampler,sampler2D noiseSampler){const int" -" kFractalNoise=0;half2 noiseVec=half2((coords+.5)*baseFrequency);half4 color" -"=half4(0.);half2 stitchData=half2(stitchDataIn);half ratio=1.;for(int octave" -"=0;octave<numOctaves;++octave){half4 noiseXCoords=$noise_helper(noiseVec,stitchData" -",stitching,permutationSampler);half4 tmp=$noise_function(noiseVec,noiseXCoords" -",noiseSampler);if(noiseType!=kFractalNoise)tmp=abs(tmp);color+=tmp*ratio;noiseVec" -"*=half2(2.);ratio*=.5;stitchData*=half2(2.);}if(noiseType==kFractalNoise)color" -"=color*half4(.5)+half4(.5);color=saturate(color);return half4(color.xyz*color" -".w,color.w);}$pure half4 sk_porter_duff_blend(half4 src,half4 dst,half4 coeffs" -"){return blend_porter_duff(coeffs,src,dst);}$pure half4 sk_hslc_blend(half4" -" src,half4 dst,half2 flipSat){return blend_hslc(flipSat,src,dst);}$pure half4" -" sk_table_colorfilter(half4 inColor,sampler2D s){half4 coords=unpremul(inColor" -")*.99609375+.001953125;half4 color=half4(sample(s,float2(half2(coords.x,.375" -"))).x,sample(s,float2(half2(coords.y,.625))).x,sample(s,float2(half2(coords" -".z,.875))).x,1.);return color*sample(s,float2(half2(coords.w,.125))).x;}$pure" -" half4 sk_gaussian_colorfilter(half4 inColor){half factor=1.-inColor.w;factor" -"=exp((-factor*factor)*4.)-.018;return factor.xxxx;}$pure half4 sample_indexed_atlas" -"(float2 textureCoords,int atlasIndex,sampler2D atlas0,sampler2D atlas1,sampler2D" -" atlas2,sampler2D atlas3){switch(atlasIndex){case 1:return sample(atlas1,textureCoords" -");case 2:return sample(atlas2,textureCoords);case 3:return sample(atlas3,textureCoords" +"(color,colorSpace,doUnpremul);}$pure half4 sk_matrix_colorfilter(half4 colorIn" +",float4x4 m,float4 v,int inHSLA,int clampRGB){if(bool(inHSLA))colorIn=$rgb_to_hsl" +"(colorIn.xyz,colorIn.w);else colorIn=unpremul(colorIn);half4 colorOut=half4" +"(m*float4(colorIn)+v);if(bool(inHSLA))colorOut=$hsl_to_rgb(colorOut.xyz,colorOut" +".w);else{if(bool(clampRGB))colorOut=saturate(colorOut);else colorOut.w=saturate" +"(colorOut.w);colorOut.xyz*=colorOut.w;}return colorOut;}$pure half4 $noise_helper" +"(half2 noiseVec,half2 stitchData,int stitching,sampler2D permutationSampler" +"){const half kBlockSize=256.;half4 floorVal;floorVal.xy=floor(noiseVec);floorVal" +".zw=floorVal.xy+half2(1.);if(bool(stitching))floorVal-=step(stitchData.xyxy" +",floorVal)*stitchData.xyxy;half sampleX=sample(permutationSampler,float2(half2" +"((floorVal.x+.5)*.00390625,.5))).x;half sampleY=sample(permutationSampler,float2" +"(half2((floorVal.z+.5)*.00390625,.5))).x;half2 latticeIdx=half2(sampleX,sampleY" +");if(sk_Caps.PerlinNoiseRoundingFix)latticeIdx=floor(latticeIdx*half2(255.)" +"+half2(.5))*half2(.003921569);half4 noiseXCoords=kBlockSize*latticeIdx.xyxy" +"+floorVal.yyww;noiseXCoords*=half4(.00390625);return noiseXCoords;}$pure half4" +" $noise_function(half2 noiseVec,half4 noiseXCoords,sampler2D noiseSampler){" +"half2 fractVal=fract(noiseVec);half2 noiseSmooth=smoothstep(0.,1.,fractVal)" +";const half kInv256=.00390625;half4 result;for(int channel=0;channel<4;channel" +"++){half chanCoord=(half(channel)+.5)*.25;half4 sampleA=sample(noiseSampler" +",float2(float(noiseXCoords.x),float(chanCoord)));half4 sampleB=sample(noiseSampler" +",float2(float(noiseXCoords.y),float(chanCoord)));half4 sampleC=sample(noiseSampler" +",float2(float(noiseXCoords.w),float(chanCoord)));half4 sampleD=sample(noiseSampler" +",float2(float(noiseXCoords.z),float(chanCoord)));half2 tmpFractVal=fractVal" +";half u=dot((sampleA.yw+sampleA.xz*kInv256)*2.-1.,tmpFractVal);tmpFractVal." +"x-=1.;half v=dot((sampleB.yw+sampleB.xz*kInv256)*2.-1.,tmpFractVal);half a=" +"mix(u,v,noiseSmooth.x);tmpFractVal.y-=1.;v=dot((sampleC.yw+sampleC.xz*kInv256" +")*2.-1.,tmpFractVal);tmpFractVal.x+=1.;u=dot((sampleD.yw+sampleD.xz*kInv256" +")*2.-1.,tmpFractVal);half b=mix(u,v,noiseSmooth.x);result[channel]=mix(a,b," +"noiseSmooth.y);}return result;}$pure half4 sk_perlin_noise_shader(float2 coords" +",float2 baseFrequency,float2 stitchDataIn,int noiseType,int numOctaves,int stitching" +",sampler2D permutationSampler,sampler2D noiseSampler){const int kFractalNoise" +"=0;half2 noiseVec=half2((coords+.5)*baseFrequency);half4 color=half4(0.);half2" +" stitchData=half2(stitchDataIn);half ratio=1.;for(int octave=0;octave<numOctaves" +";++octave){half4 noiseXCoords=$noise_helper(noiseVec,stitchData,stitching,permutationSampler" +");half4 tmp=$noise_function(noiseVec,noiseXCoords,noiseSampler);if(noiseType" +"!=kFractalNoise)tmp=abs(tmp);color+=tmp*ratio;noiseVec*=half2(2.);ratio*=.5" +";stitchData*=half2(2.);}if(noiseType==kFractalNoise)color=color*half4(.5)+half4" +"(.5);color=saturate(color);return half4(color.xyz*color.w,color.w);}$pure half4" +" sk_porter_duff_blend(half4 src,half4 dst,half4 coeffs){return blend_porter_duff" +"(coeffs,src,dst);}$pure half4 sk_hslc_blend(half4 src,half4 dst,half2 flipSat" +"){return blend_hslc(flipSat,src,dst);}$pure half4 sk_table_colorfilter(half4" +" inColor,sampler2D s){half4 coords=unpremul(inColor)*.99609375+.001953125;half4" +" color=half4(sample(s,float2(half2(coords.x,.375))).x,sample(s,float2(half2" +"(coords.y,.625))).x,sample(s,float2(half2(coords.z,.875))).x,1.);return color" +"*sample(s,float2(half2(coords.w,.125))).x;}$pure half4 sk_gaussian_colorfilter" +"(half4 inColor){half factor=1.-inColor.w;factor=exp((-factor*factor)*4.)-.018" +";return factor.xxxx;}$pure half4 sample_indexed_atlas(float2 textureCoords," +"int atlasIndex,sampler2D atlas0,sampler2D atlas1,sampler2D atlas2,sampler2D" +" atlas3){switch(atlasIndex){case 1:return sample(atlas1,textureCoords);case" +" 2:return sample(atlas2,textureCoords);case 3:return sample(atlas3,textureCoords" ");default:return sample(atlas0,textureCoords);}}$pure half3 $sample_indexed_atlas_lcd" "(float2 textureCoords,int atlasIndex,half2 offset,sampler2D atlas0,sampler2D" " atlas1,sampler2D atlas2,sampler2D atlas3){half3 distance=half3(1.);switch(" diff --git a/gfx/skia/skia/src/sksl/generated/sksl_graphite_frag_es2.minified.sksl b/gfx/skia/skia/src/sksl/generated/sksl_graphite_frag_es2.minified.sksl @@ -0,0 +1,2 @@ +static constexpr char SKSL_MINIFIED_sksl_graphite_frag_es2[] = +""; diff --git a/gfx/skia/skia/src/sksl/generated/sksl_graphite_frag_es2.unoptimized.sksl b/gfx/skia/skia/src/sksl/generated/sksl_graphite_frag_es2.unoptimized.sksl @@ -0,0 +1,2 @@ +static constexpr char SKSL_MINIFIED_sksl_graphite_frag_es2[] = +""; diff --git a/gfx/skia/skia/src/sksl/generated/sksl_graphite_vert.minified.sksl b/gfx/skia/skia/src/sksl/generated/sksl_graphite_vert.minified.sksl @@ -32,100 +32,99 @@ static constexpr char SKSL_MINIFIED_sksl_graphite_vert[] = " a,float b,float2x2 c,float2 d,float e,float4 f,float4 g,float2 h,float2 i," "float j){float2 k=f.xy;float2 l=f.zw;float2 m=g.xy;float2 n=g.zw;float o=-1." ";if($k(j)){o=n.x;n=m;}float p;if(o<0.)if(k==l&&m==n)p=1.;else p=$n(k,l,m,n," -"c);else if(k==l||l==m)p=1.;else p=$q(c*k,c*l,c*m,o);float q=i.x;float r=i.y" -";bool s=i.x==0.;float t;if(s){t=$v(.5);q=.5;}else t=$v(e*i.x);if(s){k=c*k;l" -"=c*l;m=c*m;n=c*n;h=c*h;}float2 u=$s(k==l?(l==m?n:m):l,k);float2 v=$s(n,n==m" -"?(m==l?k:l):m);float2 x=$s(k,h);if(u==0..xx){r=0.;if(o<0.){u=float2(1.,0.);" -"v=float2(-1.,0.);}else{o=-1.;u=x;v=x;if(x==0..xx){m=(n=k+q*float2(1.,0.));k" -"=(l=k-q*float2(1.,0.));x=(u=(v=float2(1.,0.)));}else m=(n=k+q*x);}}float y;" -"if(r>=0.)y=sign(r)+3.;else{float z=acos($t(x,u));float A=max(ceil(z*t),1.);" -"y=A+2.;y=min(y,b-2.);}float z=cross_length_2d(m-k,n-l);float A=abs(a)-y;if(" -"A<0.){v=u;if(h!=k)u=x;z=cross_length_2d(u,v);}float B=$t(u,v);float C=acos(" -"B);if(z<0.)C=-C;float D;float E=sign(a);if(A<0.){D=y-2.;p=1.;n=(m=(l=k));A+=" -"D+1.;if(A<0.)A=0.;else{bool G=abs(z)*inversesqrt(dot(u,u)*dot(v,v))<.01;if(" -"!G||dot(u,v)<0.)E=z<0.?min(E,0.):max(E,0.);}}else{float F=(b-y)-1.;D=max(ceil" -"(abs(C)*t),1.);D=min(D,F);p=min(p,(F-D)+1.);}float F=C/D;float G=(p+D)-1.;bool" -" H=A>=G;if(A>G)E=0.;if(abs(a)==2.&&r>0.)E*=$u(B,r);float2 I;float2 J;if(A!=" -"0.&&!H){float2 K;float2 L;float2 M=l-k;float2 N=n-k;if(o>=0.){M*=o;L=.5*N-M" -";K=(o-1.)*N;l*=o;}else{float2 O=m-l;L=O-M;K=fma(-3..xx,O,N);}float2 O=L*(p*" -"2.);float2 P=M*(p*p);float Q=0.;float R=min(p-1.,A);float S=-abs(F);float U" -"=(1.+A)*abs(F);for(float V=32.;V>=1.;V*=.5){float W=Q+V;if(W<=R){float2 X=fma" -"(W.xx,K,O);X=fma(W.xx,X,P);float Y=dot(normalize(X),u);float Z=fma(W,S,U);Z" -"=min(Z,3.14159274);if(Y>=cos(Z))Q=W;}}float V=Q/p;float W=A-Q;float X=acos(" -"clamp(u.x,-1.,1.));X=u.y>=0.?X:-X;float Y=fma(W,F,X);I=float2(cos(Y),sin(Y)" -");float2 Z=float2(-I.y,I.x);float aa=dot(Z,K);float ac=dot(Z,L);float ad=dot" -"(Z,M);float ae=max(ac*ac-aa*ad,0.);float af=sqrt(ae);if(ac>0.)af=-af;af-=ac" -";float ag=(-.5*af)*aa;float2 ah=abs(fma(af,af,ag))<abs(fma(aa,ad,ag))?float2" -"(af,aa):float2(ad,af);float ai=W!=0.&&ah.y!=0.?saturate(ah.x/ah.y):0.;float" -" aj=max(V,ai);float2 ak=$w(k,l,aj);float2 al=$w(l,m,aj);float2 am=$w(m,n,aj" -");float2 an=$w(ak,al,aj);float2 ao=$w(al,am,aj);float2 ap=$w(an,ao,aj);float" -" aq=$w(1.,o,aj);float ar=(o+1.)-aq;float as=$w(aq,ar,aj);if(aj!=ai)I=o>=0.?" -"$s(al*aq,ak*ar):$s(ao,an);J=o>=0.?an/as:ap;}else{I=A==0.?u:v;J=A==0.?k:n;}float2" -" K=float2(I.y,-I.x);J+=K*(q*E);if(s)return float4(J+d,inverse(c)*J);else return" -" float4(c*J+d,J);}float4 analytic_rrect_vertex_fn(uint a,float2 b,float2 c," -"float d,float e,float4 f,float4 g,float4 h,float4 i,float j,float3x3 k,out float4" -" l,out float4 m,out float4 n,out float4 o,out float2 p,out float2 q,out float2" -" r){float w=1.;uint x=(a+1)%4;bool y=i.z<=0.;bool z=false;float4 A;float4 B" -";float4 C=1..xxxx;bool D=false;if(f.x<-1.){D=f.y>0.;A=D?h.xxzz:h.xzzx;B=h.yyww" -";if(f.y<0.){n=-f-2.;o=g;p=float2(0.,1.);}else{n=g;o=n;p=f.zw;w=p.y<0.?.414213568" -":sign(p.y);}}else if(any(greaterThan(f,0..xxxx))){A=h.xzzx;B=h.yyww;n=f;o=g" -";p=float2(0.,-1.);}else{A=g;B=h;C=-f;n=0..xxxx;o=0..xxxx;p=float2(0.,1.);z=" -"true;}float2 E=float2(n[a],o[a]);if(a%2!=0)E=E.yx;float2 F=1..xx;if(all(greaterThan" -"(E,0..xx))){w=.414213568;F=E.yx;}float4 G=A-A.wxyz;float4 H=B-B.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=p.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);C=mix(C.yzwx,C,K);if(!N&&" -"w==0.){M*=float2(K[a],K[x]);L=(K-1.)*p.x;p.y=1.;w=1.;}}float4 N=inversesqrt" -"(J);G*=N;H*=N;float2 O=-float2(G[x],H[x]);float2 P=float2(G[a],H[a]);float2" -" Q;bool R=false;if(d<0.)if(i.w<0.||e*i.z!=0.)R=true;else{float S=i.w;float2" -" T=E+(y?-M:M);if(w==1.||any(lessThanEqual(T,S.xx)))Q=T-S;else Q=T*b-S*c;}else" -" Q=(E+M)*(b+w*b.yx);if(R)Q=i.xy;else{Q-=E;Q=(float2(A[a],B[a])+O*Q.x)+P*Q.y" -";}m=(H*(A-Q.x)-G*(B-Q.y))+L;float3x3 S=inverse(k);float3 T=k*float3(Q,1.);l" -"=float4(S[0].xy-S[0].z*Q,S[1].xy-S[1].z*Q);if(z){float4 U=-H*(S[0].x-S[0].z" -"*A)+G*(S[0].y-S[0].z*B);float4 V=-H*(S[1].x-S[1].z*A)+G*(S[1].y-S[1].z*B);m" -"*=inversesqrt(U*U+V*V);m+=(1.-C)*abs(T.z);bool W=C==1..xxxx&&dot(abs(G*G.yzwx" -"+H*H.yzwx),1..xxxx)<.00024;if(W){float2 X=m.xy+m.zw;q.y=1.+min(min(X.x,X.y)" -",abs(T.z));}else q.y=1.+abs(T.z);}if(d>0.&&T.z>0.){float2x2 U=float2x2(l);float2" -" V=float2(C[a],C[x])*c;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(z)m-=T.z;else q.y=-T.z;}else if(!z)q.y=0." -";q.x=float(e!=0.?1.:(y?-1.:0.));if(D)l=float4(float2x2(H.x,-H.y,-G.x,G.y)*float2x2" -"(l));r=Q;return float4(T.xy,T.z*j,T.z);}float4 per_edge_aa_quad_vertex_fn(uint" -" a,float2 b,float4 c,float4 d,float4 e,float f,float3x3 g,out float4 h,out float2" -" i){uint k=(a+1)%4;float4 l=d-d.wxyz;float4 m=e-e.wxyz;float4 n=1./max(abs(" -"l),max(abs(m),1..xxxx));l*=n;m*=n;float4 o=l*l+m*m;float4 p=sign(o);if(any(" -"equal(p,0..xxxx)))if(all(equal(p,0..xxxx))){l=float4(0.,1.,0.,-1.);m=float4" -"(-1.,0.,1.,0.);o=1..xxxx;}else{bool q=((p.x+p.y)+p.z)+p.w>2.5;float4 r=q?l." -"yzwx:m.yzwx;float4 s=q?m.yzwx:-l.yzwx;l=mix(r,l,p);m=mix(s,m,p);o=mix(o.yzwx" -",o,p);c=mix(c.yzwx,c,p);}float4 q=inversesqrt(o);l*=q;m*=q;float2 r=-float2" -"(l[k],m[k]);float2 s=float2(l[a],m[a]);float2 t=float2(d[a],e[a]);h=m*(d-t." -"x)-l*(e-t.y);float3x3 u=inverse(g);float3 v=g*float3(t,1.);float4 w=-m*(u[0" -"].x-u[0].z*d)+l*(u[0].y-u[0].z*e);float4 x=-m*(u[1].x-u[1].z*d)+l*(u[1].y-u" -"[1].z*e);h*=inversesqrt(w*w+x*x);h+=(1.5-c)*abs(v.z);if(any(notEqual(b,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(c[a],c[k])*b;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);h-=v.z;}i=t;return float4(v.xy,v.z*f,v.z);}float4 circular_arc_vertex_fn" -"(float3 a,float4 b,float3 c,float3 d,float3 e,float3 f,float4 g,float h,float" -" i,float3x3 j,out float4 k,out float3 l,out float3 m,out float3 n,out float" -" o,out float4 p,out float2 q){float2 r=b.xy;float2 s=r;float t=min(dot(a.xy" -",d.xy)+d.z,0.);a.xy-=d.xy*t;float u=length(a.xy);if(a.z>0.)s+=a.xy*b.z;else" -" s+=a.xy*b.w;float3 v=j*float3(s,1.);float3 w=j*float3(r,1.);float2 x=v.xy-" -"w.xy;if(x!=0..xx){x=normalize(x);v.xy+=a.z*x;if(a.z>0.)x*=u;else x*=u*c.y;}" -"k=float4(x,c.xy);if(c.z>0.){l=e;m=f;n=0..xxx;}else{l=e;m=float3(0.,0.,1.);n" -"=f;}o=h;p=g;q=s;return float4(v.xy,i,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);}}"; +"c);else p=$q(c*k,c*l,c*m,o);float q=i.x;float r=i.y;bool s=i.x==0.;float t;" +"if(s){t=$v(1.);q=.5;}else t=$v(e*i.x);if(s){k=c*k;l=c*l;m=c*m;n=c*n;h=c*h;}" +"float2 u=$s(k==l?(l==m?n:m):l,k);float2 v=$s(n,n==m?(m==l?k:l):m);if(u==0.." +"xx){u=float2(1.,0.);v=float2(-1.,0.);}float x;if(r>=0.)x=sign(r)+3.;else{float2" +" y=$s(k,h);float z=acos($t(y,u));float A=max(ceil(z*t),1.);x=A+2.;x=min(x,b" +"-2.);}float y=cross_length_2d(m-k,n-l);float z=abs(a)-x;if(z<0.){v=u;if(h!=" +"k)u=$s(k,h);y=cross_length_2d(u,v);}float A=$t(u,v);float B=acos(A);if(y<0." +")B=-B;float C;float D=sign(a);if(z<0.){C=x-2.;p=1.;n=(m=(l=k));z+=C+1.;if(z" +"<0.)z=0.;else{bool F=abs(y)*inversesqrt(dot(u,u)*dot(v,v))<.01;if(!F||dot(u" +",v)<0.)D=y<0.?min(D,0.):max(D,0.);}}else{float E=(b-x)-1.;C=max(ceil(abs(B)" +"*t),1.);C=min(C,E);p=min(p,(E-C)+1.);}float E=B/C;float F=(p+C)-1.;bool G=z" +">=F;if(z>F)D=0.;if(abs(a)==2.&&r>0.)D*=$u(A,r);float2 H;float2 I;if(z!=0.&&" +"!G){float2 J;float2 K;float2 L=l-k;float2 M=n-k;if(o>=0.){L*=o;K=.5*M-L;J=(" +"o-1.)*M;l*=o;}else{float2 N=m-l;K=N-L;J=fma(-3..xx,N,M);}float2 N=K*(p*2.);" +"float2 O=L*(p*p);float P=0.;float Q=min(p-1.,z);float R=-abs(E);float S=(1." +"+z)*abs(E);for(float U=32.;U>=1.;U*=.5){float V=P+U;if(V<=Q){float2 W=fma(V" +".xx,J,N);W=fma(V.xx,W,O);float X=dot(normalize(W),u);float Y=fma(V,R,S);Y=min" +"(Y,3.14159274);if(X>=cos(Y))P=V;}}float U=P/p;float V=z-P;float W=acos(clamp" +"(u.x,-1.,1.));W=u.y>=0.?W:-W;float X=fma(V,E,W);H=float2(cos(X),sin(X));float2" +" Y=float2(-H.y,H.x);float Z=dot(Y,J);float aa=dot(Y,K);float ac=dot(Y,L);float" +" ad=max(aa*aa-Z*ac,0.);float ae=sqrt(ad);if(aa>0.)ae=-ae;ae-=aa;float af=(-" +".5*ae)*Z;float2 ag=abs(fma(ae,ae,af))<abs(fma(Z,ac,af))?float2(ae,Z):float2" +"(ac,ae);float ah=V!=0.&&ag.y!=0.?saturate(ag.x/ag.y):0.;float ai=max(U,ah);" +"float2 aj=$w(k,l,ai);float2 ak=$w(l,m,ai);float2 al=$w(m,n,ai);float2 am=$w" +"(aj,ak,ai);float2 an=$w(ak,al,ai);float2 ao=$w(am,an,ai);float ap=$w(1.,o,ai" +");float aq=(o+1.)-ap;float ar=$w(ap,aq,ai);if(ai!=ah)H=o>=0.?$s(ak*ap,aj*aq" +"):$s(an,am);I=o>=0.?am/ar:ao;}else{H=z==0.?u:v;I=z==0.?k:n;}float2 J=float2" +"(H.y,-H.x);I+=J*(q*D);if(s)return float4(I+d,inverse(c)*I);else return float4" +"(c*I+d,I);}float4 analytic_rrect_vertex_fn(float2 a,float2 b,float c,float d" +",float4 e,float4 f,float4 g,float4 h,float i,float3x3 j,out float4 k,out float4" +" l,out float4 m,out float4 n,out float2 o,out float2 p,out float2 q){float w" +"=1.;bool x=h.z<=0.;bool y=false;float4 z;float4 A;float4 B=1..xxxx;bool C=false" +";if(e.x<-1.){C=e.y>0.;z=C?g.xxzz:g.xzzx;A=g.yyww;if(e.y<0.){m=-e-2.;n=f;o=float2" +"(0.,1.);}else{m=f;n=m;o=e.zw;w=o.y<0.?.414213568:sign(o.y);}}else if(any(greaterThan" +"(e,0..xxxx))){z=g.xzzx;A=g.yyww;m=e;n=f;o=float2(0.,-1.);}else{z=f;A=g;B=-e" +";m=0..xxxx;n=0..xxxx;o=float2(0.,1.);y=true;}uint D=uint(sk_VertexID)/9;float2" +" E=float2(m[D],n[D]);if(D%2!=0)E=E.yx;float2 F=1..xx;if(all(greaterThan(E,0." +".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=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 @@ -53,26 +53,23 @@ static constexpr char SKSL_MINIFIED_sksl_graphite_vert[] = " p2=p23.xy;float2 p3=p23.zw;float w=-1.;if($is_conic_curve(curveType)){w=p3" ".x;p3=p2;}float numParametricSegments;if(w<0.)if(p0==p1&&p2==p3)numParametricSegments" "=1.;else numParametricSegments=$wangs_formula_cubic(p0,p1,p2,p3,affineMatrix" -");else if(p0==p1||p1==p2)numParametricSegments=1.;else numParametricSegments" -"=$wangs_formula_conic(affineMatrix*p0,affineMatrix*p1,affineMatrix*p2,w);float" -" strokeRadius=strokeParams.x;float joinType=strokeParams.y;bool isHairline=" -"strokeParams.x==0.;float numRadialSegmentsPerRadian;if(isHairline){numRadialSegmentsPerRadian" -"=$num_radial_segments_per_radian(.5);strokeRadius=.5;}else numRadialSegmentsPerRadian" -"=$num_radial_segments_per_radian(maxScale*strokeParams.x);if(isHairline){p0" -"=affineMatrix*p0;p1=affineMatrix*p1;p2=affineMatrix*p2;p3=affineMatrix*p3;lastControlPoint" -"=affineMatrix*lastControlPoint;}float2 tan0=$robust_normalize_diff(p0==p1?(" -"p1==p2?p3:p2):p1,p0);float2 tan1=$robust_normalize_diff(p3,p3==p2?(p2==p1?p0" -":p1):p2);float2 prevTan=$robust_normalize_diff(p0,lastControlPoint);if(tan0" -"==0..xx){joinType=0.;if(w<0.){tan0=float2(1.,0.);tan1=float2(-1.,0.);}else{" -"w=-1.;tan0=prevTan;tan1=prevTan;if(prevTan==0..xx){p2=(p3=p0+strokeRadius*float2" -"(1.,0.));p0=(p1=p0-strokeRadius*float2(1.,0.));prevTan=(tan0=(tan1=float2(1." -",0.)));}else p2=(p3=p0+strokeRadius*prevTan);}}float numEdgesInJoin;if(joinType" -">=0.)numEdgesInJoin=sign(joinType)+3.;else{float joinRads=acos($cosine_between_unit_vectors" -"(prevTan,tan0));float numRadialSegmentsInJoin=max(ceil(joinRads*numRadialSegmentsPerRadian" -"),1.);numEdgesInJoin=numRadialSegmentsInJoin+2.;numEdgesInJoin=min(numEdgesInJoin" -",maxEdges-2.);}float turn=cross_length_2d(p2-p0,p3-p1);float combinedEdgeID" -"=abs(edgeID)-numEdgesInJoin;if(combinedEdgeID<0.){tan1=tan0;if(lastControlPoint" -"!=p0)tan0=prevTan;turn=cross_length_2d(tan0,tan1);}float cosTheta=$cosine_between_unit_vectors" +");else numParametricSegments=$wangs_formula_conic(affineMatrix*p0,affineMatrix" +"*p1,affineMatrix*p2,w);float strokeRadius=strokeParams.x;float joinType=strokeParams" +".y;bool isHairline=strokeParams.x==0.;float numRadialSegmentsPerRadian;if(isHairline" +"){numRadialSegmentsPerRadian=$num_radial_segments_per_radian(1.);strokeRadius" +"=.5;}else numRadialSegmentsPerRadian=$num_radial_segments_per_radian(maxScale" +"*strokeParams.x);if(isHairline){p0=affineMatrix*p0;p1=affineMatrix*p1;p2=affineMatrix" +"*p2;p3=affineMatrix*p3;lastControlPoint=affineMatrix*lastControlPoint;}float2" +" tan0=$robust_normalize_diff(p0==p1?(p1==p2?p3:p2):p1,p0);float2 tan1=$robust_normalize_diff" +"(p3,p3==p2?(p2==p1?p0:p1):p2);if(tan0==0..xx){tan0=float2(1.,0.);tan1=float2" +"(-1.,0.);}float numEdgesInJoin;if(joinType>=0.)numEdgesInJoin=sign(joinType" +")+3.;else{float2 prevTan=$robust_normalize_diff(p0,lastControlPoint);float joinRads" +"=acos($cosine_between_unit_vectors(prevTan,tan0));float numRadialSegmentsInJoin" +"=max(ceil(joinRads*numRadialSegmentsPerRadian),1.);numEdgesInJoin=numRadialSegmentsInJoin" +"+2.;numEdgesInJoin=min(numEdgesInJoin,maxEdges-2.);}float turn=cross_length_2d" +"(p2-p0,p3-p1);float combinedEdgeID=abs(edgeID)-numEdgesInJoin;if(combinedEdgeID" +"<0.){tan1=tan0;if(lastControlPoint!=p0)tan0=$robust_normalize_diff(p0,lastControlPoint" +");turn=cross_length_2d(tan0,tan1);}float cosTheta=$cosine_between_unit_vectors" "(tan0,tan1);float rotation=acos(cosTheta);if(turn<0.)rotation=-rotation;float" " numRadialSegments;float strokeOutset=sign(edgeID);if(combinedEdgeID<0.){numRadialSegments" "=numEdgesInJoin-2.;numParametricSegments=1.;p3=(p2=(p1=p0));combinedEdgeID+=" @@ -118,99 +115,100 @@ static constexpr char SKSL_MINIFIED_sksl_graphite_vert[] = ":p3;}float2 ortho=float2(tangent.y,-tangent.x);strokeCoord+=ortho*(strokeRadius" "*strokeOutset);if(isHairline)return float4(strokeCoord+translate,inverse(affineMatrix" ")*strokeCoord);else return float4(affineMatrix*strokeCoord+translate,strokeCoord" -");}float4 analytic_rrect_vertex_fn(uint cornerID,float2 position,float2 normal" -",float normalScale,float centerWeight,float4 xRadiiOrFlags,float4 radiiOrQuadXs" -",float4 ltrbOrQuadYs,float4 center,float depth,float3x3 localToDevice,out float4" -" jacobian,out float4 edgeDistances,out float4 xRadii,out float4 yRadii,out float2" -" strokeParams,out float2 perPixelControl,out float2 stepLocalCoords){const float" -" kMiterScale=1.;const float kBevelScale=0.;const float kRoundScale=.414213568" -";const float kEpsilon=.00024;float joinScale=kMiterScale;uint nextID=(cornerID" -"+1)%4;bool bidirectionalCoverage=center.z<=0.;bool deviceSpaceDistances=false" -";float4 xs;float4 ys;float4 edgeAA=1..xxxx;bool strokedLine=false;if(xRadiiOrFlags" -".x<-1.){strokedLine=xRadiiOrFlags.y>0.;xs=strokedLine?ltrbOrQuadYs.xxzz:ltrbOrQuadYs" -".xzzx;ys=ltrbOrQuadYs.yyww;if(xRadiiOrFlags.y<0.){xRadii=-xRadiiOrFlags-2.;" -"yRadii=radiiOrQuadXs;strokeParams=float2(0.,1.);}else{xRadii=radiiOrQuadXs;" -"yRadii=xRadii;strokeParams=xRadiiOrFlags.zw;joinScale=strokeParams.y<0.?kRoundScale" -":sign(strokeParams.y);}}else if(any(greaterThan(xRadiiOrFlags,0..xxxx))){xs" -"=ltrbOrQuadYs.xzzx;ys=ltrbOrQuadYs.yyww;xRadii=xRadiiOrFlags;yRadii=radiiOrQuadXs" -";strokeParams=float2(0.,-1.);}else{xs=radiiOrQuadXs;ys=ltrbOrQuadYs;edgeAA=" -"-xRadiiOrFlags;xRadii=0..xxxx;yRadii=0..xxxx;strokeParams=float2(0.,1.);deviceSpaceDistances" -"=true;}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 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=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(edgeX,dx,edgeMask);dy=mix(edgeY,dy,edgeMask);edgeSquaredLen=mix(edgeSquaredLen" -".yzwx,edgeSquaredLen,edgeMask);edgeAA=mix(edgeAA.yzwx,edgeAA,edgeMask);if(!" -"triangle&&joinScale==kBevelScale){strokeRadius*=float2(edgeMask[cornerID],edgeMask" -"[nextID]);edgeBias=(edgeMask-1.)*strokeParams.x;strokeParams.y=1.;joinScale" -"=kMiterScale;}}float4 inverseEdgeLen=inversesqrt(edgeSquaredLen);dx*=inverseEdgeLen" -";dy*=inverseEdgeLen;float2 xAxis=-float2(dx[nextID],dy[nextID]);float2 yAxis" -"=float2(dx[cornerID],dy[cornerID]);float2 localPos;bool snapToCenter=false;" -"if(normalScale<0.)if(center.w<0.||centerWeight*center.z!=0.)snapToCenter=true" -";else{float localAARadius=center.w;float2 insetRadii=cornerRadii+(bidirectionalCoverage" -"?-strokeRadius:strokeRadius);if(joinScale==kMiterScale||any(lessThanEqual(insetRadii" -",localAARadius.xx)))localPos=insetRadii-localAARadius;else localPos=insetRadii" -"*position-localAARadius*normal;}else localPos=(cornerRadii+strokeRadius)*(position" -"+joinScale*position.yx);if(snapToCenter)localPos=center.xy;else{localPos-=cornerRadii" -";localPos=(float2(xs[cornerID],ys[cornerID])+xAxis*localPos.x)+yAxis*localPos" -".y;}edgeDistances=(dy*(xs-localPos.x)-dx*(ys-localPos.y))+edgeBias;float3x3" -" deviceToLocal=inverse(localToDevice);float3 devPos=localToDevice*float3(localPos" -",1.);jacobian=float4(deviceToLocal[0].xy-deviceToLocal[0].z*localPos,deviceToLocal" -"[1].xy-deviceToLocal[1].z*localPos);if(deviceSpaceDistances){float4 gx=-dy*" -"(deviceToLocal[0].x-deviceToLocal[0].z*xs)+dx*(deviceToLocal[0].y-deviceToLocal" -"[0].z*ys);float4 gy=-dy*(deviceToLocal[1].x-deviceToLocal[1].z*xs)+dx*(deviceToLocal" -"[1].y-deviceToLocal[1].z*ys);edgeDistances*=inversesqrt(gx*gx+gy*gy);edgeDistances" -"+=(1.-edgeAA)*abs(devPos.z);bool subpixelCoverage=edgeAA==1..xxxx&&dot(abs(" -"dx*dx.yzwx+dy*dy.yzwx),1..xxxx)<kEpsilon;if(subpixelCoverage){float2 dim=edgeDistances" -".xy+edgeDistances.zw;perPixelControl.y=1.+min(min(dim.x,dim.y),abs(devPos.z" -"));}else perPixelControl.y=1.+abs(devPos.z);}if(normalScale>0.&&devPos.z>0." -"){float2x2 J=float2x2(jacobian);float2 edgeAANormal=float2(edgeAA[cornerID]" -",edgeAA[nextID])*normal;float2 nx=((cornerAspectRatio.x*edgeAANormal.x)*perp" -"(-yAxis))*J;float2 ny=((cornerAspectRatio.y*edgeAANormal.y)*perp(xAxis))*J;" -"bool isMidVertex=all(notEqual(edgeAANormal,0..xx));if(joinScale==kMiterScale" -"&&isMidVertex){nx=normalize(nx);ny=normalize(ny);if(dot(nx,ny)<-.8){float s" -"=sign(cross_length_2d(nx,ny));nx=s*perp(nx);ny=-s*perp(ny);}}devPos.xy+=devPos" -".z*normalize(nx+ny);if(deviceSpaceDistances)edgeDistances-=devPos.z;else perPixelControl" -".y=-devPos.z;}else if(!deviceSpaceDistances)perPixelControl.y=0.;perPixelControl" -".x=float(centerWeight!=0.?1.:(bidirectionalCoverage?-1.:0.));if(strokedLine" -")jacobian=float4(float2x2(dy.x,-dy.y,-dx.x,dx.y)*float2x2(jacobian));stepLocalCoords" -"=localPos;return float4(devPos.xy,devPos.z*depth,devPos.z);}float4 per_edge_aa_quad_vertex_fn" -"(uint cornerID,float2 normal,float4 edgeAA,float4 xs,float4 ys,float depth," -"float3x3 localToDevice,out float4 edgeDistances,out float2 stepLocalCoords)" -"{uint nextID=(cornerID+1)%4;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);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(edgeX,dx,edgeMask);dy=mix(edgeY,dy," -"edgeMask);edgeSquaredLen=mix(edgeSquaredLen.yzwx,edgeSquaredLen,edgeMask);edgeAA" -"=mix(edgeAA.yzwx,edgeAA,edgeMask);}float4 inverseEdgeLen=inversesqrt(edgeSquaredLen" -");dx*=inverseEdgeLen;dy*=inverseEdgeLen;float2 xAxis=-float2(dx[nextID],dy[" -"nextID]);float2 yAxis=float2(dx[cornerID],dy[cornerID]);float2 localPos=float2" -"(xs[cornerID],ys[cornerID]);edgeDistances=dy*(xs-localPos.x)-dx*(ys-localPos" -".y);float3x3 deviceToLocal=inverse(localToDevice);float3 devPos=localToDevice" -"*float3(localPos,1.);float4 gx=-dy*(deviceToLocal[0].x-deviceToLocal[0].z*xs" -")+dx*(deviceToLocal[0].y-deviceToLocal[0].z*ys);float4 gy=-dy*(deviceToLocal" -"[1].x-deviceToLocal[1].z*xs)+dx*(deviceToLocal[1].y-deviceToLocal[1].z*ys);" -"edgeDistances*=inversesqrt(gx*gx+gy*gy);edgeDistances+=(1.5-edgeAA)*abs(devPos" -".z);if(any(notEqual(normal,0..xx))&&devPos.z>0.){float2x2 J=float2x2(deviceToLocal" +");}float4 analytic_rrect_vertex_fn(float2 position,float2 normal,float normalScale" +",float centerWeight,float4 xRadiiOrFlags,float4 radiiOrQuadXs,float4 ltrbOrQuadYs" +",float4 center,float depth,float3x3 localToDevice,out float4 jacobian,out float4" +" edgeDistances,out float4 xRadii,out float4 yRadii,out float2 strokeParams," +"out float2 perPixelControl,out float2 stepLocalCoords){const uint kCornerVertexCount" +"=9;const float kMiterScale=1.;const float kBevelScale=0.;const float kRoundScale" +"=.414213568;const float kEpsilon=.00024;float joinScale=kMiterScale;bool bidirectionalCoverage" +"=center.z<=0.;bool deviceSpaceDistances=false;float4 xs;float4 ys;float4 edgeAA" +"=1..xxxx;bool strokedLine=false;if(xRadiiOrFlags.x<-1.){strokedLine=xRadiiOrFlags" +".y>0.;xs=strokedLine?ltrbOrQuadYs.xxzz:ltrbOrQuadYs.xzzx;ys=ltrbOrQuadYs.yyww" +";if(xRadiiOrFlags.y<0.){xRadii=-xRadiiOrFlags-2.;yRadii=radiiOrQuadXs;strokeParams" +"=float2(0.,1.);}else{xRadii=radiiOrQuadXs;yRadii=xRadii;strokeParams=xRadiiOrFlags" +".zw;joinScale=strokeParams.y<0.?kRoundScale:sign(strokeParams.y);}}else if(" +"any(greaterThan(xRadiiOrFlags,0..xxxx))){xs=ltrbOrQuadYs.xzzx;ys=ltrbOrQuadYs" +".yyww;xRadii=xRadiiOrFlags;yRadii=radiiOrQuadXs;strokeParams=float2(0.,-1.)" +";}else{xs=radiiOrQuadXs;ys=ltrbOrQuadYs;edgeAA=-xRadiiOrFlags;xRadii=0..xxxx" +";yRadii=0..xxxx;strokeParams=float2(0.,1.);deviceSpaceDistances=true;}uint cornerID" +"=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 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" +"=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(edgeX,dx,edgeMask);dy=mix(edgeY,dy,edgeMask);edgeSquaredLen=" +"mix(edgeSquaredLen.yzwx,edgeSquaredLen,edgeMask);edgeAA=mix(edgeAA.yzwx,edgeAA" +",edgeMask);if(!triangle&&joinScale==kBevelScale){strokeRadius*=float2(edgeMask" +"[cornerID],edgeMask.yzwx[cornerID]);edgeBias=(edgeMask-1.)*strokeParams.x;strokeParams" +".y=1.;joinScale=kMiterScale;}}float4 inverseEdgeLen=inversesqrt(edgeSquaredLen" +");dx*=inverseEdgeLen;dy*=inverseEdgeLen;float2 xAxis=-float2(dx.yzwx[cornerID" +"],dy.yzwx[cornerID]);float2 yAxis=float2(dx[cornerID],dy[cornerID]);float2 localPos" +";bool snapToCenter=false;if(normalScale<0.)if(center.w<0.||centerWeight*center" +".z!=0.)snapToCenter=true;else{float localAARadius=center.w;float2 insetRadii" +"=cornerRadii+(bidirectionalCoverage?-strokeRadius:strokeRadius);if(joinScale" +"==kMiterScale||any(lessThanEqual(insetRadii,localAARadius.xx)))localPos=insetRadii" +"-localAARadius;else localPos=insetRadii*position-localAARadius*normal;}else" +" localPos=(cornerRadii+strokeRadius)*(position+joinScale*position.yx);if(snapToCenter" +")localPos=center.xy;else{localPos-=cornerRadii;localPos=(float2(xs[cornerID" +"],ys[cornerID])+xAxis*localPos.x)+yAxis*localPos.y;}edgeDistances=(dy*(xs-localPos" +".x)-dx*(ys-localPos.y))+edgeBias;float3x3 deviceToLocal=inverse(localToDevice" +");float3 devPos=localToDevice*float3(localPos,1.);jacobian=float4(deviceToLocal" "[0].xy-deviceToLocal[0].z*localPos,deviceToLocal[1].xy-deviceToLocal[1].z*localPos" -");float2 edgeAANormal=float2(edgeAA[cornerID],edgeAA[nextID])*normal;float2" -" nx=(edgeAANormal.x*perp(-yAxis))*J;float2 ny=(edgeAANormal.y*perp(xAxis))*" -"J;bool isMidVertex=all(notEqual(edgeAANormal,0..xx));if(isMidVertex){nx=normalize" -"(nx);ny=normalize(ny);if(dot(nx,ny)<-.8){float s=sign(cross_length_2d(nx,ny" -"));nx=s*perp(nx);ny=-s*perp(ny);}}devPos.xy+=devPos.z*normalize(nx+ny);edgeDistances" -"-=devPos.z;}stepLocalCoords=localPos;return float4(devPos.xy,devPos.z*depth" -",devPos.z);}float4 circular_arc_vertex_fn(float3 position,float4 centerScales" -",float3 radiiAndFlags,float3 geoClipPlane,float3 fragClipPlane0,float3 fragClipPlane1" -",float4 inRoundCapPos,float inRoundCapRadius,float depth,float3x3 localToDevice" +");if(deviceSpaceDistances){float4 gx=-dy*(deviceToLocal[0].x-deviceToLocal[" +"0].z*xs)+dx*(deviceToLocal[0].y-deviceToLocal[0].z*ys);float4 gy=-dy*(deviceToLocal" +"[1].x-deviceToLocal[1].z*xs)+dx*(deviceToLocal[1].y-deviceToLocal[1].z*ys);" +"edgeDistances*=inversesqrt(gx*gx+gy*gy);edgeDistances+=(1.-edgeAA)*abs(devPos" +".z);bool subpixelCoverage=edgeAA==1..xxxx&&dot(abs(dx*dx.yzwx+dy*dy.yzwx),1." +".xxxx)<kEpsilon;if(subpixelCoverage){float2 dim=edgeDistances.xy+edgeDistances" +".zw;perPixelControl.y=1.+min(min(dim.x,dim.y),abs(devPos.z));}else perPixelControl" +".y=1.+abs(devPos.z);}if(normalScale>0.&&devPos.z>0.){float2x2 J=float2x2(jacobian" +");float2 edgeAANormal=float2(edgeAA[cornerID],edgeAA.yzwx[cornerID])*normal" +";float2 nx=((cornerAspectRatio.x*edgeAANormal.x)*perp(-yAxis))*J;float2 ny=" +"((cornerAspectRatio.y*edgeAANormal.y)*perp(xAxis))*J;bool isMidVertex=all(notEqual" +"(edgeAANormal,0..xx));if(joinScale==kMiterScale&&isMidVertex){nx=normalize(" +"nx);ny=normalize(ny);if(dot(nx,ny)<-.8){float s=sign(cross_length_2d(nx,ny)" +");nx=s*perp(nx);ny=-s*perp(ny);}}devPos.xy+=devPos.z*normalize(nx+ny);if(deviceSpaceDistances" +")edgeDistances-=devPos.z;else perPixelControl.y=-devPos.z;}else if(!deviceSpaceDistances" +")perPixelControl.y=0.;perPixelControl.x=float(centerWeight!=0.?1.:(bidirectionalCoverage" +"?-1.:0.));if(strokedLine)jacobian=float4(float2x2(dy.x,-dy.y,-dx.x,dx.y)*float2x2" +"(jacobian));stepLocalCoords=localPos;return float4(devPos.xy,devPos.z*depth" +",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 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" +"(edgeX,dx,edgeMask);dy=mix(edgeY,dy,edgeMask);edgeSquaredLen=mix(edgeSquaredLen" +".yzwx,edgeSquaredLen,edgeMask);edgeAA=mix(edgeAA.yzwx,edgeAA,edgeMask);}float4" +" inverseEdgeLen=inversesqrt(edgeSquaredLen);dx*=inverseEdgeLen;dy*=inverseEdgeLen" +";uint cornerID=uint(sk_VertexID)/kCornerVertexCount;float2 xAxis=-float2(dx" +".yzwx[cornerID],dy.yzwx[cornerID]);float2 yAxis=float2(dx[cornerID],dy[cornerID" +"]);float2 localPos=float2(xs[cornerID],ys[cornerID]);edgeDistances=dy*(xs-localPos" +".x)-dx*(ys-localPos.y);float3x3 deviceToLocal=inverse(localToDevice);float3" +" devPos=localToDevice*float3(localPos,1.);float4 gx=-dy*(deviceToLocal[0].x" +"-deviceToLocal[0].z*xs)+dx*(deviceToLocal[0].y-deviceToLocal[0].z*ys);float4" +" gy=-dy*(deviceToLocal[1].x-deviceToLocal[1].z*xs)+dx*(deviceToLocal[1].y-deviceToLocal" +"[1].z*ys);edgeDistances*=inversesqrt(gx*gx+gy*gy);edgeDistances+=(1.5-edgeAA" +")*abs(devPos.z);if(any(notEqual(normal,0..xx))&&devPos.z>0.){float2x2 J=float2x2" +"(deviceToLocal[0].xy-deviceToLocal[0].z*localPos,deviceToLocal[1].xy-deviceToLocal" +"[1].z*localPos);float2 edgeAANormal=float2(edgeAA[cornerID],edgeAA.yzwx[cornerID" +"])*normal;float2 nx=(edgeAANormal.x*perp(-yAxis))*J;float2 ny=(edgeAANormal" +".y*perp(xAxis))*J;bool isMidVertex=all(notEqual(edgeAANormal,0..xx));if(isMidVertex" +"){nx=normalize(nx);ny=normalize(ny);if(dot(nx,ny)<-.8){float s=sign(cross_length_2d" +"(nx,ny));nx=s*perp(nx);ny=-s*perp(ny);}}devPos.xy+=devPos.z*normalize(nx+ny" +");edgeDistances-=devPos.z;}stepLocalCoords=localPos;return float4(devPos.xy" +",devPos.z*depth,devPos.z);}float4 circular_arc_vertex_fn(float3 position,float4" +" centerScales,float3 radiiAndFlags,float3 geoClipPlane,float3 fragClipPlane0" +",float3 fragClipPlane1,float4 inRoundCapPos,float depth,float3x3 localToDevice" ",out float4 circleEdge,out float3 clipPlane,out float3 isectPlane,out float3" " unionPlane,out float roundCapRadius,out float4 roundCapPos,out float2 stepLocalCoords" "){float2 localCenter=centerScales.xy;float2 localPos=localCenter;float dist" @@ -223,28 +221,29 @@ static constexpr char SKSL_MINIFIED_sksl_graphite_vert[] = "else offset*=offsetScale*radiiAndFlags.y;}circleEdge=float4(offset,radiiAndFlags" ".xy);if(radiiAndFlags.z>0.){clipPlane=fragClipPlane0;isectPlane=fragClipPlane1" ";unionPlane=0..xxx;}else{clipPlane=fragClipPlane0;isectPlane=float3(0.,0.,1." -");unionPlane=fragClipPlane1;}roundCapRadius=inRoundCapRadius;roundCapPos=inRoundCapPos" -";stepLocalCoords=localPos;return float4(devPos.xy,depth,1.);}float4 text_vertex_fn" -"(float2 baseCoords,float4x4 subRunDeviceMatrix,float4x4 deviceToLocal,float2" -" atlasSizeInv,float2 size,float2 uvPos,float2 xyPos,float strikeToSourceScale" -",float depth,out float2 textureCoords,out float2 unormTexCoords,out float2 stepLocalCoords" -"){baseCoords*=size;float2 subRunCoords=strikeToSourceScale*baseCoords+xyPos" -";float4 position=subRunDeviceMatrix*float4(subRunCoords,0.,1.);stepLocalCoords" -"=(deviceToLocal*position).xy;unormTexCoords=baseCoords+uvPos;textureCoords=" -"unormTexCoords*atlasSizeInv;return float4(position.xy,depth*position.w,position" -".w);}float4 coverage_mask_vertex_fn(float2 quadCoords,float3x3 maskToDeviceRemainder" -",float4 drawBounds,float4 maskBoundsIn,float2 deviceOrigin,float depth,float3x3" -" deviceToLocal,out float4 maskBounds,out float2 textureCoords,out half invert" -",out float2 stepLocalCoords){textureCoords=mix(drawBounds.xy,drawBounds.zw," -"quadCoords);float3 drawCoords=maskToDeviceRemainder*float3(textureCoords+deviceOrigin" -",1.);float3 localCoords=deviceToLocal*drawCoords;stepLocalCoords=localCoords" -".xy/localCoords.z;if(all(lessThanEqual(maskBoundsIn.xy,maskBoundsIn.zw))){maskBounds" -"=maskBoundsIn;invert=0.;}else{maskBounds=maskBoundsIn.zwxy;invert=1.;}return" -" float4(drawCoords.xy,depth*drawCoords.z,drawCoords.z);}float4 cover_bounds_vertex_fn" -"(float2 corner,float4 bounds,float depth,float3x3 matrix,out float2 stepLocalCoords" -"){if(all(lessThanEqual(bounds.xy,bounds.zw))){corner=mix(bounds.xy,bounds.zw" -",corner);float3 devCorner=matrix*float3(corner,1.);stepLocalCoords=corner;return" -" float4(devCorner.xy,depth*devCorner.z,devCorner.z);}else{corner=mix(bounds" -".zw,bounds.xy,corner);float3 localCoords=inverse(matrix)*float3(corner,1.);" -"float invW=1./localCoords.z;stepLocalCoords=localCoords.xy*invW;return float4" -"(corner*invW,depth*invW,invW);}}"; +");unionPlane=fragClipPlane1;}if(abs(radiiAndFlags.z)>1.)roundCapRadius=(1.-" +"radiiAndFlags.y)*.5;else roundCapRadius=0.;roundCapPos=inRoundCapPos;stepLocalCoords" +"=localPos;return float4(devPos.xy,depth,1.);}float4 text_vertex_fn(float2 baseCoords" +",float4x4 subRunDeviceMatrix,float4x4 deviceToLocal,float2 atlasSizeInv,float2" +" size,float2 uvPos,float2 xyPos,float strikeToSourceScale,float depth,out float2" +" textureCoords,out float2 unormTexCoords,out float2 stepLocalCoords){baseCoords" +"*=size;float2 subRunCoords=strikeToSourceScale*baseCoords+xyPos;float4 position" +"=subRunDeviceMatrix*float4(subRunCoords,0.,1.);stepLocalCoords=(deviceToLocal" +"*position).xy;unormTexCoords=baseCoords+uvPos;textureCoords=unormTexCoords*" +"atlasSizeInv;return float4(position.xy,depth*position.w,position.w);}float4" +" coverage_mask_vertex_fn(float2 quadCoords,float3x3 maskToDeviceRemainder,float4" +" drawBounds,float4 maskBoundsIn,float2 deviceOrigin,float depth,float3x3 deviceToLocal" +",out float4 maskBounds,out float2 textureCoords,out half invert,out float2 stepLocalCoords" +"){textureCoords=mix(drawBounds.xy,drawBounds.zw,quadCoords);float3 drawCoords" +"=maskToDeviceRemainder*float3(textureCoords+deviceOrigin,1.);float3 localCoords" +"=deviceToLocal*drawCoords;stepLocalCoords=localCoords.xy/localCoords.z;if(all" +"(lessThanEqual(maskBoundsIn.xy,maskBoundsIn.zw))){maskBounds=maskBoundsIn;invert" +"=0.;}else{maskBounds=maskBoundsIn.zwxy;invert=1.;}return float4(drawCoords." +"xy,depth*drawCoords.z,drawCoords.z);}float4 cover_bounds_vertex_fn(float2 corner" +",float4 bounds,float depth,float3x3 matrix,out float2 stepLocalCoords){if(all" +"(lessThanEqual(bounds.xy,bounds.zw))){corner=mix(bounds.xy,bounds.zw,corner" +");float3 devCorner=matrix*float3(corner,1.);stepLocalCoords=corner;return float4" +"(devCorner.xy,depth*devCorner.z,devCorner.z);}else{corner=mix(bounds.zw,bounds" +".xy,corner);float3 localCoords=inverse(matrix)*float3(corner,1.);float invW" +"=1./localCoords.z;stepLocalCoords=localCoords.xy*invW;return float4(corner*" +"invW,depth*invW,invW);}}"; diff --git a/gfx/skia/skia/src/sksl/generated/sksl_graphite_vert_es2.minified.sksl b/gfx/skia/skia/src/sksl/generated/sksl_graphite_vert_es2.minified.sksl @@ -0,0 +1,2 @@ +static constexpr char SKSL_MINIFIED_sksl_graphite_vert_es2[] = +""; diff --git a/gfx/skia/skia/src/sksl/generated/sksl_graphite_vert_es2.unoptimized.sksl b/gfx/skia/skia/src/sksl/generated/sksl_graphite_vert_es2.unoptimized.sksl @@ -0,0 +1,2 @@ +static constexpr char SKSL_MINIFIED_sksl_graphite_vert_es2[] = +""; diff --git a/gfx/skia/skia/src/sksl/generated/sksl_rt_shader.minified.sksl b/gfx/skia/skia/src/sksl/generated/sksl_rt_shader.minified.sksl @@ -29,18 +29,16 @@ static constexpr char SKSL_MINIFIED_sksl_rt_shader[] = " 0.;else{half h=pow(g,d);return g<c+.016?(h*(g-c))*62.4999962:h;}}$pure half4" " $f(half3 a,half b,half c,half d,half3 e,half3 f,half3 g,half h,half i){if(" "d>0.)a*=$e(f,g,h,i);if(c==0.){half j=dot(e,g);a=saturate(j*a);return half4(" -"a,1.);}else if(c==1.){half3 j=normalize(g+half3(0.,0.,1.));half k=pow(dot(e" -",j),b);a=saturate(k*a);return half4(a,max(max(a.x,a.y),a.z));}else{half j=(" -"2.*dot(e,g)-g.z)*g.z;half k=pow(j,b);a=saturate(k*a);return half4(a,max(max" -"(a.x,a.y),a.z));}}half4 sk_lighting(shader a,float2 b,half c,half d,half e," -"half f,half3 g,half h,half3 i,half j,half3 k){half4 l=a.eval(b);half3 m=$d(" -"f,g,i,half3(half2(b),c*l.w));return $f(k,d,e,f,l.xyz,i,m,j,h);}half4 sk_arithmetic_blend" -"(half4 a,half4 b,half4 c,half d){half4 e=saturate((((c.x*a)*b+c.y*a)+c.z*b)" -"+c.w);e.xyz=min(e.xyz,max(e.w,d));return e;}half4 sk_sparse_morphology(shader" -" a,float2 b,half2 c,half d){half4 e=max(d*a.eval(b+float2(c)),d*a.eval(b-float2" -"(c)));return d*e;}half4 sk_linear_morphology(shader a,float2 b,half2 c,half" -" d,int e){half4 g=d*a.eval(b);half2 h=c;for(int j=1;j<=14;++j){if(j>e)break" -";g=max(g,max(d*a.eval(b+float2(h)),d*a.eval(b-float2(h))));h+=c;}return d*g" -";}half4 sk_overdraw(half a,half4 b,half4 c,half4 d,half4 e,half4 f,half4 g)" -"{return a<.00196078443?b:(a<.005882353?c:(a<.009803922?d:(a<.01372549?e:(a<" -".01764706?f:g))));}"; +"a,1.);}else{half3 j=normalize(g+half3(0.,0.,1.));half k=pow(dot(e,j),b);a=saturate" +"(k*a);return half4(a,max(max(a.x,a.y),a.z));}}half4 sk_lighting(shader a,float2" +" b,half c,half d,half e,half f,half3 g,half h,half3 i,half j,half3 k){half4" +" l=a.eval(b);half3 m=$d(f,g,i,half3(half2(b),c*l.w));return $f(k,d,e,f,l.xyz" +",i,m,j,h);}half4 sk_arithmetic_blend(half4 a,half4 b,half4 c,half d){half4 e" +"=saturate((((c.x*a)*b+c.y*a)+c.z*b)+c.w);e.xyz=min(e.xyz,max(e.w,d));return" +" e;}half4 sk_sparse_morphology(shader a,float2 b,half2 c,half d){half4 e=max" +"(d*a.eval(b+float2(c)),d*a.eval(b-float2(c)));return d*e;}half4 sk_linear_morphology" +"(shader a,float2 b,half2 c,half d,int e){half4 g=d*a.eval(b);half2 h=c;for(" +"int j=1;j<=14;++j){if(j>e)break;g=max(g,max(d*a.eval(b+float2(h)),d*a.eval(" +"b-float2(h))));h+=c;}return d*g;}half4 sk_overdraw(half a,half4 b,half4 c,half4" +" d,half4 e,half4 f,half4 g){return a<.00196078443?b:(a<.005882353?c:(a<.009803922" +"?d:(a<.01372549?e:(a<.01764706?f:g))));}"; diff --git a/gfx/skia/skia/src/sksl/generated/sksl_rt_shader.unoptimized.sksl b/gfx/skia/skia/src/sksl/generated/sksl_rt_shader.unoptimized.sksl @@ -48,14 +48,11 @@ static constexpr char SKSL_MINIFIED_sksl_rt_shader[] = " lightDir,half3 surfaceToLight,half cosCutoffAngle,half spotFalloff){if(lightType" ">0.)color*=$spotlight_scale(lightDir,surfaceToLight,cosCutoffAngle,spotFalloff" ");if(materialType==0.){half coeff=dot(normal,surfaceToLight);color=saturate" -"(coeff*color);return half4(color,1.);}else if(materialType==1.){half3 halfDir" -"=normalize(surfaceToLight+half3(0.,0.,1.));half coeff=pow(dot(normal,halfDir" -"),shininess);color=saturate(coeff*color);return half4(color,max(max(color.x" -",color.y),color.z));}else{half hilite=(2.*dot(normal,surfaceToLight)-surfaceToLight" -".z)*surfaceToLight.z;half coeff=pow(hilite,shininess);color=saturate(coeff*" -"color);return half4(color,max(max(color.x,color.y),color.z));}}half4 sk_lighting" -"(shader normalMap,float2 coord,half depth,half shininess,half materialType," -"half lightType,half3 lightPos,half spotFalloff,half3 lightDir,half cosCutoffAngle" +"(coeff*color);return half4(color,1.);}else{half3 halfDir=normalize(surfaceToLight" +"+half3(0.,0.,1.));half coeff=pow(dot(normal,halfDir),shininess);color=saturate" +"(coeff*color);return half4(color,max(max(color.x,color.y),color.z));}}half4" +" sk_lighting(shader normalMap,float2 coord,half depth,half shininess,half materialType" +",half lightType,half3 lightPos,half spotFalloff,half3 lightDir,half cosCutoffAngle" ",half3 lightColor){half4 normalAndA=normalMap.eval(coord);half3 surfaceToLight" "=$surface_to_light(lightType,lightPos,lightDir,half3(half2(coord),depth*normalAndA" ".w));return $compute_lighting(lightColor,shininess,materialType,lightType,normalAndA" diff --git a/gfx/skia/skia/src/sksl/ir/SkSLFunctionDeclaration.cpp b/gfx/skia/skia/src/sksl/ir/SkSLFunctionDeclaration.cpp @@ -238,7 +238,8 @@ static bool check_main_signature(const Context& context, Position pos, const Typ break; } case ProgramKind::kFragment: - case ProgramKind::kGraphiteFragment: { + case ProgramKind::kGraphiteFragment: + case ProgramKind::kGraphiteFragmentES2: { bool validParams = (parameters.size() == 0) || (parameters.size() == 1 && paramIsCoords(0)); if (!validParams) { @@ -249,6 +250,7 @@ static bool check_main_signature(const Context& context, Position pos, const Typ } case ProgramKind::kVertex: case ProgramKind::kGraphiteVertex: + case ProgramKind::kGraphiteVertexES2: case ProgramKind::kCompute: if (!returnType.matches(*context.fTypes.fVoid)) { errors.error(pos, "'main' must return 'void'"); diff --git a/gfx/skia/skia/src/sksl/ir/SkSLSwitchStatement.cpp b/gfx/skia/skia/src/sksl/ir/SkSLSwitchStatement.cpp @@ -194,7 +194,7 @@ std::unique_ptr<Statement> SwitchStatement::Convert(const Context& context, // If a switch-case has variable declarations at its top level, we want to create a scoped block // around the switch, then move the variable declarations out of the switch body and into the // outer scope. This prevents scoping issues in backends which don't offer a native switch. - // (skbug.com/40045447) It also allows static-switch optimization to work properly when variables are + // (skia:14375) It also allows static-switch optimization to work properly when variables are // inherited from earlier fall-through cases. (oss-fuzz:70589) std::unique_ptr<Block> block = Transform::HoistSwitchVarDeclarationsAtTopLevel(context, cases, *symbolTable, pos); diff --git a/gfx/skia/skia/src/sksl/ir/SkSLType.h b/gfx/skia/skia/src/sksl/ir/SkSLType.h @@ -28,12 +28,6 @@ #include <string_view> #include <tuple> -// IWYU wants this, but it's C++20 and beyond. We can remove -// this guard once C++ core is allowed to use C++20 -#if __cplusplus >= 202002L -#include <compare> -#endif - namespace SkSL { class Context; diff --git a/gfx/skia/skia/src/sksl/sksl_graphite_frag.sksl b/gfx/skia/skia/src/sksl/sksl_graphite_frag.sksl @@ -28,6 +28,10 @@ $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(colorParam.rgb, 1.0); } @@ -36,24 +40,24 @@ $pure half4 sk_alpha_only(float4 colorParam) { return half4(0.0, 0.0, 0.0, colorParam.a); } -$pure float3 $apply_srgb_xfer_fn(float3 x, float4 gabc, float3 def) { +$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, float3 abc, float3 def) { +$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, float3 abc, float3 def) { +$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, float3 abc, float3 def) { +$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])), @@ -62,12 +66,10 @@ $pure float3 $apply_hlg_inv_xfer_fn(float3 x, float3 abc, float3 def) { $pure half4 sk_color_space_transform(half4 color, half3x3 gamut, - float4 srcGABC, - float4 srcDEF_args, - float4 dstGABC, - float4 dstDEF_args, - float4 srcOOTF_args, - float4 dstOOTF_args) { + 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} @@ -89,9 +91,9 @@ $pure half4 sk_color_space_transform(half4 color, // 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 - half(srcDEF_args.w); - half alphaSwizzle1 = half(srcDEF_args.w) * half(dstDEF_args.w); - half alphaSwizzleR = half(srcDEF_args.w) - alphaSwizzle1; + 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)); } @@ -109,18 +111,8 @@ $pure half4 sk_color_space_transform(half4 color, colorF = sign(colorF) * $apply_hlg_xfer_fn(abs(colorF), srcGABC.yzw, srcDEF_args.xyz); } - if (srcOOTF_args.w != 0) { - float Y = dot(srcOOTF_args.rgb, colorF); - colorF *= sign(Y) * pow(abs(Y), srcOOTF_args.w); - } - colorF = gamut * colorF; - if (dstOOTF_args.w != 0) { - float Y = dot(dstOOTF_args.rgb, colorF); - colorF *= sign(Y) * pow(abs(Y), dstOOTF_args.w); - } - if (dstGABC.x > 0) { colorF = sign(colorF) * $apply_srgb_xfer_fn(abs(colorF), dstGABC, dstDEF_args.xyz); } else if (dstGABC.x < -1 ) { @@ -136,7 +128,7 @@ $pure half4 sk_color_space_transform(half4 color, // "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 = half(dstDEF_args.w); + half noPremul = dstDEF_args.w; color.rgb = half3(colorF.rgb) * max(color.a, noPremul); return color; @@ -174,10 +166,10 @@ $pure half4 sk_color_space_transform_premul(half4 color, half2 args) { $pure half4 sk_color_space_transform_srgb(half4 color, half3x3 gamut, - float4 srcGABC, - float4 srcDEF_args, - float4 dstGABC, - float4 dstDEF_args) { + 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} @@ -199,9 +191,9 @@ $pure half4 sk_color_space_transform_srgb(half4 color, // 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 - half(srcDEF_args.w); - half alphaSwizzle1 = half(srcDEF_args.w) * half(dstDEF_args.w); - half alphaSwizzleR = half(srcDEF_args.w) - alphaSwizzle1; + 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)); } @@ -219,7 +211,7 @@ $pure half4 sk_color_space_transform_srgb(half4 color, // "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 = half(dstDEF_args.w); + half noPremul = dstDEF_args.w; color.rgb = half3(colorF.rgb) * max(color.a, noPremul); return color; @@ -301,7 +293,7 @@ $pure half4 $sample_image_subset(float2 pos, sampler2D s) { // Do hard-edge shader transitions to the border color for nearest-neighbor decal tiling at the // subset boundaries. Snap the input coordinates to nearest neighbor before comparing to the - // subset rect, to avoid GPU interpolation errors. See skbug.com/40041736. + // subset rect, to avoid GPU interpolation errors. See https://crbug.com/skia/10403. if (tileModeX == $kTileModeDecal && filterMode == $kFilterModeNearest) { float snappedX = floor(pos.x) + 0.5; if (snappedX < subset.x || snappedX > subset.z) { @@ -324,7 +316,7 @@ $pure half4 $sample_image_subset(float2 pos, if (filterMode == $kFilterModeNearest) { insetClamp = float4(floor(subset.xy) + $kLinearInset, ceil(subset.zw) - $kLinearInset); } else { - insetClamp = float4(subset.xy + linearFilterInset, subset.zw - linearFilterInset); + 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); @@ -441,9 +433,10 @@ $pure half4 sk_cubic_image_shader(float2 coords, return $cubic_filter_image(coords, invImgSize, subset, tileModeX, tileModeY, cubicCoeffs, s); } -$pure half4 sk_hw_image_shader(float2 coords, sampler2D s) { - // This assumes that `coords` have already been normalized. - return sample(s, coords); +$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, @@ -570,8 +563,6 @@ $pure half4 sk_cubic_yuv_image_shader(float2 coords, $pure half4 sk_hw_yuv_image_shader(float2 coords, float2 invImgSizeY, float2 invImgSizeUV, // Relative to Y's coordinate space - float4 subset, - float2 linearFilterUVInset, half4 channelSelectY, half4 channelSelectU, half4 channelSelectV, @@ -582,29 +573,10 @@ $pure half4 sk_hw_yuv_image_shader(float2 coords, sampler2D sU, sampler2D sV, sampler2D sA) { - // If the base filtermode is nearest and we have to snap the coords to Y's - // texel centers to get the correct positions for UV, and then clamp so that - // we don't sample any coordinates beyond the boundary. - float4 subsetUV = subset; - if (linearFilterUVInset.x < 0) { - coords = floor(coords) + 0.5; - // Snap Y subset to texel boundaries for nearest neighbor filter mode; - // use the original subset for UV since it will have linear filtering. - subset = float4(floor(subset.xy), ceil(subset.zw)); - } - - float2 coordsUV = coords; - if (linearFilterUVInset.y < 0) { - linearFilterUVInset = abs(linearFilterUVInset); - coordsUV = - clamp(coords, subsetUV.xy + linearFilterUVInset, subsetUV.zw - linearFilterUVInset); - coords = clamp(coords, subset.xy + $kLinearInset, subset.zw - $kLinearInset); - } - half4 sampleColorY, sampleColorU, sampleColorV, sampleColorA; sampleColorY = $sample_image(coords, invImgSizeY, sY); - sampleColorU = $sample_image(coordsUV, invImgSizeUV, sU); - sampleColorV = $sample_image(coordsUV, invImgSizeUV, sV); + sampleColorU = $sample_image(coords, invImgSizeUV, sU); + sampleColorV = $sample_image(coords, invImgSizeUV, sV); half alpha; if (channelSelectA == half4(1)) { alpha = 1; @@ -621,37 +593,16 @@ $pure half4 sk_hw_yuv_image_shader(float2 coords, $pure half4 sk_hw_yuv_no_swizzle_image_shader(float2 coords, float2 invImgSizeY, float2 invImgSizeUV, // Relative to Y's coord space - float4 subset, - float2 linearFilterUVInset, half3x3 yuvToRGBMatrix, half4 yuvToRGBXlateAlphaParam, sampler2D sY, sampler2D sU, sampler2D sV, sampler2D sA) { - // If the base filtermode is nearest and we have to snap the coords to Y's - // texel centers to get the correct positions for UV, and then clamp so that - // we don't sample any coordinates beyond the boundary. - float4 subsetUV = subset; - if (linearFilterUVInset.x < 0) { - coords = floor(coords) + 0.5; - // Snap Y subset to texel boundaries for nearest neighbor filter mode; - // use the original subset for UV since it will have linear filtering. - subset = float4(floor(subset.xy), ceil(subset.zw)); - } - - float2 coordsUV = coords; - if (linearFilterUVInset.y < 0) { - linearFilterUVInset = abs(linearFilterUVInset); - coordsUV = - clamp(coords, subsetUV.xy + linearFilterUVInset, subsetUV.zw - linearFilterUVInset); - coords = clamp(coords, subset.xy + $kLinearInset, subset.zw - $kLinearInset); - } - half4 sampleColorY, sampleColorU, sampleColorV, sampleColorA; half Y = $sample_image(coords, invImgSizeY, sY).r; - half U = $sample_image(coordsUV, invImgSizeUV, sU).r; - half V = $sample_image(coordsUV, invImgSizeUV, sV).r; + half U = $sample_image(coords, invImgSizeUV, sU).r; + half V = $sample_image(coords, invImgSizeUV, sV).r; // When it's a Y_U_V_A texture, yuvToRGBXlateAlphaParam.w is 0 and we have a real A sampler so // alpha is just that saturated value (should be a no-op). For Y_U_V, we set // yuvToRGBXlateAlphaParam.w to 1 and sample from Y, which after the saturate is always 1. @@ -1170,22 +1121,27 @@ $pure half4 sk_conical_grad_buf_shader(float2 coords, return $interpolated_to_rgb_unpremul(color, colorSpace, doUnpremul); } -$pure half4 sk_hsl_matrix_colorfilter(half4 color, half4x4 m, half4 v) { - color = $rgb_to_hsl(color.rgb, color.a); // includes unpremul - color = (m * color) + v; - return $hsl_to_rgb(color.rgb, color.a); // includes clamp and premul -} +$pure half4 sk_matrix_colorfilter(half4 colorIn, float4x4 m, float4 v, int inHSLA, int clampRGB) { + if (bool(inHSLA)) { + colorIn = $rgb_to_hsl(colorIn.rgb, colorIn.a); // includes unpremul + } else { + colorIn = unpremul(colorIn); + } -$pure half4 sk_matrix_colorfilter(half4 color, half4x4 m, half4 v, half2 minMaxRGB) { - color = unpremul(color); - color = (m * color) + v; + half4 colorOut = half4((m * colorIn) + v); - // minMaxRGB is a tuple that stores [minRGB, maxRGB]. minA and maxA are always 0 and 1. minRGB - // and maxRGB are either 0 and 1 for clamped color filters, or very large negative/positive - // values to be unclamped. - color = clamp(color, minMaxRGB.xxx0, minMaxRGB.yyy1); - color.rgb *= color.a; - return color; + if (bool(inHSLA)) { + colorOut = $hsl_to_rgb(colorOut.rgb, colorOut.a); // includes clamp and premul + } else { + if (bool(clampRGB)) { + colorOut = saturate(colorOut); + } else { + colorOut.a = saturate(colorOut.a); + } + colorOut.rgb *= colorOut.a; + } + + return colorOut; } // This method computes the 4 x-coodinates ([0..1]) that should be used to look diff --git a/gfx/skia/skia/src/sksl/sksl_graphite_frag_es2.sksl b/gfx/skia/skia/src/sksl/sksl_graphite_frag_es2.sksl @@ -0,0 +1 @@ +// TODO(jamesgk): implement Graphite ES2 support diff --git a/gfx/skia/skia/src/sksl/sksl_graphite_vert.sksl b/gfx/skia/skia/src/sksl/sksl_graphite_vert.sksl @@ -228,18 +228,14 @@ $pure float4 tessellate_stroked_curve(float edgeID, float maxEdges, float numParametricSegments; if (w < 0) { if (p0 == p1 && p2 == p3) { - numParametricSegments = 1; // a line (or a circle if p1 == p2) + numParametricSegments = 1; // a line } else { numParametricSegments = $wangs_formula_cubic(p0, p1, p2, p3, affineMatrix); } } else { - if (p0 == p1 || p1 == p2) { - numParametricSegments = 1; // a line (or a square if p0 == p2) - } else { - numParametricSegments = $wangs_formula_conic(affineMatrix * p0, - affineMatrix * p1, - affineMatrix * p2, w); - } + numParametricSegments = $wangs_formula_conic(affineMatrix * p0, + affineMatrix * p1, + affineMatrix * p2, w); } // Matches skgpu::tess::StrokeParams @@ -248,7 +244,7 @@ $pure float4 tessellate_stroked_curve(float edgeID, float maxEdges, bool isHairline = strokeParams.x == 0.0; float numRadialSegmentsPerRadian; if (isHairline) { - numRadialSegmentsPerRadian = $num_radial_segments_per_radian(0.5); + numRadialSegmentsPerRadian = $num_radial_segments_per_radian(1.0); strokeRadius = 0.5; } else { numRadialSegmentsPerRadian = $num_radial_segments_per_radian(maxScale * strokeParams.x); @@ -264,37 +260,14 @@ $pure float4 tessellate_stroked_curve(float edgeID, float maxEdges, lastControlPoint = affineMatrix * lastControlPoint; } - // Find the starting and ending tangents (of the curve) and the tangent of the previous curve - // that connects to it. + // Find the starting and ending tangents. float2 tan0 = $robust_normalize_diff((p0 == p1) ? ((p1 == p2) ? p3 : p2) : p1, p0); float2 tan1 = $robust_normalize_diff(p3, (p3 == p2) ? ((p2 == p1) ? p0 : p1) : p2); - float2 prevTan = $robust_normalize_diff(p0, lastControlPoint); if (tan0 == float2(0)) { - // The stroke is a point, which is a special case for square or circle caps. - joinType = 0; - if (w < 0) { - // This is a special case representing a stroke-width circle, so treat it as a 180 - // degree point stroke instead. - tan0 = float2(1,0); - tan1 = float2(-1,0); - } else { - // This is a special case representing a stroke-width square, or half-square cap - // (depending on the value of lastControlPoint). - w = -1; - tan0 = prevTan; - tan1 = prevTan; - if (prevTan == float2(0.0, 0.0)) { - // A square cap, so adjust both p0,p1 and p2,p3 to shift by the stroke radius in - // opposite directions along the X axis - p2 = p3 = p0 + strokeRadius * float2(1.0, 0.0); - p0 = p1 = p0 - strokeRadius * float2(1.0, 0.0); - prevTan = tan0 = tan1 = float2(1.0, 0.0); - } else { - // A half-square, so behave as a lineTo from p0 to a strokeRadius outset away from - // the prior tangent point - p2 = p3 = p0 + strokeRadius * prevTan; - } - } + // The stroke is a point. This special case tells us to draw a stroke-width circle as a + // 180 degree point stroke instead. + tan0 = float2(1,0); + tan1 = float2(-1,0); } // Determine how many edges to give to the join. We emit the first and final edges @@ -307,7 +280,7 @@ $pure float4 tessellate_stroked_curve(float edgeID, float maxEdges, // +2 because we emit the beginning and ending edges twice (see above comments). numEdgesInJoin = sign(joinType) + (1 + 2); } else { - // NOTE: for circles, prevTan = (0,0), but this still works out to 180 degrees here + float2 prevTan = $robust_normalize_diff(p0, lastControlPoint); float joinRads = acos($cosine_between_unit_vectors(prevTan, tan0)); float numRadialSegmentsInJoin = max(ceil(joinRads * numRadialSegmentsPerRadian), 1); // +2 because we emit the beginning and ending edges twice (see above comment). @@ -329,7 +302,7 @@ $pure float4 tessellate_stroked_curve(float edgeID, float maxEdges, // means the join is disabled, and to disable it with the existing code we can leave // tan0 equal to tan1. if (lastControlPoint != p0) { - tan0 = prevTan; + tan0 = $robust_normalize_diff(p0, lastControlPoint); } turn = cross_length_2d(tan0, tan1); } @@ -361,7 +334,6 @@ $pure float4 tessellate_stroked_curve(float edgeID, float maxEdges, // nearly equivalent this could theoretically result in bad seaming and/or cracks on the // side we don't put it on. If the tangents are nearly equivalent then we leave the join // double-sided. - // NOTE: For square caps, the construction of tan0/tan1 ensure we emit a full width edge const float sinEpsilon = 1e-2; // ~= sin(180deg / 3000) bool tangentsNearlyParallel = (abs(turn) * inversesqrt(dot(tan0, tan0) * dot(tan1, tan1))) < sinEpsilon; @@ -422,7 +394,7 @@ $pure float4 tessellate_stroked_curve(float edgeID, float maxEdges, B = E - C; A = fma(float2(-3), E, D); } - // FIXME(crbug.com/800804,skbug.com/40042642): Consider normalizing the exponents in A,B,C at + // FIXME(crbug.com/800804,skbug.com/11268): Consider normalizing the exponents in A,B,C at // this point in order to prevent fp32 overflow. // Now find the coefficients that give a tangent direction from a parametric edge ID: @@ -558,7 +530,6 @@ $pure float4 tessellate_stroked_curve(float edgeID, float maxEdges, } float4 analytic_rrect_vertex_fn(// Vertex Attributes - uint cornerID, float2 position, float2 normal, float normalScale, @@ -579,9 +550,12 @@ float4 analytic_rrect_vertex_fn(// Vertex Attributes out float2 perPixelControl, // Render Step out float2 stepLocalCoords) { + const uint kCornerVertexCount = 9; // KEEP IN SYNC WITH C++'s + // AnalyticRRectRenderStep::kCornerVertexCount const float kMiterScale = 1.0; const float kBevelScale = 0.0; const float kRoundScale = 0.41421356237; // sqrt(2)-1 + const float kEpsilon = 0.00024; // SK_ScalarNearlyZero // Default to miter'ed vertex positioning. Corners with sufficiently large corner radii, or @@ -589,15 +563,6 @@ float4 analytic_rrect_vertex_fn(// Vertex Attributes // the final coverage calculations in the fragment shader. float joinScale = kMiterScale; - // Calculate a rotated cornerID value to use for indexing within vectors that would normally - // require ".yzwx" swizzling before indexing (e.g., if `var` is a container of 4, - // `var.yzwx[cornerID]` is instead handled as `var[nextID]`). Though theoretically equivalent, - // using the modified cornerID gets around a driver bug within Qualcomm Adreno 620 GPUs (used by - // the Pixel 5) which leads to incorrect output when swizzling and indexing into a vector within - // one line or operation. This issue may also exist on other devices as well. Since the - // workaround is simple, just use this approach for all vertex shaders. - uint nextID = (cornerID + 1) % 4; - // Unpack instance-level state that determines the vertex placement and style of shape. bool bidirectionalCoverage = center.z <= 0.0; bool deviceSpaceDistances = false; @@ -654,6 +619,7 @@ float4 analytic_rrect_vertex_fn(// Vertex Attributes } // Adjust state on a per-corner basis + uint cornerID = uint(sk_VertexID) / kCornerVertexCount; float2 cornerRadii = float2(xRadii[cornerID], yRadii[cornerID]); if (cornerID % 2 != 0) { // Corner radii are uploaded in the local coordinate frame, but vertex placement happens @@ -709,8 +675,7 @@ float4 analytic_rrect_vertex_fn(// Vertex Attributes // Don't outset by stroke radius for butt caps on the zero-length edge, but // adjust edgeBias and strokeParams to calculate an AA miter'ed shape with the // non-uniform stroke outset. - - strokeRadius *= float2(edgeMask[cornerID], edgeMask[nextID]); + strokeRadius *= float2(edgeMask[cornerID], edgeMask.yzwx[cornerID]); edgeBias = (edgeMask - 1.0) * strokeParams.x; strokeParams.y = 1.0; joinScale = kMiterScale; @@ -723,8 +688,8 @@ float4 analytic_rrect_vertex_fn(// Vertex Attributes dy *= inverseEdgeLen; // Calculate local coordinate for the vertex (relative to xAxis and yAxis at first). - float2 xAxis = -float2(dx[nextID], dy[nextID]); - float2 yAxis = float2(dx[cornerID], dy[cornerID]); + float2 xAxis = -float2(dx.yzwx[cornerID], dy.yzwx[cornerID]); + float2 yAxis = float2(dx.xyzw[cornerID], dy.xyzw[cornerID]); float2 localPos; bool snapToCenter = false; if (normalScale < 0.0) { @@ -810,7 +775,7 @@ float4 analytic_rrect_vertex_fn(// Vertex Attributes // because it accounts for the position's influence on a line's projected direction. float2x2 J = float2x2(jacobian); - float2 edgeAANormal = float2(edgeAA[cornerID], edgeAA[nextID]) * normal; + float2 edgeAANormal = float2(edgeAA[cornerID], edgeAA.yzwx[cornerID]) * normal; float2 nx = cornerAspectRatio.x * edgeAANormal.x * perp(-yAxis) * J; float2 ny = cornerAspectRatio.y * edgeAANormal.y * perp( xAxis) * J; @@ -877,7 +842,6 @@ float4 analytic_rrect_vertex_fn(// Vertex Attributes } float4 per_edge_aa_quad_vertex_fn(// Vertex Attributes - uint cornerID, float2 normal, // Instance Attributes float4 edgeAA, @@ -889,11 +853,10 @@ float4 per_edge_aa_quad_vertex_fn(// Vertex Attributes out float4 edgeDistances, // Render Step out float2 stepLocalCoords) { - const float kEpsilon = 0.00024; // SK_ScalarNearlyZero + const uint kCornerVertexCount = 4; // KEEP IN SYNC WITH C++'s + // PerEdgeAAQuadRenderStep::kCornerVertexCount - // Calculate a rotated cornerID value to use for indexing within vectors that would normally - // require ".yzwx" swizzling (see similar note in analytic_rrect_vertex_fn for explanation). - uint nextID = (cornerID + 1) % 4; + const float kEpsilon = 0.00024; // SK_ScalarNearlyZero // Calculate the local edge vectors, ordered L, T, R, B starting from the bottom left point. // For quadrilaterals these are not necessarily axis-aligned, but in all cases they orient @@ -935,8 +898,9 @@ float4 per_edge_aa_quad_vertex_fn(// Vertex Attributes dy *= inverseEdgeLen; // Calculate local coordinate for the vertex (relative to xAxis and yAxis at first). - float2 xAxis = -float2(dx[nextID], dy[nextID]); - float2 yAxis = float2(dx[cornerID], dy[cornerID]); + uint cornerID = uint(sk_VertexID) / kCornerVertexCount; + float2 xAxis = -float2(dx.yzwx[cornerID], dy.yzwx[cornerID]); + float2 yAxis = float2(dx.xyzw[cornerID], dy.xyzw[cornerID]); // Vertex is outset from the base shape (and possibly with an additional AA outset later // in device space). @@ -976,7 +940,7 @@ float4 per_edge_aa_quad_vertex_fn(// Vertex Attributes float2x2 J = float2x2(deviceToLocal[0].xy - deviceToLocal[0].z*localPos, deviceToLocal[1].xy - deviceToLocal[1].z*localPos); - float2 edgeAANormal = float2(edgeAA[cornerID], edgeAA[nextID]) * normal; + float2 edgeAANormal = float2(edgeAA[cornerID], edgeAA.yzwx[cornerID]) * normal; float2 nx = edgeAANormal.x * perp(-yAxis) * J; float2 ny = edgeAANormal.y * perp( xAxis) * J; @@ -1024,7 +988,6 @@ float4 circular_arc_vertex_fn(float3 position, float3 fragClipPlane0, float3 fragClipPlane1, float4 inRoundCapPos, - float inRoundCapRadius, float depth, float3x3 localToDevice, // Varyings @@ -1082,7 +1045,13 @@ float4 circular_arc_vertex_fn(float3 position, isectPlane = float3(0, 0, 1); unionPlane = fragClipPlane1; } - roundCapRadius = inRoundCapRadius; + if (abs(radiiAndFlags.z) > 1) { + // This is the cap radius in normalized space where the outer radius is 1 and + // radii.y is the normalized inner radius. + roundCapRadius = (1.0 - radiiAndFlags.y) / 2.0; + } else { + roundCapRadius = 0; + } roundCapPos = inRoundCapPos; stepLocalCoords = localPos; diff --git a/gfx/skia/skia/src/sksl/sksl_graphite_vert_es2.sksl b/gfx/skia/skia/src/sksl/sksl_graphite_vert_es2.sksl @@ -0,0 +1 @@ +// TODO(jamesgk): implement Graphite ES2 support diff --git a/gfx/skia/skia/src/sksl/sksl_rt_shader.sksl b/gfx/skia/skia/src/sksl/sksl_rt_shader.sksl @@ -156,21 +156,16 @@ $pure half4 $compute_lighting(half3 color, half shininess, half materialType, ha color *= $spotlight_scale(lightDir, surfaceToLight, cosCutoffAngle, spotFalloff); } - // Scale color by material type - if (materialType == 0) { // diffuse + // Diffuse and specular reflections scale the light's color differently + if (materialType == 0) { half coeff = dot(normal, surfaceToLight); color = saturate(coeff * color); return half4(color, 1.0); - } else if (materialType == 1) { // specular + } else { half3 halfDir = normalize(surfaceToLight + half3(0, 0, 1)); half coeff = pow(dot(normal, halfDir), shininess); color = saturate(coeff * color); return half4(color, max(max(color.r, color.g), color.b)); - } else { // emboss - half hilite = (2 * dot(normal, surfaceToLight) - surfaceToLight.z) * surfaceToLight.z; - half coeff = pow(hilite, shininess); - color = saturate(coeff * color); - return half4(color, max(max(color.r, color.g), color.b)); } } diff --git a/gfx/skia/skia/src/sksl/sksl_shared.sksl b/gfx/skia/skia/src/sksl/sksl_shared.sksl @@ -326,7 +326,7 @@ $export $pure half4 $hsl_to_rgb(half3 hsl, half a) { // Color conversion functions used in gradient interpolation, based on // https://www.w3.org/TR/css-color-4/#color-conversion-code -// TODO(skbug.com/40044213): For all of these, we can eliminate any linear math at the beginning +// TODO(skia:13108): For all of these, we can eliminate any linear math at the beginning // (by removing the corresponding linear math at the end of the CPU code). $export $pure half3 $css_lab_to_xyz(half3 lab) { const half k = 24389 / 27.0; @@ -483,7 +483,7 @@ $export $pure half3 $css_okhcl_gamut_map_to_linear_srgb(half3 okhcl) { return $css_oklab_gamut_map_to_linear_srgb($css_hcl_to_lab(okhcl)); } -// TODO(skbug.com/40044213): Use our optimized version (though it has different range) +// TODO(skia:13108): Use our optimized version (though it has different range) // Doing so might require fixing (re-deriving?) the math for the HWB version below $export $pure half3 $css_hsl_to_srgb(half3 hsl) { hsl.x = mod(hsl.x, 360); diff --git a/gfx/skia/skia/src/text/GlyphRun.cpp b/gfx/skia/skia/src/text/GlyphRun.cpp @@ -166,7 +166,8 @@ static SkRect glyphrun_source_bounds( // Use conservative bounds. All glyph have a box of fontBounds size. if (scaledRotations.empty()) { - SkRect bounds = SkRect::BoundsOrEmpty(positions); + SkRect bounds; + bounds.setBounds(positions.data(), SkCount(positions)); bounds.fLeft += fontBounds.left(); bounds.fTop += fontBounds.top(); bounds.fRight += fontBounds.right(); @@ -337,7 +338,7 @@ SkSpan<const SkGlyphID> GlyphRunBuilder::textToGlyphIDs( int count = font.countText(bytes, byteLength, encoding); if (count > 0) { fScratchGlyphIDs.resize(count); - font.textToGlyphs(bytes, byteLength, encoding, fScratchGlyphIDs); + font.textToGlyphs(bytes, byteLength, encoding, fScratchGlyphIDs.data(), count); return SkSpan(fScratchGlyphIDs); } else { return SkSpan<const SkGlyphID>(); diff --git a/gfx/skia/skia/src/text/gpu/GlyphVector.h b/gfx/skia/skia/src/text/gpu/GlyphVector.h @@ -11,7 +11,6 @@ #include "include/core/SkRefCnt.h" #include "include/core/SkSpan.h" #include "src/core/SkGlyph.h" -#include "src/core/SkStrike.h" // IWYU pragma: keep #include "src/gpu/AtlasTypes.h" #include "src/text/StrikeForGPU.h" #include "src/text/gpu/StrikeCache.h" diff --git a/gfx/skia/skia/src/text/gpu/SubRunContainer.cpp b/gfx/skia/skia/src/text/gpu/SubRunContainer.cpp @@ -15,6 +15,7 @@ #include "include/core/SkPaint.h" #include "include/core/SkPath.h" #include "include/core/SkPathEffect.h" +#include "include/core/SkPoint.h" #include "include/core/SkRect.h" #include "include/core/SkScalar.h" #include "include/core/SkStrokeRec.h" @@ -46,9 +47,11 @@ #include "src/text/GlyphRun.h" #include "src/text/StrikeForGPU.h" #include "src/text/gpu/Glyph.h" +#include "src/text/gpu/GlyphVector.h" #include "src/text/gpu/SDFMaskFilter.h" -#include "src/text/gpu/StrikeCache.h" +#include "src/text/gpu/SubRunAllocator.h" #include "src/text/gpu/SubRunControl.h" +#include "src/text/gpu/VertexFiller.h" #include <algorithm> #include <climits> @@ -57,6 +60,25 @@ #include <optional> #include <vector> +#if defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS) +#include "include/core/SkRRect.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" +#include "src/gpu/ganesh/GrFragmentProcessor.h" +#include "src/gpu/ganesh/GrPaint.h" +#include "src/gpu/ganesh/SkGr.h" +#include "src/gpu/ganesh/SurfaceDrawContext.h" +#include "src/gpu/ganesh/effects/GrDistanceFieldGeoProc.h" +#include "src/gpu/ganesh/ops/AtlasTextOp.h" + +class GrRecordingContext; + +using AtlasTextOp = skgpu::ganesh::AtlasTextOp; +#endif // defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS) + using namespace skia_private; using namespace skglyph; @@ -93,6 +115,30 @@ using namespace sktext; using namespace sktext::gpu; namespace { +#if defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS) +SkPMColor4f calculate_colors(skgpu::ganesh::SurfaceDrawContext* sdc, + const SkPaint& paint, + const SkMatrix& matrix, + MaskFormat maskFormat, + GrPaint* grPaint) { + GrRecordingContext* rContext = sdc->recordingContext(); + const GrColorInfo& colorInfo = sdc->colorInfo(); + const SkSurfaceProps& props = sdc->surfaceProps(); + if (maskFormat == MaskFormat::kARGB) { + SkPaintToGrPaintReplaceShader(rContext, colorInfo, paint, matrix, nullptr, props, grPaint); + float a = grPaint->getColor4f().fA; + return {a, a, a, a}; + } + SkPaintToGrPaint(rContext, colorInfo, paint, matrix, props, grPaint); + return grPaint->getColor4f(); +} + +SkMatrix position_matrix(const SkMatrix& drawMatrix, SkPoint drawOrigin) { + SkMatrix position_matrix = drawMatrix; + return position_matrix.preTranslate(drawOrigin.x(), drawOrigin.y()); +} +#endif // defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS) + SkSpan<const SkPackedGlyphID> get_packedIDs(SkZip<const SkPackedGlyphID, const SkPoint> accepted) { return accepted.get<0>(); } @@ -176,7 +222,7 @@ void PathOpSubmitter::flatten(SkWriteBuffer& buffer) const { buffer.writeInt(fIsAntiAliased); buffer.writeScalar(fStrikeToSourceScale); - buffer.writePointArray(fPositions); + buffer.writePointArray(fPositions.data(), SkCount(fPositions)); for (IDOrPath& idOrPath : fIDsOrPaths) { buffer.writeInt(idOrPath.fGlyphID); } @@ -312,7 +358,8 @@ PathOpSubmitter::submitDraws(SkCanvas* canvas, SkPoint drawOrigin, const SkPaint SkMatrix pathMatrix = strikeToSource; pathMatrix.postTranslate(pos.x(), pos.y()); - SkPath deviceOutline = idOrPath.fPath.makeTransform(pathMatrix); + SkPath deviceOutline; + idOrPath.fPath.transform(pathMatrix, &deviceOutline); deviceOutline.setIsVolatile(true); canvas->drawPath(deviceOutline, runPaint); } @@ -436,7 +483,7 @@ void DrawableOpSubmitter::flatten(SkWriteBuffer& buffer) const { fStrikePromise.flatten(buffer); buffer.writeScalar(fStrikeToSourceScale); - buffer.writePointArray(fPositions); + buffer.writePointArray(fPositions.data(), SkCount(fPositions)); for (IDOrDrawable idOrDrawable : fIDsOrDrawables) { buffer.writeInt(idOrDrawable.fGlyphID); } @@ -591,12 +638,55 @@ const AtlasSubRun* DrawableSubRun::testingOnly_atlasSubRun() const { return nullptr; } +#if defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS) +enum ClipMethod { + kClippedOut, + kUnclipped, + kGPUClipped, + kGeometryClipped +}; + +std::tuple<ClipMethod, SkIRect> +calculate_clip(const GrClip* clip, SkRect deviceBounds, SkRect glyphBounds) { + if (clip == nullptr && !deviceBounds.intersects(glyphBounds)) { + return {kClippedOut, SkIRect::MakeEmpty()}; + } else if (clip != nullptr) { + switch (auto result = clip->preApply(glyphBounds, GrAA::kNo); result.fEffect) { + case GrClip::Effect::kClippedOut: + return {kClippedOut, SkIRect::MakeEmpty()}; + case GrClip::Effect::kUnclipped: + return {kUnclipped, SkIRect::MakeEmpty()}; + case GrClip::Effect::kClipped: { + if (result.fIsRRect && result.fRRect.isRect()) { + SkRect r = result.fRRect.rect(); + if (result.fAA == GrAA::kNo || GrClip::IsPixelAligned(r)) { + SkIRect clipRect = SkIRect::MakeEmpty(); + // Clip geometrically during onPrepare using clipRect. + r.round(&clipRect); + if (clipRect.contains(glyphBounds)) { + // If fully within the clip, signal no clipping using the empty rect. + return {kUnclipped, SkIRect::MakeEmpty()}; + } + // Use the clipRect to clip the geometry. + return {kGeometryClipped, clipRect}; + } + // Partial pixel clipped at this point. Have the GPU handle it. + } + } + break; + } + } + return {kGPUClipped, SkIRect::MakeEmpty()}; +} +#endif // defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS) + // -- DirectMaskSubRun ----------------------------------------------------------------------------- -class DirectMaskSubRun final : public AtlasSubRun { +class DirectMaskSubRun final : public SubRun, public AtlasSubRun { public: DirectMaskSubRun(VertexFiller&& vertexFiller, GlyphVector&& glyphs) - : AtlasSubRun(std::move(vertexFiller), std::move(glyphs)) {} + : fVertexFiller{std::move(vertexFiller)} + , fGlyphs{std::move(glyphs)} {} static SubRunOwner Make(SkRect creationBounds, SkZip<const SkPackedGlyphID, const SkPoint> accepted, @@ -649,23 +739,114 @@ public: fVertexFiller.unflattenSize(); } + int glyphCount() const override { + return SkCount(fGlyphs.glyphs()); + } + + SkSpan<const Glyph*> glyphs() const override { + return fGlyphs.glyphs(); + } + + MaskFormat maskFormat() const override { return fVertexFiller.grMaskType(); } + int glyphSrcPadding() const override { return 0; } + unsigned short instanceFlags() const override { + return (unsigned short)fVertexFiller.grMaskType(); + } + void testingOnly_packedGlyphIDToGlyph(StrikeCache* cache) const override { fGlyphs.packedGlyphIDToGlyph(cache); } - std::tuple<bool, SkRect> deviceRectAndNeedsTransform( - const SkMatrix &positionMatrix) const override { - auto [integerTranslate, deviceRect] = - fVertexFiller.deviceRectAndCheckTransform(positionMatrix); - return {!integerTranslate, deviceRect}; +#if defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS) + size_t vertexStride(const SkMatrix& drawMatrix) const override { + return fVertexFiller.vertexStride(drawMatrix); } - GlyphParams glyphParams() const override { - // Since this is non-SDF, isAA will be ignored so we just pass true - return { /*isSDF=*/false, fVertexFiller.isLCD(), /*isAA=*/true }; - } + std::tuple<const GrClip*, GrOp::Owner> makeAtlasTextOp( + const GrClip* clip, + const SkMatrix& viewMatrix, + SkPoint drawOrigin, + const SkPaint& paint, + sk_sp<SkRefCnt>&& subRunStorage, + skgpu::ganesh::SurfaceDrawContext* sdc) const override { + SkASSERT(this->glyphCount() != 0); + const SkMatrix& positionMatrix = position_matrix(viewMatrix, drawOrigin); + + auto [integerTranslate, subRunDeviceBounds] = + fVertexFiller.deviceRectAndCheckTransform(positionMatrix); + if (subRunDeviceBounds.isEmpty()) { + return {nullptr, nullptr}; + } + // Rect for optimized bounds clipping when doing an integer translate. + SkIRect geometricClipRect = SkIRect::MakeEmpty(); + if (integerTranslate) { + // We can clip geometrically using clipRect and ignore clip when an axis-aligned + // rectangular non-AA clip is used. If clipRect is empty, and clip is nullptr, then + // there is no clipping needed. + const SkRect deviceBounds = SkRect::MakeWH(sdc->width(), sdc->height()); + auto [clipMethod, clipRect] = calculate_clip(clip, deviceBounds, subRunDeviceBounds); + + switch (clipMethod) { + case kClippedOut: + // Returning nullptr as op means skip this op. + return {nullptr, nullptr}; + case kUnclipped: + case kGeometryClipped: + // GPU clip is not needed. + clip = nullptr; + break; + case kGPUClipped: + // Use th GPU clip; clipRect is ignored. + break; + } + geometricClipRect = clipRect; + + if (!geometricClipRect.isEmpty()) { SkASSERT(clip == nullptr); } + } + + GrPaint grPaint; + const SkPMColor4f drawingColor = calculate_colors(sdc, + paint, + viewMatrix, + fVertexFiller.grMaskType(), + &grPaint); + + auto geometry = AtlasTextOp::Geometry::Make(*this, + viewMatrix, + drawOrigin, + geometricClipRect, + std::move(subRunStorage), + drawingColor, + sdc->arenaAlloc()); + + GrRecordingContext* const rContext = sdc->recordingContext(); + + GrOp::Owner op = GrOp::Make<AtlasTextOp>(rContext, + fVertexFiller.opMaskType(), + !integerTranslate, + this->glyphCount(), + subRunDeviceBounds, + geometry, + sdc->colorInfo(), + std::move(grPaint)); + return {clip, std::move(op)}; + } + + void fillVertexData(void* vertexDst, int offset, int count, + GrColor color, + const SkMatrix& drawMatrix, SkPoint drawOrigin, + SkIRect clip) const override { + const SkMatrix positionMatrix = position_matrix(drawMatrix, drawOrigin); + fVertexFiller.fillVertexData(offset, count, + fGlyphs.glyphs(), + color, + positionMatrix, + clip, + vertexDst); + } +#endif // defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS) std::tuple<bool, int> regenerateAtlas(int begin, int end, RegenerateAtlasDelegate regenerateAtlas) const override { @@ -673,6 +854,8 @@ public: &fGlyphs, begin, end, fVertexFiller.grMaskType(), this->glyphSrcPadding()); } + const VertexFiller& vertexFiller() const override { return fVertexFiller; } + bool canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const override { auto [reuse, _] = fVertexFiller.deviceRectAndCheckTransform(positionMatrix); return reuse; @@ -691,16 +874,24 @@ protected: fVertexFiller.flatten(buffer); fGlyphs.flatten(buffer); } + +private: + const VertexFiller fVertexFiller; + + // The regenerateAtlas method mutates fGlyphs. It should be called from onPrepare which must + // be single threaded. + mutable GlyphVector fGlyphs; }; // -- TransformedMaskSubRun ------------------------------------------------------------------------ -class TransformedMaskSubRun final : public AtlasSubRun { +class TransformedMaskSubRun final : public SubRun, public AtlasSubRun { public: TransformedMaskSubRun(bool isBigEnough, VertexFiller&& vertexFiller, GlyphVector&& glyphs) - : AtlasSubRun(std::move(vertexFiller), std::move(glyphs)) - , fIsBigEnough{isBigEnough} {} + : fIsBigEnough{isBigEnough} + , fVertexFiller{std::move(vertexFiller)} + , fGlyphs{std::move(glyphs)} {} static SubRunOwner Make(SkZip<const SkPackedGlyphID, const SkPoint> accepted, const SkMatrix& initialPositionMatrix, @@ -759,6 +950,18 @@ public: fGlyphs.packedGlyphIDToGlyph(cache); } + int glyphCount() const override { return SkCount(fGlyphs.glyphs()); } + + SkSpan<const Glyph*> glyphs() const override { + return fGlyphs.glyphs(); + } + + unsigned short instanceFlags() const override { + return (unsigned short)fVertexFiller.grMaskType(); + } + + MaskFormat maskFormat() const override { return fVertexFiller.grMaskType(); } + int glyphSrcPadding() const override { return 1; } void draw(SkCanvas*, @@ -770,23 +973,73 @@ public: {/* isSDF = */false, fVertexFiller.isLCD(), fVertexFiller.grMaskType()}); } - std::tuple<bool, SkRect> deviceRectAndNeedsTransform( - const SkMatrix &positionMatrix) const override { - auto [_, deviceRect] = fVertexFiller.deviceRectAndCheckTransform(positionMatrix); - return {true, deviceRect}; - } +#if defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS) - GlyphParams glyphParams() const override { - // Since this is non-SDF, isAA will be ignored so we just pass true - return { /*isSDF=*/false, fVertexFiller.isLCD(), /*isAA=*/true }; + size_t vertexStride(const SkMatrix& drawMatrix) const override { + return fVertexFiller.vertexStride(drawMatrix); } + std::tuple<const GrClip*, GrOp::Owner> makeAtlasTextOp( + const GrClip* clip, + const SkMatrix& viewMatrix, + SkPoint drawOrigin, + const SkPaint& paint, + sk_sp<SkRefCnt>&& subRunStorage, + skgpu::ganesh::SurfaceDrawContext* sdc) const override { + SkASSERT(this->glyphCount() != 0); + + GrPaint grPaint; + SkPMColor4f drawingColor = calculate_colors(sdc, + paint, + viewMatrix, + fVertexFiller.grMaskType(), + &grPaint); + + auto geometry = AtlasTextOp::Geometry::Make(*this, + viewMatrix, + drawOrigin, + SkIRect::MakeEmpty(), + std::move(subRunStorage), + drawingColor, + sdc->arenaAlloc()); + + GrRecordingContext* const rContext = sdc->recordingContext(); + SkMatrix positionMatrix = position_matrix(viewMatrix, drawOrigin); + auto [_, deviceRect] = fVertexFiller.deviceRectAndCheckTransform(positionMatrix); + GrOp::Owner op = GrOp::Make<AtlasTextOp>(rContext, + fVertexFiller.opMaskType(), + true, + this->glyphCount(), + deviceRect, + geometry, + sdc->colorInfo(), + std::move(grPaint)); + return {clip, std::move(op)}; + } + + void fillVertexData( + void* vertexDst, int offset, int count, + GrColor color, + const SkMatrix& drawMatrix, SkPoint drawOrigin, + SkIRect clip) const override { + const SkMatrix positionMatrix = position_matrix(drawMatrix, drawOrigin); + fVertexFiller.fillVertexData(offset, count, + fGlyphs.glyphs(), + color, + positionMatrix, + clip, + vertexDst); + } +#endif // defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS) + std::tuple<bool, int> regenerateAtlas(int begin, int end, RegenerateAtlasDelegate regenerateAtlas) const override { return regenerateAtlas( &fGlyphs, begin, end, fVertexFiller.grMaskType(), this->glyphSrcPadding()); } + const VertexFiller& vertexFiller() const override { return fVertexFiller; } + protected: SubRunStreamTag subRunStreamTag() const override { return SubRunStreamTag::kTransformMaskStreamTag; @@ -800,6 +1053,12 @@ protected: private: const bool fIsBigEnough; + + const VertexFiller fVertexFiller; + + // The regenerateAtlas method mutates fGlyphs. It should be called from onPrepare which must + // be single threaded. + mutable GlyphVector fGlyphs; }; // class TransformedMaskSubRun // -- SDFTSubRun ----------------------------------------------------------------------------------- @@ -812,19 +1071,52 @@ bool has_some_antialiasing(const SkFont& font ) { #if !defined(SK_DISABLE_SDF_TEXT) -class SDFTSubRun final : public AtlasSubRun { +#if defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS) + +static std::tuple<AtlasTextOp::MaskType, uint32_t, bool> calculate_sdf_parameters( + const skgpu::ganesh::SurfaceDrawContext& sdc, + const SkMatrix& drawMatrix, + bool useLCDText, + bool isAntiAliased) { + const GrColorInfo& colorInfo = sdc.colorInfo(); + const SkSurfaceProps& props = sdc.surfaceProps(); + using MT = AtlasTextOp::MaskType; + bool isLCD = useLCDText && props.pixelGeometry() != kUnknown_SkPixelGeometry; + MT maskType = !isAntiAliased ? MT::kAliasedDistanceField + : isLCD ? MT::kLCDDistanceField + : MT::kGrayscaleDistanceField; + + bool useGammaCorrectDistanceTable = colorInfo.isLinearlyBlended(); + uint32_t DFGPFlags = drawMatrix.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0; + DFGPFlags |= drawMatrix.isScaleTranslate() ? kScaleOnly_DistanceFieldEffectFlag : 0; + DFGPFlags |= useGammaCorrectDistanceTable ? kGammaCorrect_DistanceFieldEffectFlag : 0; + DFGPFlags |= MT::kAliasedDistanceField == maskType ? kAliased_DistanceFieldEffectFlag : 0; + DFGPFlags |= drawMatrix.hasPerspective() ? kPerspective_DistanceFieldEffectFlag : 0; + + if (isLCD) { + bool isBGR = SkPixelGeometryIsBGR(props.pixelGeometry()); + bool isVertical = SkPixelGeometryIsV(props.pixelGeometry()); + DFGPFlags |= kUseLCD_DistanceFieldEffectFlag; + DFGPFlags |= isBGR ? kBGR_DistanceFieldEffectFlag : 0; + DFGPFlags |= isVertical ? kPortrait_DistanceFieldEffectFlag : 0; + } + return {maskType, DFGPFlags, useGammaCorrectDistanceTable}; +} + +#endif // defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS) + +class SDFTSubRun final : public SubRun, public AtlasSubRun { public: SDFTSubRun(bool useLCDText, bool antiAliased, const SDFTMatrixRange& matrixRange, VertexFiller&& vertexFiller, GlyphVector&& glyphs) - : AtlasSubRun(std::move(vertexFiller), std::move(glyphs)) - , fUseLCDText{useLCDText} - , fAntiAliased{antiAliased} - , fMatrixRange{matrixRange} { - SkASSERT(fVertexFiller.grMaskType() == MaskFormat::kA8); - } + : fUseLCDText{useLCDText} + , fAntiAliased{antiAliased} + , fMatrixRange{matrixRange} + , fVertexFiller{std::move(vertexFiller)} + , fGlyphs{std::move(glyphs)} { } static SubRunOwner Make(SkZip<const SkPackedGlyphID, const SkPoint> accepted, const SkFont& runFont, @@ -888,8 +1180,21 @@ public: fGlyphs.packedGlyphIDToGlyph(cache); } + int glyphCount() const override { return fVertexFiller.count(); } + MaskFormat maskFormat() const override { + SkASSERT(fVertexFiller.grMaskType() == MaskFormat::kA8); + return MaskFormat::kA8; + } int glyphSrcPadding() const override { return SK_DistanceFieldInset; } + SkSpan<const Glyph*> glyphs() const override { + return fGlyphs.glyphs(); + } + + unsigned short instanceFlags() const override { + return (unsigned short)MaskFormat::kA8; + } + void draw(SkCanvas*, SkPoint drawOrigin, const SkPaint& paint, @@ -899,21 +1204,79 @@ public: {/* isSDF = */true, /* isLCD = */fUseLCDText, skgpu::MaskFormat::kA8}); } - std::tuple<bool, SkRect> deviceRectAndNeedsTransform( - const SkMatrix &positionMatrix) const override { +#if defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS) + size_t vertexStride(const SkMatrix& drawMatrix) const override { + return fVertexFiller.vertexStride(drawMatrix); + } + + std::tuple<const GrClip*, GrOp::Owner> makeAtlasTextOp( + const GrClip* clip, + const SkMatrix& viewMatrix, + SkPoint drawOrigin, + const SkPaint& paint, + sk_sp<SkRefCnt>&& subRunStorage, + skgpu::ganesh::SurfaceDrawContext* sdc) const override { + SkASSERT(this->glyphCount() != 0); + + GrPaint grPaint; + SkPMColor4f drawingColor = calculate_colors(sdc, + paint, + viewMatrix, + MaskFormat::kA8, + &grPaint); + + auto [maskType, DFGPFlags, useGammaCorrectDistanceTable] = + calculate_sdf_parameters(*sdc, viewMatrix, fUseLCDText, fAntiAliased); + + auto geometry = AtlasTextOp::Geometry::Make(*this, + viewMatrix, + drawOrigin, + SkIRect::MakeEmpty(), + std::move(subRunStorage), + drawingColor, + sdc->arenaAlloc()); + + GrRecordingContext* const rContext = sdc->recordingContext(); + SkMatrix positionMatrix = position_matrix(viewMatrix, drawOrigin); auto [_, deviceRect] = fVertexFiller.deviceRectAndCheckTransform(positionMatrix); - return {true, deviceRect}; + GrOp::Owner op = GrOp::Make<AtlasTextOp>(rContext, + maskType, + true, + this->glyphCount(), + deviceRect, + SkPaintPriv::ComputeLuminanceColor(paint), + useGammaCorrectDistanceTable, + DFGPFlags, + geometry, + std::move(grPaint)); + + return {clip, std::move(op)}; } - GlyphParams glyphParams() const override { - return { /*isSDF=*/true, fUseLCDText, /*isAA=*/fAntiAliased }; + void fillVertexData( + void *vertexDst, int offset, int count, + GrColor color, + const SkMatrix& drawMatrix, SkPoint drawOrigin, + SkIRect clip) const override { + const SkMatrix positionMatrix = position_matrix(drawMatrix, drawOrigin); + + fVertexFiller.fillVertexData(offset, count, + fGlyphs.glyphs(), + color, + positionMatrix, + clip, + vertexDst); } +#endif // defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS) + std::tuple<bool, int> regenerateAtlas(int begin, int end, RegenerateAtlasDelegate regenerateAtlas) const override { return regenerateAtlas(&fGlyphs, begin, end, MaskFormat::kA8, this->glyphSrcPadding()); } + const VertexFiller& vertexFiller() const override { return fVertexFiller; } + protected: SubRunStreamTag subRunStreamTag() const override { return SubRunStreamTag::kSDFTStreamTag; } void doFlatten(SkWriteBuffer& buffer) const override { @@ -928,6 +1291,12 @@ private: const bool fUseLCDText; const bool fAntiAliased; const SDFTMatrixRange fMatrixRange; + + const VertexFiller fVertexFiller; + + // The regenerateAtlas method mutates fGlyphs. It should be called from onPrepare which must + // be single threaded. + mutable GlyphVector fGlyphs; }; // class SDFTSubRun #endif // !defined(SK_DISABLE_SDF_TEXT) @@ -959,7 +1328,7 @@ void add_multi_mask_format( auto glyphsWithSameFormat = SkMakeZip(interval.get<0>(), interval.get<1>()); addSingleMaskFormat(glyphsWithSameFormat, format); } -} // anonymous namespace +} // namespace namespace sktext::gpu { SubRun::~SubRun() = default; @@ -1282,17 +1651,22 @@ make_sdft_strike_spec(const SkFont& font, const SkPaint& paint, // Check for dashing and adjust the intervals. if (SkPathEffect* pathEffect = paint.getPathEffect(); pathEffect != nullptr) { - if (auto info = as_PEB(pathEffect)->asADash()) { - SkSpan<const SkScalar> src = info->fIntervals; - SkASSERT(src.size() > 1); - // Allocate the intervals. - std::vector<SkScalar> scaledIntervals(src.size()); - for (size_t i = 0; i < src.size(); ++i) { - scaledIntervals[i] = src[i] / strikeToSourceScale; + SkPathEffectBase::DashInfo dashInfo; + if (as_PEB(pathEffect)->asADash(&dashInfo) == SkPathEffectBase::DashType::kDash) { + if (dashInfo.fCount > 0) { + // Allocate the intervals. + std::vector<SkScalar> scaledIntervals(dashInfo.fCount); + dashInfo.fIntervals = scaledIntervals.data(); + // Call again to get the interval data. + (void)as_PEB(pathEffect)->asADash(&dashInfo); + for (SkScalar& interval : scaledIntervals) { + interval /= strikeToSourceScale; + } + auto scaledDashes = SkDashPathEffect::Make(scaledIntervals.data(), + scaledIntervals.size(), + dashInfo.fPhase / strikeToSourceScale); + dfPaint.setPathEffect(scaledDashes); } - auto scaledDashes = SkDashPathEffect::Make(scaledIntervals, - info->fPhase / strikeToSourceScale); - dfPaint.setPathEffect(scaledDashes); } } @@ -1413,13 +1787,10 @@ SubRunContainerOwner SubRunContainer::MakeInAlloc( } } #endif // !defined(SK_DISABLE_SDF_TEXT) - // Mask filters with 3D format (e.g. EmbossMaskFilter) need to be drawn as a path - // in order to apply the filter through the Canvas AutoLayer system. - const bool needsAutoLayer = runPaint.getMaskFilter() && - as_MFB(runPaint.getMaskFilter())->getFormat() == SkMask::k3D_Format; + // Direct Mask case // Handle all the directly mapped mask subruns. - if (!source.empty() && !positionMatrix.hasPerspective() && !needsAutoLayer) { + if (!source.empty() && !positionMatrix.hasPerspective()) { // Process masks including ARGB - this should be the 99.99% case. // This will handle medium size emoji that are sharing the run with SDFT drawn text. // If things are too big they will be passed along to the drawing of last resort @@ -1553,7 +1924,7 @@ SubRunContainerOwner SubRunContainer::MakeInAlloc( // large, then the M may return 0 because its dimensions are > 65535, but // the small character produces regular result because its largest dimension // is < 65535. This will create an improper scale factor causing the M to be - // too large to fit in the atlas. Tracked by skbug.com/40044801. + // too large to fit in the atlas. Tracked by skia:13714. return maxDimension; }; @@ -1637,7 +2008,7 @@ SkSpan<SkPoint> MakePointsFromBuffer(SkReadBuffer& buffer, SubRunAllocator* allo BagOfBytes::WillCountFit<SkPoint>(glyphCount))) { return {}; } SkPoint* positionsData = alloc->makePODArray<SkPoint>(glyphCount); - if (!buffer.readPointArray({positionsData, glyphCount})) { return {}; } + if (!buffer.readPointArray(positionsData, glyphCount)) { return {}; } return {positionsData, glyphCount}; } diff --git a/gfx/skia/skia/src/text/gpu/SubRunContainer.h b/gfx/skia/skia/src/text/gpu/SubRunContainer.h @@ -9,15 +9,9 @@ #define sktext_gpu_SubRunContainer_DEFINED #include "include/core/SkMatrix.h" -#include "include/core/SkRect.h" #include "include/core/SkRefCnt.h" #include "include/core/SkSpan.h" -#include "include/private/base/SkPoint_impl.h" -#include "include/private/base/SkTLogic.h" -#include "src/core/SkColorData.h" // IWYU pragma: keep -#include "src/text/gpu/GlyphVector.h" #include "src/text/gpu/SubRunAllocator.h" -#include "src/text/gpu/VertexFiller.h" #include <cstddef> #include <functional> @@ -31,6 +25,7 @@ class SkPaint; class SkReadBuffer; class SkStrikeClient; class SkWriteBuffer; +struct SkPoint; struct SkStrikeDeviceInfo; namespace sktext { @@ -42,9 +37,22 @@ namespace skgpu { enum class MaskFormat : int; } +#if defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS) +#include "src/gpu/ganesh/GrColor.h" +#include "src/gpu/ganesh/ops/GrOp.h" + +class GrClip; +struct SkIRect; +namespace skgpu::ganesh { +class SurfaceDrawContext; +} +#endif + namespace sktext::gpu { +class GlyphVector; class Glyph; class StrikeCache; +class VertexFiller; using RegenerateAtlasDelegate = std::function<std::tuple<bool, int>(GlyphVector*, int begin, @@ -58,7 +66,57 @@ struct RendererData { skgpu::MaskFormat maskFormat; }; -class AtlasSubRun; +// -- AtlasSubRun -------------------------------------------------------------------------------- +// AtlasSubRun is the API that AtlasTextOp uses to generate vertex data for drawing. +// There are three different ways AtlasSubRun is specialized. +// * DirectMaskSubRun* - this is by far the most common type of SubRun. The mask pixels are +// in 1:1 correspondence with the pixels on the device. The destination rectangles in this +// SubRun are in device space. This SubRun handles color glyphs. +// * TransformedMaskSubRun* - handles glyph where the image in the atlas needs to be +// transformed to the screen. It is usually used for large color glyph which can't be +// drawn with paths or scaled distance fields, but will be used to draw bitmap glyphs to +// the screen, if the matrix does not map 1:1 to the screen. The destination rectangles +// are in source space. +// * SDFTSubRun* - scaled distance field text handles largish single color glyphs that still +// can fit in the atlas; the sizes between direct SubRun, and path SubRun. The destination +// rectangles are in source space. +class AtlasSubRun { +public: + virtual ~AtlasSubRun() = default; + + virtual SkSpan<const Glyph*> glyphs() const = 0; + virtual int glyphCount() const = 0; + virtual skgpu::MaskFormat maskFormat() const = 0; + virtual int glyphSrcPadding() const = 0; + virtual unsigned short instanceFlags() const = 0; + +#if defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS) + virtual size_t vertexStride(const SkMatrix& drawMatrix) const = 0; + + virtual std::tuple<const GrClip*, GrOp::Owner> makeAtlasTextOp( + const GrClip*, + const SkMatrix& viewMatrix, + SkPoint drawOrigin, + const SkPaint&, + sk_sp<SkRefCnt>&& subRunStorage, + skgpu::ganesh::SurfaceDrawContext*) const = 0; + + virtual void fillVertexData( + void* vertexDst, int offset, int count, + GrColor color, + const SkMatrix& drawMatrix, + SkPoint drawOrigin, + SkIRect clip) const = 0; +#endif + // This call is not thread safe. It should only be called from a known single-threaded env. + virtual std::tuple<bool, int> regenerateAtlas( + int begin, int end, RegenerateAtlasDelegate) const = 0; + + virtual const VertexFiller& vertexFiller() const = 0; + + virtual void testingOnly_packedGlyphIDToGlyph(StrikeCache* cache) const = 0; +}; + using AtlasDrawDelegate = std::function<void(const sktext::gpu::AtlasSubRun* subRun, SkPoint drawOrigin, const SkPaint& paint, @@ -104,79 +162,6 @@ private: SubRunOwner fNext; }; -// -- AtlasSubRun -------------------------------------------------------------------------------- -// AtlasSubRun is the API that AtlasTextOp uses to generate vertex data for drawing. -// There are three different ways AtlasSubRun is specialized. -// * DirectMaskSubRun* - this is by far the most common type of SubRun. The mask pixels are -// in 1:1 correspondence with the pixels on the device. The destination rectangles in this -// SubRun are in device space. This SubRun handles color glyphs. -// * TransformedMaskSubRun* - handles glyph where the image in the atlas needs to be -// transformed to the screen. It is usually used for large color glyph which can't be -// drawn with paths or scaled distance fields, but will be used to draw bitmap glyphs to -// the screen, if the matrix does not map 1:1 to the screen. The destination rectangles -// are in source space. -// * SDFTSubRun* - scaled distance field text handles largish single color glyphs that still -// can fit in the atlas; the sizes between direct SubRun, and path SubRun. The destination -// rectangles are in source space. -class AtlasSubRun : public SubRun { -public: - AtlasSubRun(VertexFiller&& vertexFiller, GlyphVector&& glyphs) - : fVertexFiller{std::move(vertexFiller)} - , fGlyphs{std::move(glyphs)} {} - ~AtlasSubRun() override = default; - - SkSpan<const Glyph*> glyphs() const { return fGlyphs.glyphs(); } - int glyphCount() const { return SkCount(fGlyphs.glyphs()); } - skgpu::MaskFormat maskFormat() const { return fVertexFiller.grMaskType(); } - virtual int glyphSrcPadding() const = 0; - unsigned short instanceFlags() const { return (unsigned short)this->maskFormat(); } - - virtual std::tuple<bool, SkRect> deviceRectAndNeedsTransform( - const SkMatrix &positionMatrix) const = 0; - - struct GlyphParams { - bool isSDF; - bool isLCD; - bool isAA; - }; - virtual GlyphParams glyphParams() const = 0; - - size_t vertexStride(const SkMatrix& drawMatrix) const { - return fVertexFiller.vertexStride(drawMatrix); - } - - void fillVertexData( - void* vertexDst, int offset, int count, - const SkPMColor4f& color, - const SkMatrix& drawMatrix, - SkPoint drawOrigin, - SkIRect clip) const { - SkMatrix positionMatrix = drawMatrix; - positionMatrix.preTranslate(drawOrigin.x(), drawOrigin.y()); - fVertexFiller.fillVertexData(offset, count, - fGlyphs.glyphs(), - color, - positionMatrix, - clip, - vertexDst); - } - - // This call is not thread safe. It should only be called from a known single-threaded env. - virtual std::tuple<bool, int> regenerateAtlas( - int begin, int end, RegenerateAtlasDelegate) const = 0; - - const VertexFiller& vertexFiller() const { return fVertexFiller; } - - virtual void testingOnly_packedGlyphIDToGlyph(StrikeCache* cache) const = 0; - -protected: - const VertexFiller fVertexFiller; - - // The regenerateAtlas method mutates fGlyphs. It should be called from onPrepare which must - // be single threaded. - mutable GlyphVector fGlyphs; -}; - // -- SubRunList ----------------------------------------------------------------------------------- class SubRunList { public: diff --git a/gfx/skia/skia/src/text/gpu/VertexFiller.cpp b/gfx/skia/skia/src/text/gpu/VertexFiller.cpp @@ -12,14 +12,23 @@ #include "include/core/SkRect.h" #include "include/core/SkScalar.h" #include "include/core/SkTypes.h" +#include "include/private/base/SkTLogic.h" #include "src/core/SkReadBuffer.h" #include "src/core/SkWriteBuffer.h" #include "src/gpu/AtlasTypes.h" #include "src/text/gpu/SubRunAllocator.h" #include "src/text/gpu/SubRunContainer.h" +#include <cstdint> #include <optional> +#if defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS) +#include "include/core/SkPoint3.h" +#include "src/base/SkZip.h" +#include "src/gpu/ganesh/ops/AtlasTextOp.h" +#include "src/text/gpu/Glyph.h" +#endif + using MaskFormat = skgpu::MaskFormat; namespace sktext::gpu { @@ -72,7 +81,7 @@ void VertexFiller::flatten(SkWriteBuffer &buffer) const { buffer.writeBool(fCanDrawDirect); buffer.writeMatrix(fCreationMatrix); buffer.writeRect(fCreationBounds); - buffer.writePointArray(fLeftTop); + buffer.writePointArray(fLeftTop.data(), SkCount(fLeftTop)); } SkMatrix VertexFiller::viewDifference(const SkMatrix &positionMatrix) const { @@ -85,7 +94,7 @@ SkMatrix VertexFiller::viewDifference(const SkMatrix &positionMatrix) const { // Check for integer translate with the same 2x2 matrix. // Returns the translation, and true if the change from creation matrix to the position matrix // supports using direct glyph masks. -std::tuple<bool, SkVector> VertexFiller::CanUseDirect( +static std::tuple<bool, SkVector> can_use_direct( const SkMatrix& creationMatrix, const SkMatrix& positionMatrix) { // The existing direct glyph info can be used if the creationMatrix, and the // positionMatrix have the same 2x2, the translation between them is integer, and no @@ -102,6 +111,241 @@ std::tuple<bool, SkVector> VertexFiller::CanUseDirect( translation}; } +struct AtlasPt { + uint16_t u; + uint16_t v; +}; + +#if defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS) + +// Normal text mask, SDFT, or color. +struct Mask2DVertex { + SkPoint devicePos; + GrColor color; + AtlasPt atlasPos; +}; + +struct ARGB2DVertex { + ARGB2DVertex(SkPoint d, GrColor, AtlasPt a) : devicePos{d}, atlasPos{a} {} + + SkPoint devicePos; + AtlasPt atlasPos; +}; + +// Perspective SDFT or SDFT forced to 3D or perspective color. +struct Mask3DVertex { + SkPoint3 devicePos; + GrColor color; + AtlasPt atlasPos; +}; + +struct ARGB3DVertex { + ARGB3DVertex(SkPoint3 d, GrColor, AtlasPt a) : devicePos{d}, atlasPos{a} {} + + SkPoint3 devicePos; + AtlasPt atlasPos; +}; + +size_t VertexFiller::vertexStride(const SkMatrix &matrix) const { + if (fMaskType != MaskFormat::kARGB) { + // For formats MaskFormat::kA565 and MaskFormat::kA8 where A8 include SDF. + return matrix.hasPerspective() ? sizeof(Mask3DVertex) : sizeof(Mask2DVertex); + } else { + // For format MaskFormat::kARGB + return matrix.hasPerspective() ? sizeof(ARGB3DVertex) : sizeof(ARGB2DVertex); + } +} + +// The 99% case. Direct Mask, No clip, No RGB. +void fillDirectNoClipping(SkZip<Mask2DVertex[4], const Glyph*, const SkPoint> quadData, + GrColor color, + SkPoint originOffset) { + for (auto[quad, glyph, leftTop] : quadData) { + auto[al, at, ar, ab] = glyph->fAtlasLocator.getUVs(); + SkScalar dl = leftTop.x() + originOffset.x(), + dt = leftTop.y() + originOffset.y(), + dr = dl + (ar - al), + db = dt + (ab - at); + + quad[0] = {{dl, dt}, color, {al, at}}; // L,T + quad[1] = {{dl, db}, color, {al, ab}}; // L,B + quad[2] = {{dr, dt}, color, {ar, at}}; // R,T + quad[3] = {{dr, db}, color, {ar, ab}}; // R,B + } +} + +template <typename Rect> +static auto LTBR(const Rect& r) { + return std::make_tuple(r.left(), r.top(), r.right(), r.bottom()); +} + +// Handle any combination of BW or color and clip or no clip. +template<typename Quad, typename VertexData> +static void fillDirectClipped(SkZip<Quad, const Glyph*, const VertexData> quadData, + GrColor color, + SkPoint originOffset, + SkIRect* clip = nullptr) { + for (auto[quad, glyph, leftTop] : quadData) { + auto[al, at, ar, ab] = glyph->fAtlasLocator.getUVs(); + uint16_t w = ar - al, + h = ab - at; + SkScalar l = leftTop.x() + originOffset.x(), + t = leftTop.y() + originOffset.y(); + if (clip == nullptr) { + auto[dl, dt, dr, db] = SkRect::MakeLTRB(l, t, l + w, t + h); + quad[0] = {{dl, dt}, color, {al, at}}; // L,T + quad[1] = {{dl, db}, color, {al, ab}}; // L,B + quad[2] = {{dr, dt}, color, {ar, at}}; // R,T + quad[3] = {{dr, db}, color, {ar, ab}}; // R,B + } else { + SkIRect devIRect = SkIRect::MakeLTRB(l, t, l + w, t + h); + SkScalar dl, dt, dr, db; + if (!clip->containsNoEmptyCheck(devIRect)) { + if (SkIRect clipped; clipped.intersect(devIRect, *clip)) { + al += clipped.left() - devIRect.left(); + at += clipped.top() - devIRect.top(); + ar += clipped.right() - devIRect.right(); + ab += clipped.bottom() - devIRect.bottom(); + std::tie(dl, dt, dr, db) = LTBR(clipped); + } else { + // TODO: omit generating any vertex data for fully clipped glyphs ? + std::tie(dl, dt, dr, db) = std::make_tuple(0, 0, 0, 0); + std::tie(al, at, ar, ab) = std::make_tuple(0, 0, 0, 0); + } + } else { + std::tie(dl, dt, dr, db) = LTBR(devIRect); + } + quad[0] = {{dl, dt}, color, {al, at}}; // L,T + quad[1] = {{dl, db}, color, {al, ab}}; // L,B + quad[2] = {{dr, dt}, color, {ar, at}}; // R,T + quad[3] = {{dr, db}, color, {ar, ab}}; // R,B + } + } +} + +template<typename Quad, typename VertexData> +static void fill2D(SkZip<Quad, const Glyph*, const VertexData> quadData, + GrColor color, + const SkMatrix& viewDifference) { + for (auto [quad, glyph, leftTop] : quadData) { + auto [l, t] = leftTop; + auto [r, b] = leftTop + glyph->fAtlasLocator.widthHeight(); + SkPoint lt = viewDifference.mapXY(l, t), + lb = viewDifference.mapXY(l, b), + rt = viewDifference.mapXY(r, t), + rb = viewDifference.mapXY(r, b); + auto [al, at, ar, ab] = glyph->fAtlasLocator.getUVs(); + quad[0] = {lt, color, {al, at}}; // L,T + quad[1] = {lb, color, {al, ab}}; // L,B + quad[2] = {rt, color, {ar, at}}; // R,T + quad[3] = {rb, color, {ar, ab}}; // R,B + } +} + +template<typename Quad, typename VertexData> +static void fill3D(SkZip<Quad, const Glyph*, const VertexData> quadData, + GrColor color, + const SkMatrix& viewDifference) { + auto mapXYZ = [&](SkScalar x, SkScalar y) { + SkPoint pt{x, y}; + SkPoint3 result; + viewDifference.mapHomogeneousPoints(&result, &pt, 1); + return result; + }; + for (auto [quad, glyph, leftTop] : quadData) { + auto [l, t] = leftTop; + auto [r, b] = leftTop + glyph->fAtlasLocator.widthHeight(); + SkPoint3 lt = mapXYZ(l, t), + lb = mapXYZ(l, b), + rt = mapXYZ(r, t), + rb = mapXYZ(r, b); + auto [al, at, ar, ab] = glyph->fAtlasLocator.getUVs(); + quad[0] = {lt, color, {al, at}}; // L,T + quad[1] = {lb, color, {al, ab}}; // L,B + quad[2] = {rt, color, {ar, at}}; // R,T + quad[3] = {rb, color, {ar, ab}}; // R,B + } +} + +void VertexFiller::fillVertexData(int offset, int count, + SkSpan<const Glyph*> glyphs, + GrColor color, + const SkMatrix& positionMatrix, + SkIRect clip, + void* vertexBuffer) const { + auto quadData = [&](auto dst) { + return SkMakeZip(dst, + glyphs.subspan(offset, count), + fLeftTop.subspan(offset, count)); + }; + + // Handle direct mask drawing specifically. + if (fCanDrawDirect) { + auto [noTransformNeeded, originOffset] = + can_use_direct(fCreationMatrix, positionMatrix); + + if (noTransformNeeded) { + if (clip.isEmpty()) { + if (fMaskType != MaskFormat::kARGB) { + using Quad = Mask2DVertex[4]; + SkASSERT(sizeof(Mask2DVertex) == this->vertexStride(SkMatrix::I())); + fillDirectNoClipping(quadData((Quad*)vertexBuffer), color, originOffset); + } else { + using Quad = ARGB2DVertex[4]; + SkASSERT(sizeof(ARGB2DVertex) == this->vertexStride(SkMatrix::I())); + fillDirectClipped(quadData((Quad*)vertexBuffer), color, originOffset); + } + } else { + if (fMaskType != MaskFormat::kARGB) { + using Quad = Mask2DVertex[4]; + SkASSERT(sizeof(Mask2DVertex) == this->vertexStride(SkMatrix::I())); + fillDirectClipped(quadData((Quad*)vertexBuffer), color, originOffset, &clip); + } else { + using Quad = ARGB2DVertex[4]; + SkASSERT(sizeof(ARGB2DVertex) == this->vertexStride(SkMatrix::I())); + fillDirectClipped(quadData((Quad*)vertexBuffer), color, originOffset, &clip); + } + } + return; + } + } + + // Handle the general transformed case. + SkMatrix viewDifference = this->viewDifference(positionMatrix); + if (!positionMatrix.hasPerspective()) { + if (fMaskType == MaskFormat::kARGB) { + using Quad = ARGB2DVertex[4]; + SkASSERT(sizeof(ARGB2DVertex) == this->vertexStride(positionMatrix)); + fill2D(quadData((Quad*)vertexBuffer), color, viewDifference); + } else { + using Quad = Mask2DVertex[4]; + SkASSERT(sizeof(Mask2DVertex) == this->vertexStride(positionMatrix)); + fill2D(quadData((Quad*)vertexBuffer), color, viewDifference); + } + } else { + if (fMaskType == MaskFormat::kARGB) { + using Quad = ARGB3DVertex[4]; + SkASSERT(sizeof(ARGB3DVertex) == this->vertexStride(positionMatrix)); + fill3D(quadData((Quad*)vertexBuffer), color, viewDifference); + } else { + using Quad = Mask3DVertex[4]; + SkASSERT(sizeof(Mask3DVertex) == this->vertexStride(positionMatrix)); + fill3D(quadData((Quad*)vertexBuffer), color, viewDifference); + } + } +} + +using AtlasTextOp = skgpu::ganesh::AtlasTextOp; +AtlasTextOp::MaskType VertexFiller::opMaskType() const { + switch (fMaskType) { + case MaskFormat::kA8: return AtlasTextOp::MaskType::kGrayscaleCoverage; + case MaskFormat::kA565: return AtlasTextOp::MaskType::kLCDCoverage; + case MaskFormat::kARGB: return AtlasTextOp::MaskType::kColorBitmap; + } + SkUNREACHABLE; +} +#endif // defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS) + bool VertexFiller::isLCD() const { return fMaskType == MaskFormat::kA565; } // Return true if the positionMatrix represents an integer translation. Return the device @@ -110,7 +354,8 @@ bool VertexFiller::isLCD() const { return fMaskType == MaskFormat::kA565; } std::tuple<bool, SkRect> VertexFiller::deviceRectAndCheckTransform( const SkMatrix &positionMatrix) const { if (fCanDrawDirect) { - const auto [directDrawCompatible, offset] = CanUseDirect(fCreationMatrix, positionMatrix); + const auto [directDrawCompatible, offset] = + can_use_direct(fCreationMatrix, positionMatrix); if (directDrawCompatible) { return {true, fCreationBounds.makeOffset(offset)}; diff --git a/gfx/skia/skia/src/text/gpu/VertexFiller.h b/gfx/skia/skia/src/text/gpu/VertexFiller.h @@ -15,15 +15,20 @@ #include "include/core/SkTypes.h" #include "include/private/base/SkTLogic.h" #include "src/base/SkVx.h" -#include "src/core/SkColorData.h" -#include <cstddef> #include <optional> #include <tuple> class SkReadBuffer; class SkWriteBuffer; +#if defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS) +#include "src/gpu/ganesh/GrColor.h" +#include "src/gpu/ganesh/ops/AtlasTextOp.h" + +#include <cstddef> +#endif // defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS) + namespace skgpu { enum class MaskFormat : int; @@ -75,16 +80,20 @@ public: void flatten(SkWriteBuffer &buffer) const; - // These are only available if the Ganesh backend is compiled in (see GaneshVertexFiller.cpp) +#if defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS) size_t vertexStride(const SkMatrix &matrix) const; + void fillVertexData(int offset, int count, SkSpan<const Glyph*> glyphs, - const SkPMColor4f& color, + GrColor color, const SkMatrix& positionMatrix, SkIRect clip, void* vertexBuffer) const; - // This is only available if the Graphite backend is compiled in (see GraphiteVertexFiller.cpp) + skgpu::ganesh::AtlasTextOp::MaskType opMaskType() const; +#endif // defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS) + + // This is only available if the graphite backend is compiled in (see GraphiteVertexFiller.cpp) void fillInstanceData(skgpu::graphite::DrawWriter* dw, int offset, int count, unsigned short flags, @@ -106,9 +115,6 @@ public: int count() const { return SkCount(fLeftTop); } private: - static std::tuple<bool, SkVector> CanUseDirect(const SkMatrix& creationMatrix, - const SkMatrix& positionMatrix); - SkMatrix viewDifference(const SkMatrix &positionMatrix) const; const skgpu::MaskFormat fMaskType; diff --git a/gfx/skia/skia/src/utils/SkClipStackUtils.cpp b/gfx/skia/skia/src/utils/SkClipStackUtils.cpp @@ -14,9 +14,9 @@ enum class SkClipOp; -SkPath SkClipStack_AsPath(const SkClipStack& cs) { - SkPath path; - path.setFillType(SkPathFillType::kInverseEvenOdd); +void SkClipStack_AsPath(const SkClipStack& cs, SkPath* path) { + path->reset(); + path->setFillType(SkPathFillType::kInverseEvenOdd); SkClipStack::Iter iter(cs, SkClipStack::Iter::kBottom_IterStart); while (const SkClipStack::Element* element = iter.next()) { @@ -27,19 +27,16 @@ SkPath SkClipStack_AsPath(const SkClipStack& cs) { } SkPath operand; if (element->getDeviceSpaceType() != SkClipStack::Element::DeviceSpaceType::kEmpty) { - operand = element->asDeviceSpacePath(); + element->asDeviceSpacePath(&operand); } SkClipOp elementOp = element->getOp(); if (element->isReplaceOp()) { - path = operand; + *path = operand; // TODO: Once expanding clip ops are removed, we can switch the iterator to be top // to bottom, which allows us to break here on encountering a replace op. } else { - if (auto result = Op(path, operand, (SkPathOp)elementOp)) { - path = *result; - } + Op(*path, operand, (SkPathOp)elementOp, path); } } - return path; } diff --git a/gfx/skia/skia/src/utils/SkClipStackUtils.h b/gfx/skia/skia/src/utils/SkClipStackUtils.h @@ -16,6 +16,6 @@ class SkPath; // Return the resolved clipstack as a single path. // Note: uses SkPathOps as part of its implementation. // -SkPath SkClipStack_AsPath(const SkClipStack&); +void SkClipStack_AsPath(const SkClipStack& cs, SkPath* path); #endif diff --git a/gfx/skia/skia/src/utils/SkCustomTypeface.cpp b/gfx/skia/skia/src/utils/SkCustomTypeface.cpp @@ -24,7 +24,6 @@ #include "include/core/SkRefCnt.h" #include "include/core/SkScalar.h" #include "include/core/SkSerialProcs.h" -#include "include/core/SkSpan.h" #include "include/core/SkStream.h" #include "include/core/SkString.h" #include "include/core/SkTypeface.h" @@ -40,11 +39,9 @@ #include "src/core/SkScalerContext.h" #include "src/core/SkStreamPriv.h" -#include <algorithm> #include <cstdint> #include <cstring> #include <memory> -#include <optional> #include <utility> #include <vector> @@ -102,12 +99,12 @@ private: std::unique_ptr<SkScalerContext> onCreateScalerContext(const SkScalerContextEffects&, const SkDescriptor* desc) const override; void onFilterRec(SkScalerContextRec* rec) const override; - void getGlyphToUnicodeMap(SkSpan<SkUnichar>) const override; + void getGlyphToUnicodeMap(SkUnichar* glyphToUnicode) const override; std::unique_ptr<SkAdvancedTypefaceMetrics> onGetAdvancedMetrics() const override; void onGetFontDescriptor(SkFontDescriptor* desc, bool* isLocal) const override; - void onCharsToGlyphs(SkSpan<const SkUnichar>, SkSpan<SkGlyphID>) const override; + void onCharsToGlyphs(const SkUnichar* chars, int count, SkGlyphID glyphs[]) const override; void onGetFamilyName(SkString* familyName) const override; bool onGetPostScriptName(SkString*) const override; @@ -133,12 +130,11 @@ private: void getPostScriptGlyphNames(SkString*) const override {} bool onGlyphMaskNeedsCurrentColor() const override { return false; } - int onGetVariationDesignPosition( - SkSpan<SkFontArguments::VariationPosition::Coordinate>) const override { return 0; } - int onGetVariationDesignParameters(SkSpan<SkFontParameters::Variation::Axis>) const override { - return 0; - } - int onGetTableTags(SkSpan<SkFontTableTag>) const override { return 0; } + int onGetVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate[], + int) const override { return 0; } + int onGetVariationDesignParameters(SkFontParameters::Variation::Axis[], + int) const override { return 0; } + int onGetTableTags(SkFontTableTag tags[]) const override { return 0; } size_t onGetTableData(SkFontTableTag, size_t, size_t, void*) const override { return 0; } int glyphCount() const { @@ -179,7 +175,7 @@ void SkCustomTypefaceBuilder::setGlyph(SkGlyphID index, float advance, rec.fAdvance = advance; rec.fDrawable = std::move(drawable); rec.fBounds = bounds; - rec.fPath = SkPath(); + rec.fPath.reset(); } sk_sp<SkTypeface> SkCustomTypefaceBuilder::detach() { @@ -209,9 +205,8 @@ void SkUserTypeface::onFilterRec(SkScalerContextRec* rec) const { rec->setHinting(SkFontHinting::kNone); } -void SkUserTypeface::getGlyphToUnicodeMap(SkSpan<SkUnichar> glyphToUnicode) const { - const int n = std::min(this->glyphCount(), (int)glyphToUnicode.size()); - for (int gid = 0; gid < n; ++gid) { +void SkUserTypeface::getGlyphToUnicodeMap(SkUnichar* glyphToUnicode) const { + for (int gid = 0; gid < this->glyphCount(); ++gid) { glyphToUnicode[gid] = SkTo<SkUnichar>(gid); } } @@ -225,12 +220,9 @@ void SkUserTypeface::onGetFontDescriptor(SkFontDescriptor* desc, bool* isLocal) *isLocal = true; } -void SkUserTypeface::onCharsToGlyphs(SkSpan<const SkUnichar> chars, - SkSpan<SkGlyphID> glyphs) const { - SkASSERT(chars.size() == glyphs.size()); - const int glyphCount = this->glyphCount(); - for (size_t i = 0; i < chars.size(); ++i) { - glyphs[i] = chars[i] < glyphCount ? SkTo<SkGlyphID>(chars[i]) : 0; +void SkUserTypeface::onCharsToGlyphs(const SkUnichar* chars, int count, SkGlyphID glyphs[]) const { + for (int i = 0; i < count; ++i) { + glyphs[i] = chars[i] < this->glyphCount() ? SkTo<SkGlyphID>(chars[i]) : 0; } } @@ -253,9 +245,10 @@ public: SkUserScalerContext(SkUserTypeface& face, const SkScalerContextEffects& effects, const SkDescriptor* desc) - : SkScalerContext(face, effects, desc) - , fMatrix(fRec.getSingleMatrix()) - {} + : SkScalerContext(face, effects, desc) { + fRec.getSingleMatrix(&fMatrix); + this->forceGenerateImageFromPath(); + } const SkUserTypeface* userTF() const { return static_cast<SkUserTypeface*>(this->getTypeface()); @@ -273,7 +266,7 @@ protected: } const auto& rec = tf->fGlyphRecs[gid]; - mx.advance = fMatrix.mapPoint({rec.fAdvance, 0}); + mx.advance = fMatrix.mapXY(rec.fAdvance, 0); if (rec.isDrawable()) { mx.maskFormat = SkMask::kARGB32_Format; @@ -285,18 +278,13 @@ protected: // These do not have an outline path. mx.neverRequestPath = true; - } else { - mx.computeFromPath = true; } return mx; } void generateImage(const SkGlyph& glyph, void* imageBuffer) override { const auto& rec = this->userTF()->fGlyphRecs[glyph.getGlyphID()]; - if (!rec.isDrawable()) { - this->generateImageFromPath(glyph, imageBuffer); - return; - } + SkASSERTF(rec.isDrawable(), "Only drawable-backed glyphs should reach generateImage."); auto canvas = SkCanvas::MakeRasterDirectN32(glyph.width(), glyph.height(), static_cast<SkPMColor*>(imageBuffer), @@ -313,12 +301,14 @@ protected: canvas->drawDrawable(rec.fDrawable.get(), &fMatrix); } - std::optional<SkScalerContext::GeneratedPath> generatePath(const SkGlyph& glyph) override { + bool generatePath(const SkGlyph& glyph, SkPath* path, bool* modified) override { const auto& rec = this->userTF()->fGlyphRecs[glyph.getGlyphID()]; SkASSERT(!rec.isDrawable()); - return {{rec.fPath.makeTransform(fMatrix), false}}; + rec.fPath.transform(fMatrix, path); + + return true; } sk_sp<SkDrawable> generateDrawable(const SkGlyph& glyph) override { @@ -359,12 +349,12 @@ protected: } void generateFontMetrics(SkFontMetrics* metrics) override { - auto [sx, sy] = fMatrix.mapPoint({1, 1}); + auto [sx, sy] = fMatrix.mapXY(1, 1); *metrics = scale_fontmetrics(this->userTF()->fMetrics, sx, sy); } private: - const SkMatrix fMatrix; + SkMatrix fMatrix; }; std::unique_ptr<SkScalerContext> SkUserTypeface::onCreateScalerContext( @@ -498,26 +488,25 @@ sk_sp<SkTypeface> SkCustomTypefaceBuilder::Deserialize(SkStream* stream) { } switch (gtype) { - case GlyphType::kDrawable: { - SkDeserialProcs procs; - procs.fAllowSkSL = false; - auto drawable = SkDrawable::Deserialize(data->data(), data->size(), &procs); - if (!drawable) { - return nullptr; - } - builder.setGlyph(i, advance, std::move(drawable), bounds); - } break; - case GlyphType::kPath: { - size_t bytesRead = 0; - auto path = SkPath::ReadFromMemory(data->data(), data->size(), &bytesRead); - if (path.has_value() && (bytesRead == data->size())) { - builder.setGlyph(i, advance, *path); - } else { - return nullptr; - } - } break; - default: + case GlyphType::kDrawable: { + SkDeserialProcs procs; + procs.fAllowSkSL = false; + auto drawable = SkDrawable::Deserialize(data->data(), data->size(), &procs); + if (!drawable) { + return nullptr; + } + builder.setGlyph(i, advance, std::move(drawable), bounds); + } break; + case GlyphType::kPath: { + SkPath path; + if (path.readFromMemory(data->data(), data->size()) != data->size()) { return nullptr; + } + + builder.setGlyph(i, advance, path); + } break; + default: + return nullptr; } } diff --git a/gfx/skia/skia/src/utils/SkDashPath.cpp b/gfx/skia/skia/src/utils/SkDashPath.cpp @@ -9,32 +9,32 @@ #include "include/core/SkPaint.h" #include "include/core/SkPath.h" -#include "include/core/SkPathBuilder.h" #include "include/core/SkPathMeasure.h" #include "include/core/SkPoint.h" #include "include/core/SkRect.h" #include "include/core/SkScalar.h" -#include "include/core/SkSpan.h" #include "include/core/SkStrokeRec.h" #include "include/core/SkTypes.h" #include "include/private/base/SkAlign.h" #include "include/private/base/SkFloatingPoint.h" #include "include/private/base/SkTo.h" #include "src/core/SkPathEffectBase.h" +#include "src/core/SkPathEnums.h" +#include "src/core/SkPathPriv.h" #include "src/core/SkPointPriv.h" #include <algorithm> #include <cmath> -#include <cstddef> #include <cstdint> +#include <iterator> static inline int is_even(int x) { return !(x & 1); } -static SkScalar find_first_interval(SkSpan<const SkScalar> intervals, SkScalar phase, - size_t* index) { - for (size_t i = 0; i < intervals.size(); ++i) { +static SkScalar find_first_interval(const SkScalar intervals[], SkScalar phase, + int32_t* index, int count) { + for (int i = 0; i < count; ++i) { SkScalar gap = intervals[i]; if (phase > gap || (phase == gap && gap)) { phase -= gap; @@ -51,12 +51,12 @@ static SkScalar find_first_interval(SkSpan<const SkScalar> intervals, SkScalar p return intervals[0]; } -void SkDashPath::CalcDashParameters(SkScalar phase, SkSpan<const SkScalar> intervals, - SkScalar* initialDashLength, size_t* initialDashIndex, +void SkDashPath::CalcDashParameters(SkScalar phase, const SkScalar intervals[], int32_t count, + SkScalar* initialDashLength, int32_t* initialDashIndex, SkScalar* intervalLength, SkScalar* adjustedPhase) { SkScalar len = 0; - for (SkScalar interval : intervals) { - len += interval; + for (int i = 0; i < count; i++) { + len += intervals[i]; } *intervalLength = len; // Adjust phase to be between 0 and len, "flipping" phase if negative. @@ -83,10 +83,11 @@ void SkDashPath::CalcDashParameters(SkScalar phase, SkSpan<const SkScalar> inter } SkASSERT(phase >= 0 && phase < len); - *initialDashLength = find_first_interval(intervals, phase, initialDashIndex); + *initialDashLength = find_first_interval(intervals, phase, + initialDashIndex, count); SkASSERT(*initialDashLength >= 0); - SkASSERT(*initialDashIndex < intervals.size()); + SkASSERT(*initialDashIndex >= 0 && *initialDashIndex < count); } static void outset_for_stroke(SkRect* rect, const SkStrokeRec& rec) { @@ -167,16 +168,16 @@ static bool clip_line(SkPoint pts[2], const SkRect& bounds, SkScalar intervalLen } // Handles only lines and rects. -// If cull_path() returns true, builder is the new smaller path, -// otherwise builder may have been changed but you should ignore it. +// If cull_path() returns true, dstPath is the new smaller path, +// otherwise dstPath may have been changed but you should ignore it. static bool cull_path(const SkPath& srcPath, const SkStrokeRec& rec, - const SkRect* cullRect, SkScalar intervalLength, SkPathBuilder* builder) { + const SkRect* cullRect, SkScalar intervalLength, SkPath* dstPath) { if (!cullRect) { SkPoint pts[2]; if (srcPath.isLine(pts) && pts[0] == pts[1]) { adjust_zero_length_line(pts); - builder->moveTo(pts[0]); - builder->lineTo(pts[1]); + dstPath->moveTo(pts[0]); + dstPath->lineTo(pts[1]); return true; } return false; @@ -190,8 +191,8 @@ static bool cull_path(const SkPath& srcPath, const SkStrokeRec& rec, SkPoint pts[2]; if (srcPath.isLine(pts)) { if (clip_line(pts, bounds, intervalLength, 0)) { - builder->moveTo(pts[0]); - builder->lineTo(pts[1]); + dstPath->moveTo(pts[0]); + dstPath->lineTo(pts[1]); return true; } return false; @@ -202,31 +203,30 @@ static bool cull_path(const SkPath& srcPath, const SkStrokeRec& rec, // We'll break the rect into four lines, culling each separately. SkPath::Iter iter(srcPath, false); - std::optional<SkPath::IterRec> it = iter.next(); - SkASSERT(it.has_value() && it->fVerb == SkPathVerb::kMove); + SkPoint pts[4]; // Rects are all moveTo and lineTo, so we'll only use pts[0] and pts[1]. + SkAssertResult(SkPath::kMove_Verb == iter.next(pts)); double accum = 0; // Sum of unculled edge lengths to keep the phase correct. // Intentionally a double to minimize the risk of overflow and drift. - while ((it = iter.next()) && (it->fVerb == SkPathVerb::kLine)) { + while (iter.next(pts) == SkPath::kLine_Verb) { // Notice this vector v and accum work with the original unclipped length. - SkVector v = it->fPoints[1] - it->fPoints[0]; + SkVector v = pts[1] - pts[0]; - SkPoint pts[2] = {it->fPoints[0], it->fPoints[1]}; if (clip_line(pts, bounds, intervalLength, std::fmod(accum, intervalLength))) { // pts[0] may have just been changed by clip_line(). // If that's not where we ended the previous lineTo(), we need to moveTo() there. - auto maybeLast = builder->getLastPt(); - if (!maybeLast || *maybeLast != pts[0]) { - builder->moveTo(pts[0]); + SkPoint last; + if (!dstPath->getLastPt(&last) || last != pts[0]) { + dstPath->moveTo(pts[0]); } - builder->lineTo(pts[1]); + dstPath->lineTo(pts[1]); } // We either just traveled v.fX horizontally or v.fY vertically. SkASSERT(v.fX == 0 || v.fY == 0); accum += SkScalarAbs(v.fX + v.fY); } - return !builder->isEmpty(); + return !dstPath->isEmpty(); } return false; @@ -234,7 +234,7 @@ static bool cull_path(const SkPath& srcPath, const SkStrokeRec& rec, class SpecialLineRec { public: - bool init(const SkPath& src, SkPathBuilder* dst, SkStrokeRec* rec, + bool init(const SkPath& src, SkPath* dst, SkStrokeRec* rec, int intervalCount, SkScalar intervalLength) { if (rec->isHairlineStyle() || !src.isLine(fPts)) { return false; @@ -277,7 +277,7 @@ public: return true; } - void addSegment(SkScalar d0, SkScalar d1, SkPathBuilder* path) const { + void addSegment(SkScalar d0, SkScalar d1, SkPath* path) const { SkASSERT(d0 <= fPathLength); // clamp the segment to our length if (d1 > fPathLength) { @@ -295,7 +295,7 @@ public: pts[2].set(x1 - fNormal.fX, y1 - fNormal.fY); // lineTo pts[3].set(x0 - fNormal.fX, y0 - fNormal.fY); // lineTo - path->addPolygon(pts, false); + path->addPoly(pts, std::size(pts), false); } private: @@ -306,12 +306,11 @@ private: }; -bool SkDashPath::InternalFilter(SkPathBuilder* dst, const SkPath& src, SkStrokeRec* rec, - const SkRect* cullRect, SkSpan<const SkScalar> aIntervals, - SkScalar initialDashLength, int32_t initialDashIndex, +bool SkDashPath::InternalFilter(SkPath* dst, const SkPath& src, SkStrokeRec* rec, + const SkRect* cullRect, const SkScalar aIntervals[], + int32_t count, SkScalar initialDashLength, int32_t initialDashIndex, SkScalar intervalLength, SkScalar startPhase, StrokeRecApplication strokeRecApplication) { - const size_t count = aIntervals.size(); // we must always have an even number of intervals SkASSERT(is_even(count)); @@ -321,19 +320,19 @@ bool SkDashPath::InternalFilter(SkPathBuilder* dst, const SkPath& src, SkStrokeR return false; } - const SkScalar* intervals = aIntervals.data(); + const SkScalar* intervals = aIntervals; SkScalar dashCount = 0; + int segCount = 0; - SkPathBuilder builder; SkPath cullPathStorage; const SkPath* srcPtr = &src; - if (cull_path(src, *rec, cullRect, intervalLength, &builder)) { + if (cull_path(src, *rec, cullRect, intervalLength, &cullPathStorage)) { // if rect is closed, starts in a dash, and ends in a dash, add the initial join - // potentially a better fix is described here: skbug.com/40038693 + // potentially a better fix is described here: bug.skia.org/7445 if (src.isRect(nullptr) && src.isLastContourClosed() && is_even(initialDashIndex)) { SkScalar pathLength = SkPathMeasure(src, false, rec->getResScale()).getLength(); SkScalar endPhase = SkScalarMod(pathLength + startPhase, intervalLength); - size_t index = 0; + int index = 0; while (endPhase > intervals[index]) { endPhase -= intervals[index++]; SkASSERT(index <= count); @@ -341,7 +340,7 @@ bool SkDashPath::InternalFilter(SkPathBuilder* dst, const SkPath& src, SkStrokeR // We have run out of intervals. endPhase "should" never get to this point, // but it could if the subtracts underflowed. Hence we will pin it as if it // perfectly ran through the intervals. - // See crbug.com/875494 (and skbug.com/40039544) + // See crbug.com/875494 (and skbug.com/8274) endPhase = 0; break; } @@ -365,18 +364,14 @@ bool SkDashPath::InternalFilter(SkPathBuilder* dst, const SkPath& src, SkStrokeR const SkScalar kTinyOffset = SK_ScalarNearlyZero; // scale vector to make start of tiny right angle v *= kTinyOffset; - builder.moveTo(midPoint - v); - builder.lineTo(midPoint); + cullPathStorage.moveTo(midPoint - v); + cullPathStorage.lineTo(midPoint); v = midPoint - src.getPoint(next); // scale vector to make end of tiny right angle v *= kTinyOffset; - builder.lineTo(midPoint - v); + cullPathStorage.lineTo(midPoint - v); } } - - // If PathMeasure took a SkPathRaw, we could pass it the raw from src or the builder, - // and not need to first 'detach' a path from the builder. - cullPathStorage = builder.detach(); srcPtr = &cullPathStorage; } @@ -390,7 +385,7 @@ bool SkDashPath::InternalFilter(SkPathBuilder* dst, const SkPath& src, SkStrokeR bool skipFirstSegment = meas.isClosed(); bool addedSegment = false; SkScalar length = meas.getLength(); - size_t index = initialDashIndex; + int index = initialDashIndex; // Since the path length / dash length ratio may be arbitrarily large, we can exert // significant memory pressure while attempting to build the filtered path. To avoid this, @@ -416,6 +411,7 @@ bool SkDashPath::InternalFilter(SkPathBuilder* dst, const SkPath& src, SkStrokeR addedSegment = false; if (is_even(index) && !skipFirstSegment) { addedSegment = true; + ++segCount; if (specialLine) { lineRec.addSegment(SkDoubleToScalar(distance), @@ -447,36 +443,42 @@ bool SkDashPath::InternalFilter(SkPathBuilder* dst, const SkPath& src, SkStrokeR if (meas.isClosed() && is_even(initialDashIndex) && initialDashLength >= 0) { meas.getSegment(0, initialDashLength, dst, !addedSegment); + ++segCount; } } while (meas.nextContour()); + // TODO: do we still need this? + if (segCount > 1) { + SkPathPriv::SetConvexity(*dst, SkPathConvexity::kConcave); + } + return true; } -bool SkDashPath::FilterDashPath(SkPathBuilder* dst, const SkPath& src, SkStrokeRec* rec, +bool SkDashPath::FilterDashPath(SkPath* dst, const SkPath& src, SkStrokeRec* rec, const SkRect* cullRect, const SkPathEffectBase::DashInfo& info) { - if (!ValidDashPath(info.fPhase, info.fIntervals)) { + if (!ValidDashPath(info.fPhase, info.fIntervals, info.fCount)) { return false; } SkScalar initialDashLength = 0; - size_t initialDashIndex = 0; + int32_t initialDashIndex = 0; SkScalar intervalLength = 0; - CalcDashParameters(info.fPhase, info.fIntervals, &initialDashLength, - &initialDashIndex, &intervalLength); - return InternalFilter(dst, src, rec, cullRect, info.fIntervals, initialDashLength, + CalcDashParameters(info.fPhase, info.fIntervals, info.fCount, + &initialDashLength, &initialDashIndex, &intervalLength); + return InternalFilter(dst, src, rec, cullRect, info.fIntervals, info.fCount, initialDashLength, initialDashIndex, intervalLength, info.fPhase); } -bool SkDashPath::ValidDashPath(SkScalar phase, SkSpan<const SkScalar> intervals) { - if (intervals.size() < 2 || !SkIsAlign2(intervals.size())) { +bool SkDashPath::ValidDashPath(SkScalar phase, const SkScalar intervals[], int32_t count) { + if (count < 2 || !SkIsAlign2(count)) { return false; } SkScalar length = 0; - for (SkScalar interval : intervals) { - if (interval < 0) { + for (int i = 0; i < count; i++) { + if (intervals[i] < 0) { return false; } - length += interval; + length += intervals[i]; } // watch out for values that might make us go out of bounds return length > 0 && SkIsFinite(phase, length); diff --git a/gfx/skia/skia/src/utils/SkDashPathPriv.h b/gfx/skia/skia/src/utils/SkDashPathPriv.h @@ -9,29 +9,28 @@ #define SkDashPathPriv_DEFINED #include "include/core/SkPathEffect.h" -#include "include/core/SkSpan.h" #include "src/core/SkPathEffectBase.h" namespace SkDashPath { -/** - * Calculates the initialDashLength, initialDashIndex, and intervalLength based on the - * inputed phase and intervals. If adjustedPhase is passed in, then the phase will be - * adjusted to be between 0 and intervalLength. The result will be stored in adjustedPhase. - * If adjustedPhase is nullptr then it is assumed phase is already between 0 and intervalLength - * - * Caller should have already used ValidDashPath to exclude invalid data. - */ -void CalcDashParameters(SkScalar phase, SkSpan<const SkScalar> intervals, - SkScalar* initialDashLength, size_t* initialDashIndex, - SkScalar* intervalLength, SkScalar* adjustedPhase = nullptr); + /** + * Calculates the initialDashLength, initialDashIndex, and intervalLength based on the + * inputed phase and intervals. If adjustedPhase is passed in, then the phase will be + * adjusted to be between 0 and intervalLength. The result will be stored in adjustedPhase. + * If adjustedPhase is nullptr then it is assumed phase is already between 0 and intervalLength + * + * Caller should have already used ValidDashPath to exclude invalid data. + */ + void CalcDashParameters(SkScalar phase, const SkScalar intervals[], int32_t count, + SkScalar* initialDashLength, int32_t* initialDashIndex, + SkScalar* intervalLength, SkScalar* adjustedPhase = nullptr); -bool FilterDashPath(SkPathBuilder* dst, const SkPath& src, SkStrokeRec*, const SkRect*, - const SkPathEffectBase::DashInfo& info); + bool FilterDashPath(SkPath* dst, const SkPath& src, SkStrokeRec*, const SkRect*, + const SkPathEffectBase::DashInfo& info); #ifdef SK_BUILD_FOR_FUZZER -const SkScalar kMaxDashCount = 10000; + const SkScalar kMaxDashCount = 10000; #else -const SkScalar kMaxDashCount = 1000000; + const SkScalar kMaxDashCount = 1000000; #endif /** See comments for InternalFilter */ @@ -46,14 +45,13 @@ const SkScalar kMaxDashCount = 1000000; * evaluate the dash and stroke to produce a stroked output path with a fill strokeRec. Passing * true for disallowStrokeRecApplication turns this behavior off. */ - bool InternalFilter(SkPathBuilder* dst, const SkPath& src, SkStrokeRec* rec, - const SkRect* cullRect, SkSpan<const SkScalar> aIntervals, - SkScalar initialDashLength, int32_t initialDashIndex, + bool InternalFilter(SkPath* dst, const SkPath& src, SkStrokeRec* rec, + const SkRect* cullRect, const SkScalar aIntervals[], + int32_t count, SkScalar initialDashLength, int32_t initialDashIndex, SkScalar intervalLength, SkScalar startPhase, StrokeRecApplication = StrokeRecApplication::kAllow); - bool ValidDashPath(SkScalar phase, SkSpan<const SkScalar> intervals); - + bool ValidDashPath(SkScalar phase, const SkScalar intervals[], int32_t count); } // namespace SkDashPath #endif diff --git a/gfx/skia/skia/src/utils/SkFloatUtils.h b/gfx/skia/skia/src/utils/SkFloatUtils.h @@ -170,15 +170,4 @@ private: FloatingPointUnion fU; }; -/** Interpolate along the function described by (keys[length], values[length]) - for the passed searchKey. SearchKeys outside the range keys[0]-keys[Length] - clamp to the min or max value. This function assumes the number of pairs - (length) will be small and a linear search is used. - - Repeated keys are allowed for discontinuous functions (so long as keys is - monotonically increasing). If key is the value of a repeated scalar in - keys the first one will be used. -*/ -float SkFloatInterpFunc(float searchKey, const float keys[], const float values[], int length); - #endif diff --git a/gfx/skia/skia/src/utils/SkNWayCanvas.cpp b/gfx/skia/skia/src/utils/SkNWayCanvas.cpp @@ -11,7 +11,6 @@ #include "include/core/SkColor.h" #include "include/core/SkMatrix.h" #include "include/core/SkPoint.h" -#include "include/core/SkRSXform.h" #include "include/core/SkRect.h" #include "include/core/SkRefCnt.h" #include "include/core/SkScalar.h" @@ -227,7 +226,7 @@ void SkNWayCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[ const SkPaint& paint) { Iter iter(fList); while (iter.next()) { - iter->drawPoints(mode, {pts, count}, paint); + iter->drawPoints(mode, count, pts, paint); } } @@ -313,11 +312,7 @@ void SkNWayCanvas::onDrawAtlas2(const SkImage* image, const SkRSXform xform[], c const SkPaint* paint) { Iter iter(fList); while (iter.next()) { - iter->drawAtlas(image, - {xform, count}, - {tex, count}, - {colors, colors ? count : 0}, - bmode, sampling, cull, paint); + iter->drawAtlas(image, xform, tex, colors, count, bmode, sampling, cull, paint); } } diff --git a/gfx/skia/skia/src/utils/SkOrderedFontMgr.cpp b/gfx/skia/skia/src/utils/SkOrderedFontMgr.cpp @@ -55,8 +55,7 @@ sk_sp<SkFontStyleSet> SkOrderedFontMgr::onCreateStyleSet(int index) const { sk_sp<SkFontStyleSet> SkOrderedFontMgr::onMatchFamily(const char familyName[]) const { for (const auto& fm : fList) { - const auto fs = fm->matchFamily(familyName); - if (fs->count() > 0){ + if (auto fs = fm->matchFamily(familyName)) { return fs; } } @@ -86,15 +85,6 @@ sk_sp<SkTypeface> SkOrderedFontMgr::onMatchFamilyStyleCharacter( return nullptr; } -sk_sp<SkTypeface> SkOrderedFontMgr::onLegacyMakeTypeface(const char family[], SkFontStyle style) const { - for (const auto& fm : fList) { - if (auto tf = fm->matchFamilyStyle(family, style)) { - return fm->legacyMakeTypeface(family, style); - } - } - return nullptr; -} - // All of these are defined to fail by returning null sk_sp<SkTypeface> SkOrderedFontMgr::onMakeFromData(sk_sp<SkData>, int ttcIndex) const { @@ -114,3 +104,7 @@ sk_sp<SkTypeface> SkOrderedFontMgr::onMakeFromStreamArgs(std::unique_ptr<SkStrea sk_sp<SkTypeface> SkOrderedFontMgr::onMakeFromFile(const char path[], int ttcIndex) const { return nullptr; } + +sk_sp<SkTypeface> SkOrderedFontMgr::onLegacyMakeTypeface(const char family[], SkFontStyle) const { + return nullptr; +} diff --git a/gfx/skia/skia/src/utils/SkPaintFilterCanvas.cpp b/gfx/skia/skia/src/utils/SkPaintFilterCanvas.cpp @@ -12,7 +12,6 @@ #include "include/core/SkPaint.h" #include "include/core/SkPixmap.h" #include "include/core/SkPoint.h" -#include "include/core/SkRSXform.h" #include "include/core/SkRect.h" #include "include/core/SkSurface.h" // IWYU pragma: keep #include "include/core/SkSurfaceProps.h" diff --git a/gfx/skia/skia/src/utils/SkParsePath.cpp b/gfx/skia/skia/src/utils/SkParsePath.cpp @@ -6,8 +6,6 @@ */ #include "include/core/SkPath.h" -#include "include/core/SkPathBuilder.h" -#include "include/core/SkPathTypes.h" #include "include/core/SkPoint.h" #include "include/core/SkScalar.h" #include "include/core/SkStream.h" @@ -19,6 +17,8 @@ #include <cstdio> +enum class SkPathDirection; + static inline bool is_between(int c, int min, int max) { return (unsigned)(c - min) <= (unsigned)(max - min); } @@ -104,10 +104,10 @@ static const char* find_flag(const char str[], bool* value) { return str; } -std::optional<SkPath> SkParsePath::FromSVGString(const char data[]) { +bool SkParsePath::FromSVGString(const char data[], SkPath* result) { // We will write all data to this local path and only write it // to result if the whole parsing succeeds. - SkPathBuilder builder; + SkPath path; SkPoint first = {0, 0}; SkPoint c = {0, 0}; SkPoint lastc = {0, 0}; @@ -123,7 +123,7 @@ std::optional<SkPath> SkParsePath::FromSVGString(const char data[]) { for (;;) { if (!data) { // Truncated data - return {}; + return false; } data = skip_ws(data); if (data[0] == '\0') { @@ -132,7 +132,7 @@ std::optional<SkPath> SkParsePath::FromSVGString(const char data[]) { char ch = data[0]; if (is_digit(ch) || ch == '-' || ch == '+' || ch == '.') { if (op == '\0' || op == 'Z') { - return {}; + return false; } } else if (is_sep(ch)) { data = skip_sep(data); @@ -152,14 +152,14 @@ std::optional<SkPath> SkParsePath::FromSVGString(const char data[]) { // find_points might have failed, so this might be the // previous point. However, data will be set to nullptr // if it failed, so we will check this at the top of the loop. - builder.moveTo(points[0]); + path.moveTo(points[0]); previousOp = '\0'; op = 'L'; c = points[0]; break; case 'L': // Line data = find_points(data, points, 1, relative, &c); - builder.lineTo(points[0]); + path.lineTo(points[0]); c = points[0]; break; case 'H': // Horizontal Line @@ -168,12 +168,12 @@ std::optional<SkPath> SkParsePath::FromSVGString(const char data[]) { // be set to nullptr and this lineTo is bogus but will // be ultimately ignored when the next time through the loop // detects that and bails out. - builder.lineTo(scratch, c.fY); + path.lineTo(scratch, c.fY); c.fX = scratch; break; case 'V': // Vertical Line data = find_scalar(data, &scratch, relative, c.fY); - builder.lineTo(c.fX, scratch); + path.lineTo(c.fX, scratch); c.fY = scratch; break; case 'C': // Cubic Bezier Curve @@ -187,7 +187,7 @@ std::optional<SkPath> SkParsePath::FromSVGString(const char data[]) { points[0].fY -= lastc.fY - c.fY; } cubicCommon: - builder.cubicTo(points[0], points[1], points[2]); + path.cubicTo(points[0], points[1], points[2]); lastc = points[1]; c = points[2]; break; @@ -202,7 +202,7 @@ std::optional<SkPath> SkParsePath::FromSVGString(const char data[]) { points[0].fY -= lastc.fY - c.fY; } quadraticCommon: - builder.quadTo(points[0], points[1]); + path.quadTo(points[0], points[1]); lastc = points[0]; c = points[1]; break; @@ -219,25 +219,26 @@ std::optional<SkPath> SkParsePath::FromSVGString(const char data[]) { && (data = find_flag(data, &sweep)) && (data = skip_sep(data)) && (data = find_points(data, &points[0], 1, relative, &c))) { - builder.arcTo(radii, angle, (SkPathBuilder::ArcSize) largeArc, + path.arcTo(radii, angle, (SkPath::ArcSize) largeArc, (SkPathDirection) !sweep, points[0]); - c = builder.points().back(); + path.getLastPt(&c); } } break; case 'Z': // Close Path - builder.close(); + path.close(); c = first; break; default: - return {}; + return false; } if (previousOp == 0) { first = c; } previousOp = op; } - - return builder.detach(); + // we're good, go ahead and swap in the result + result->swap(path); + return true; } /////////////////////////////////////////////////////////////////////////////// @@ -268,39 +269,40 @@ SkString SkParsePath::ToSVGString(const SkPath& path, PathEncoding encoding) { current_point = pts[count - 1] * rel_selector; }; - SkPath::Iter iter(path, false); + SkPath::Iter iter(path, false); + SkPoint pts[4]; - while (auto rec = iter.next()) { - SkSpan<const SkPoint> pts = rec->fPoints; - switch (rec->fVerb) { - case SkPathVerb::kConic: { + for (;;) { + switch (iter.next(pts)) { + case SkPath::kConic_Verb: { const SkScalar tol = SK_Scalar1 / 1024; // how close to a quad SkAutoConicToQuads quadder; - const SkPoint* quadPts = quadder.computeQuads(pts.data(), rec->conicWeight(), tol); + const SkPoint* quadPts = quadder.computeQuads(pts, iter.conicWeight(), tol); for (int i = 0; i < quadder.countQuads(); ++i) { append_command('Q', &quadPts[i*2 + 1], 2); } } break; - case SkPathVerb::kMove: + case SkPath::kMove_Verb: append_command('M', &pts[0], 1); break; - case SkPathVerb::kLine: + case SkPath::kLine_Verb: append_command('L', &pts[1], 1); break; - case SkPathVerb::kQuad: + case SkPath::kQuad_Verb: append_command('Q', &pts[1], 2); break; - case SkPathVerb::kCubic: + case SkPath::kCubic_Verb: append_command('C', &pts[1], 3); break; - case SkPathVerb::kClose: + case SkPath::kClose_Verb: stream.write("Z", 1); break; + case SkPath::kDone_Verb: { + SkString str; + str.resize(stream.bytesWritten()); + stream.copyTo(str.data()); + return str; + } } } - - SkString str; - str.resize(stream.bytesWritten()); - stream.copyTo(str.data()); - return str; } diff --git a/gfx/skia/skia/src/utils/SkPatchUtils.cpp b/gfx/skia/skia/src/utils/SkPatchUtils.cpp @@ -183,19 +183,19 @@ SkISize SkPatchUtils::GetLevelOfDetail(const SkPoint cubics[12], const SkMatrix* // Approximate length of each cubic. SkPoint pts[kNumPtsCubic]; SkPatchUtils::GetTopCubic(cubics, pts); - matrix->mapPoints(pts); + matrix->mapPoints(pts, kNumPtsCubic); SkScalar topLength = approx_arc_length(pts, kNumPtsCubic); SkPatchUtils::GetBottomCubic(cubics, pts); - matrix->mapPoints(pts); + matrix->mapPoints(pts, kNumPtsCubic); SkScalar bottomLength = approx_arc_length(pts, kNumPtsCubic); SkPatchUtils::GetLeftCubic(cubics, pts); - matrix->mapPoints(pts); + matrix->mapPoints(pts, kNumPtsCubic); SkScalar leftLength = approx_arc_length(pts, kNumPtsCubic); SkPatchUtils::GetRightCubic(cubics, pts); - matrix->mapPoints(pts); + matrix->mapPoints(pts, kNumPtsCubic); SkScalar rightLength = approx_arc_length(pts, kNumPtsCubic); if (topLength < 0 || bottomLength < 0 || leftLength < 0 || rightLength < 0) { diff --git a/gfx/skia/skia/src/utils/SkPatchUtils.h b/gfx/skia/skia/src/utils/SkPatchUtils.h @@ -22,7 +22,7 @@ class SkPatchUtils { public: // Enums for control points based on the order specified in the constructor (clockwise). enum { - kNumCtrlPts = 12, + kNumCtrlPts = 12, kNumCorners = 4, kNumPtsCubic = 4 }; diff --git a/gfx/skia/skia/src/utils/SkPolyUtils.cpp b/gfx/skia/skia/src/utils/SkPolyUtils.cpp @@ -1641,7 +1641,7 @@ bool SkTriangulateSimplePolygon(const SkPoint* polygonVerts, uint16_t* indexMap, // get bounds SkRect bounds; - if (!bounds.setBoundsCheck({polygonVerts, polygonSize})) { + if (!bounds.setBoundsCheck(polygonVerts, polygonSize)) { return false; } // get winding direction diff --git a/gfx/skia/skia/src/utils/SkShaderUtils.cpp b/gfx/skia/skia/src/utils/SkShaderUtils.cpp @@ -14,8 +14,6 @@ #include "src/sksl/SkSLString.h" #include <cstddef> -#include <iomanip> -#include <sstream> using namespace skia_private; @@ -255,25 +253,6 @@ void VisitLineByLine(const std::string& text, } } -std::string SpirvAsHexStream(SkSpan<const uint32_t> spirv) { - std::ostringstream result; - result << "Paste the following SPIR-V binary in https://www.khronos.org/spir/visualizer/\n"; - result << " or pass to `spirv-dis` (optionally with `--comment --nested-indent`)\n"; - - constexpr size_t kIndicesPerRow = 10; - size_t rowOffset = 0; - for (size_t index = 0; index < spirv.size(); ++index, ++rowOffset) { - if (rowOffset == kIndicesPerRow) { - result << "\n"; - rowOffset = 0; - } - result << "0x" << std::uppercase << std::setfill('0') << std::setw(8) << std::hex - << spirv[index] << ","; - } - - return result.str(); -} - std::string BuildShaderErrorMessage(const char* shader, const char* errors) { std::string abortText{"Shader compilation error\n" "------------------------\n"}; diff --git a/gfx/skia/skia/src/utils/SkShaderUtils.h b/gfx/skia/skia/src/utils/SkShaderUtils.h @@ -8,7 +8,6 @@ #ifndef SkShaderUtils_DEFINED #define SkShaderUtils_DEFINED -#include "include/core/SkSpan.h" #include "include/private/base/SkDebug.h" #include <cstdint> @@ -31,9 +30,6 @@ inline void PrintLineByLine(const std::string& text) { }); } -// Prints binary shaders one line at the time. This ensures they don't get truncated by the adb log. -std::string SpirvAsHexStream(SkSpan<const uint32_t> spirv); - // Combines raw shader and error text into an easier-to-read error message with line numbers. std::string BuildShaderErrorMessage(const char* shader, const char* errors); diff --git a/gfx/skia/skia/src/utils/SkShadowTessellator.cpp b/gfx/skia/skia/src/utils/SkShadowTessellator.cpp @@ -270,6 +270,8 @@ bool SkBaseShadowTessellator::computeConvexShadow(SkScalar inset, SkScalar outse SkScalar minDistSq = SkPointPriv::DistanceToLineSegmentBetweenSqd(fCentroid, fPathPolygon[0], fPathPolygon[1]); + SkRect bounds; + bounds.setBounds(&fPathPolygon[0], fPathPolygon.size()); for (int i = 1; i < fPathPolygon.size(); ++i) { int j = i + 1; if (i == fPathPolygon.size() - 1) { @@ -781,7 +783,7 @@ void SkBaseShadowTessellator::handleLine(const SkPoint& p) { } void SkBaseShadowTessellator::handleLine(const SkMatrix& m, SkPoint* p) { - *p = m.mapPoint(*p); + m.mapPoints(p, 1); this->handleLine(*p); } @@ -812,12 +814,12 @@ void SkBaseShadowTessellator::handleQuad(const SkPoint pts[3]) { } void SkBaseShadowTessellator::handleQuad(const SkMatrix& m, SkPoint pts[3]) { - m.mapPoints({pts, 3}); + m.mapPoints(pts, 3); this->handleQuad(pts); } void SkBaseShadowTessellator::handleCubic(const SkMatrix& m, SkPoint pts[4]) { - m.mapPoints({pts, 4}); + m.mapPoints(pts, 4); #if defined(SK_GANESH) // TODO: Pull PathUtils out of Ganesh? int maxCount = GrPathUtils::cubicPointCount(pts, kCubicTolerance); @@ -841,7 +843,7 @@ void SkBaseShadowTessellator::handleConic(const SkMatrix& m, SkPoint pts[3], SkS if (m.hasPerspective()) { w = SkConic::TransformW(pts, w, m); } - m.mapPoints({pts, 3}); + m.mapPoints(pts, 3); SkAutoConicToQuads quadder; const SkPoint* quads = quadder.computeQuads(pts, w, kConicTolerance); SkPoint lastPoint = *(quads++); @@ -955,46 +957,40 @@ SkAmbientShadowTessellator::SkAmbientShadowTessellator(const SkPath& path, } } -static inline void spancpy(SkSpan<SkPoint> dst, SkSpan<const SkPoint> src) { - SkASSERT(dst.size() >= src.size()); - for (size_t i = 0; i < src.size(); ++i) { - dst[i] = src[i]; - } -} - bool SkAmbientShadowTessellator::computePathPolygon(const SkPath& path, const SkMatrix& ctm) { fPathPolygon.reserve(path.countPoints()); // walk around the path, tessellate and generate outer ring // if original path is transparent, will accumulate sum of points for centroid SkPath::Iter iter(path, true); + SkPoint pts[4]; + SkPath::Verb verb; bool verbSeen = false; bool closeSeen = false; - while (auto rec = iter.next()) { + while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { if (closeSeen) { return false; } - SkPoint pts[4]; // max needed - spancpy(pts, rec->fPoints); // need a writable copy - switch (rec->fVerb) { - case SkPathVerb::kLine: + switch (verb) { + case SkPath::kLine_Verb: this->handleLine(ctm, &pts[1]); break; - case SkPathVerb::kQuad: + case SkPath::kQuad_Verb: this->handleQuad(ctm, pts); break; - case SkPathVerb::kCubic: + case SkPath::kCubic_Verb: this->handleCubic(ctm, pts); break; - case SkPathVerb::kConic: - this->handleConic(ctm, pts, rec->conicWeight()); + case SkPath::kConic_Verb: + this->handleConic(ctm, pts, iter.conicWeight()); break; - case SkPathVerb::kMove: + case SkPath::kMove_Verb: if (verbSeen) { return false; } break; - case SkPathVerb::kClose: + case SkPath::kClose_Verb: + case SkPath::kDone_Verb: closeSeen = true; break; } @@ -1080,7 +1076,9 @@ bool SkSpotShadowTessellator::computeClipAndPathPolygons(const SkPath& path, con // Will also accumulate sum of areas for centroid. // For Bezier curves, we compute additional interior points on curve. SkPath::Iter iter(path, true); + SkPoint pts[4]; SkPoint clipPts[4]; + SkPath::Verb verb; // coefficients to compute cubic Bezier at t = 5/16 static constexpr SkScalar kA = 0.32495117187f; @@ -1089,22 +1087,21 @@ bool SkSpotShadowTessellator::computeClipAndPathPolygons(const SkPath& path, con static constexpr SkScalar kD = 0.03051757812f; SkPoint curvePoint; + SkScalar w; bool closeSeen = false; bool verbSeen = false; - while (auto rec = iter.next()) { + while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { if (closeSeen) { return false; } - SkPoint pts[4]; // max needed - spancpy(pts, rec->fPoints); // need a writable copy - switch (rec->fVerb) { - case SkPathVerb::kLine: - clipPts[0] = ctm.mapPoint(pts[1]); + switch (verb) { + case SkPath::kLine_Verb: + ctm.mapPoints(clipPts, &pts[1], 1); this->addToClip(clipPts[0]); this->handleLine(shadowTransform, &pts[1]); break; - case SkPathVerb::kQuad: - ctm.mapPoints({clipPts, 3}, {pts, 3}); + case SkPath::kQuad_Verb: + ctm.mapPoints(clipPts, pts, 3); // point at t = 1/2 curvePoint.fX = 0.25f*clipPts[0].fX + 0.5f*clipPts[1].fX + 0.25f*clipPts[2].fX; curvePoint.fY = 0.25f*clipPts[0].fY + 0.5f*clipPts[1].fY + 0.25f*clipPts[2].fY; @@ -1112,9 +1109,9 @@ bool SkSpotShadowTessellator::computeClipAndPathPolygons(const SkPath& path, con this->addToClip(clipPts[2]); this->handleQuad(shadowTransform, pts); break; - case SkPathVerb::kConic: { - ctm.mapPoints({clipPts, 3}, {pts, 3}); - const float w = rec->conicWeight(); + case SkPath::kConic_Verb: + ctm.mapPoints(clipPts, pts, 3); + w = iter.conicWeight(); // point at t = 1/2 curvePoint.fX = 0.25f*clipPts[0].fX + w*0.5f*clipPts[1].fX + 0.25f*clipPts[2].fX; curvePoint.fY = 0.25f*clipPts[0].fY + w*0.5f*clipPts[1].fY + 0.25f*clipPts[2].fY; @@ -1122,9 +1119,9 @@ bool SkSpotShadowTessellator::computeClipAndPathPolygons(const SkPath& path, con this->addToClip(curvePoint); this->addToClip(clipPts[2]); this->handleConic(shadowTransform, pts, w); - } break; - case SkPathVerb::kCubic: - ctm.mapPoints({clipPts, 4}, {pts, 4}); + break; + case SkPath::kCubic_Verb: + ctm.mapPoints(clipPts, pts, 4); // point at t = 5/16 curvePoint.fX = kA*clipPts[0].fX + kB*clipPts[1].fX + kC*clipPts[2].fX + kD*clipPts[3].fX; @@ -1140,14 +1137,17 @@ bool SkSpotShadowTessellator::computeClipAndPathPolygons(const SkPath& path, con this->addToClip(clipPts[3]); this->handleCubic(shadowTransform, pts); break; - case SkPathVerb::kMove: + case SkPath::kMove_Verb: if (verbSeen) { return false; } break; - case SkPathVerb::kClose: + case SkPath::kClose_Verb: + case SkPath::kDone_Verb: closeSeen = true; break; + default: + SkDEBUGFAIL("unknown verb"); } verbSeen = true; } diff --git a/gfx/skia/skia/src/utils/SkShadowUtils.cpp b/gfx/skia/skia/src/utils/SkShadowUtils.cpp @@ -165,7 +165,7 @@ struct SpotVerticesFactory { noTrans[SkMatrix::kMTransX] = 0; noTrans[SkMatrix::kMTransY] = 0; SkPoint devCenter(fLocalCenter); - devCenter = noTrans.mapPoint(devCenter); + noTrans.mapPoints(&devCenter, 1); SkPoint3 centerLightPos = SkPoint3::Make(devCenter.fX, devCenter.fY, fDevLightPos.fZ); *translate = fOffset; return SkShadowTessellator::MakeSpot(path, noTrans, zParams, @@ -469,7 +469,7 @@ bool draw_shadow(const FACTORY& factory, SkColorFilters::Blend(color, SkBlendMode::kModulate)->makeComposed( SkColorFilterPriv::MakeGaussian())); - drawProc(vertices.get(), SkBlendMode::kDst, paint, + drawProc(vertices.get(), SkBlendMode::kModulate, paint, context.fTranslate.fX, context.fTranslate.fY, path.viewMatrix().hasPerspective()); return true; @@ -548,7 +548,7 @@ static bool fill_shadow_rec(const SkPath& path, const SkPoint3& zPlaneParams, if (!ctm.invert(&inverse)) { return false; } - pt = inverse.mapPoint(pt); + inverse.mapPoints(&pt, 1); } rec->fZPlaneParams = zPlaneParams; @@ -596,12 +596,13 @@ static bool validate_rec(const SkDrawShadowRec& rec) { SkIsFinite(rec.fLightRadius); } -void SkDevice::drawShadow(SkCanvas* canvas, const SkPath& path, const SkDrawShadowRec& rec) { +void SkDevice::drawShadow(const SkPath& path, const SkDrawShadowRec& rec) { if (!validate_rec(rec)) { return; } SkMatrix viewMatrix = this->localToDevice(); + SkAutoDeviceTransformRestore adr(this, SkM44()); #if !defined(SK_ENABLE_OPTIMIZE_SIZE) auto drawVertsProc = [this](const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint, @@ -634,13 +635,11 @@ void SkDevice::drawShadow(SkCanvas* canvas, const SkPath& path, const SkDrawShad SkPoint3 zPlaneParams = rec.fZPlaneParams; SkPoint3 devLightPos = rec.fLightPos; if (!directional) { - viewMatrix.mapPoints({(SkPoint*)&devLightPos.fX, 1}); + viewMatrix.mapPoints((SkPoint*)&devLightPos.fX, 1); } float lightRadius = rec.fLightRadius; if (SkColorGetA(rec.fAmbientColor) > 0) { - SkAutoDeviceTransformRestore adr(this, SkM44()); - bool success = false; #if !defined(SK_ENABLE_OPTIMIZE_SIZE) if (uncached && !useBlur) { @@ -659,7 +658,7 @@ void SkDevice::drawShadow(SkCanvas* canvas, const SkPath& path, const SkDrawShad // 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. this->drawVertices(vertices.get(), - SkBlender::Mode(SkBlendMode::kDst), + SkBlender::Mode(SkBlendMode::kModulate), paint, /*skipColorXform=*/true); success = true; @@ -684,7 +683,8 @@ void SkDevice::drawShadow(SkCanvas* canvas, const SkPath& path, const SkDrawShad // All else has failed, draw with blur if (!success) { // Pretransform the path to avoid transforming the stroke, below. - SkPath devSpacePath = path.makeTransform(canvas->getLocalToDeviceAs3x3()); + SkPath devSpacePath; + path.transform(viewMatrix, &devSpacePath); devSpacePath.setIsVolatile(true); // The tesselator outsets by AmbientBlurRadius (or 'r') to get the outer ring of @@ -719,22 +719,18 @@ void SkDevice::drawShadow(SkCanvas* canvas, const SkPath& path, const SkDrawShad SkScalar strokeWidth = 0.5f*(devSpaceOutset - blurRadius); // Now draw with blur - SkAutoCanvasRestore autoRestore(canvas, /*doSave=*/true); - canvas->setMatrix(SkM44()); SkPaint paint; paint.setColor(rec.fAmbientColor); paint.setStrokeWidth(strokeWidth); paint.setStyle(SkPaint::kStrokeAndFill_Style); SkScalar sigma = SkBlurMask::ConvertRadiusToSigma(blurRadius); - paint.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, sigma, - /*respectCTM=*/false)); - canvas->drawPath(devSpacePath, paint); + bool respectCTM = false; + paint.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, sigma, respectCTM)); + this->drawPath(devSpacePath, paint, true); } } if (SkColorGetA(rec.fSpotColor) > 0) { - SkAutoDeviceTransformRestore adr(this, SkM44()); - bool success = false; #if !defined(SK_ENABLE_OPTIMIZE_SIZE) if (uncached && !useBlur) { @@ -755,7 +751,7 @@ void SkDevice::drawShadow(SkCanvas* canvas, const SkPath& path, const SkDrawShad // 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. this->drawVertices(vertices.get(), - SkBlender::Mode(SkBlendMode::kDst), + SkBlender::Mode(SkBlendMode::kModulate), paint, /*skipColorXform=*/true); success = true; @@ -770,7 +766,7 @@ void SkDevice::drawShadow(SkCanvas* canvas, const SkPath& path, const SkDrawShad SkPoint center = SkPoint::Make(path.getBounds().centerX(), path.getBounds().centerY()); factory.fLocalCenter = center; - center = viewMatrix.mapPoint(center); + viewMatrix.mapPoints(&center, 1); SkScalar radius, scale; if (SkToBool(rec.fFlags & kDirectionalLight_ShadowFlag)) { SkDrawShadowMetrics::GetDirectionalParams(zPlaneParams.fZ, devLightPos.fX, @@ -837,22 +833,19 @@ void SkDevice::drawShadow(SkCanvas* canvas, const SkPath& path, const SkDrawShad SkMatrix shadowMatrix; SkScalar radius; if (!SkDrawShadowMetrics::GetSpotShadowTransform(devLightPos, lightRadius, - canvas->getLocalToDeviceAs3x3(), - zPlaneParams, + viewMatrix, zPlaneParams, path.getBounds(), directional, &shadowMatrix, &radius)) { return; } - SkAutoCanvasRestore autoRestore(canvas, /*doSave=*/true); + SkAutoDeviceTransformRestore adr2(this, SkM44(shadowMatrix)); - // And draw with blur - canvas->setMatrix(shadowMatrix); SkPaint paint; paint.setColor(rec.fSpotColor); SkScalar sigma = SkBlurMask::ConvertRadiusToSigma(radius); - paint.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, sigma, - /*respectCTM=*/false)); - canvas->drawPath(path, paint); + bool respectCTM = false; + paint.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, sigma, respectCTM)); + this->drawPath(path, paint, false); } } } diff --git a/gfx/skia/skia/src/utils/SkTextUtils.cpp b/gfx/skia/skia/src/utils/SkTextUtils.cpp @@ -11,7 +11,6 @@ #include "include/core/SkFont.h" #include "include/core/SkMatrix.h" #include "include/core/SkPath.h" -#include "include/core/SkPathBuilder.h" #include "include/core/SkPoint.h" #include "include/core/SkScalar.h" #include "include/core/SkTextBlob.h" @@ -40,22 +39,22 @@ void SkTextUtils::GetPath(const void* text, size_t length, SkTextEncoding encodi SkScalar x, SkScalar y, const SkFont& font, SkPath* path) { SkAutoToGlyphs ag(font, text, length, encoding); AutoTArray<SkPoint> pos(ag.count()); - font.getPos(ag, pos, {x, y}); + font.getPos(ag.glyphs(), ag.count(), pos.get(), {x, y}); struct Rec { - SkPathBuilder fDst; + SkPath* fDst; const SkPoint* fPos; - } rec = { {}, pos.get() }; + } rec = { path, pos.get() }; - font.getPaths(ag, [](const SkPath* src, const SkMatrix& mx, void* ctx) { + path->reset(); + font.getPaths(ag.glyphs(), ag.count(), [](const SkPath* src, const SkMatrix& mx, void* ctx) { Rec* rec = (Rec*)ctx; if (src) { SkMatrix m(mx); m.postTranslate(rec->fPos->fX, rec->fPos->fY); - rec->fDst.addPath(*src, m); + rec->fDst->addPath(*src, m); } rec->fPos += 1; }, &rec); - *path = rec.fDst.detach(); } diff --git a/gfx/skia/skia/src/utils/win/SkDWriteGeometrySink.cpp b/gfx/skia/skia/src/utils/win/SkDWriteGeometrySink.cpp @@ -8,7 +8,7 @@ #include "include/core/SkTypes.h" #if defined(SK_BUILD_FOR_WIN) -#include "include/core/SkPathBuilder.h" +#include "include/core/SkPath.h" #include "src/utils/SkFloatUtils.h" #include "src/utils/win/SkDWriteGeometrySink.h" #include "src/utils/win/SkObjBase.h" @@ -16,8 +16,8 @@ #include <dwrite.h> #include <d2d1.h> -SkDWriteGeometrySink::SkDWriteGeometrySink(SkPathBuilder* builder) - : fRefCount{1}, fBuilder{builder}, fStarted{false}, fCurrent{0,0} {} +SkDWriteGeometrySink::SkDWriteGeometrySink(SkPath* path) + : fRefCount{1}, fPath{path}, fStarted{false}, fCurrent{0,0} {} SkDWriteGeometrySink::~SkDWriteGeometrySink() { } @@ -50,10 +50,10 @@ SK_STDMETHODIMP_(ULONG) SkDWriteGeometrySink::Release(void) { SK_STDMETHODIMP_(void) SkDWriteGeometrySink::SetFillMode(D2D1_FILL_MODE fillMode) { switch (fillMode) { case D2D1_FILL_MODE_ALTERNATE: - fBuilder->setFillType(SkPathFillType::kEvenOdd); + fPath->setFillType(SkPathFillType::kEvenOdd); break; case D2D1_FILL_MODE_WINDING: - fBuilder->setFillType(SkPathFillType::kWinding); + fPath->setFillType(SkPathFillType::kWinding); break; default: SkDEBUGFAIL("Unknown D2D1_FILL_MODE."); @@ -79,7 +79,7 @@ SK_STDMETHODIMP_(void) SkDWriteGeometrySink::AddLines(const D2D1_POINT_2F *point for (const D2D1_POINT_2F *end = &points[pointsCount]; points < end; ++points) { if (this->currentIsNot(*points)) { this->goingTo(*points); - fBuilder->lineTo(points->x, points->y); + fPath->lineTo(points->x, points->y); } } } @@ -126,12 +126,12 @@ SK_STDMETHODIMP_(void) SkDWriteGeometrySink::AddBeziers(const D2D1_BEZIER_SEGMEN this->goingTo(beziers->point3); Point quadraticP1; if (check_quadratic(cubic, quadraticP1)) { - fBuilder->quadTo(quadraticP1.x, quadraticP1.y, - beziers->point3.x, beziers->point3.y); + fPath->quadTo( quadraticP1.x, quadraticP1.y, + beziers->point3.x, beziers->point3.y); } else { - fBuilder->cubicTo(beziers->point1.x, beziers->point1.y, - beziers->point2.x, beziers->point2.y, - beziers->point3.x, beziers->point3.y); + fPath->cubicTo(beziers->point1.x, beziers->point1.y, + beziers->point2.x, beziers->point2.y, + beziers->point3.x, beziers->point3.y); } } } @@ -139,7 +139,7 @@ SK_STDMETHODIMP_(void) SkDWriteGeometrySink::AddBeziers(const D2D1_BEZIER_SEGMEN SK_STDMETHODIMP_(void) SkDWriteGeometrySink::EndFigure(D2D1_FIGURE_END figureEnd) { if (fStarted) { - fBuilder->close(); + fPath->close(); } } @@ -147,8 +147,8 @@ SK_STDMETHODIMP SkDWriteGeometrySink::Close() { return S_OK; } -HRESULT SkDWriteGeometrySink::Create(SkPathBuilder* builder, IDWriteGeometrySink** geometryToPath) { - *geometryToPath = new SkDWriteGeometrySink(builder); +HRESULT SkDWriteGeometrySink::Create(SkPath* path, IDWriteGeometrySink** geometryToPath) { + *geometryToPath = new SkDWriteGeometrySink(path); return S_OK; } diff --git a/gfx/skia/skia/src/utils/win/SkDWriteGeometrySink.h b/gfx/skia/skia/src/utils/win/SkDWriteGeometrySink.h @@ -8,23 +8,25 @@ #ifndef SkDWriteToPath_DEFINED #define SkDWriteToPath_DEFINED -#include "include/core/SkPathBuilder.h" +#include "include/core/SkTypes.h" #include "src/utils/win/SkObjBase.h" +class SkPath; + #include <dwrite.h> #include <d2d1.h> class SkDWriteGeometrySink : public IDWriteGeometrySink { private: LONG fRefCount; - SkPathBuilder* fBuilder; + SkPath* fPath; bool fStarted; D2D1_POINT_2F fCurrent; void goingTo(const D2D1_POINT_2F pt) { if (!fStarted) { fStarted = true; - fBuilder->moveTo(fCurrent.x, fCurrent.y); + fPath->moveTo(fCurrent.x, fCurrent.y); } fCurrent = pt; } @@ -34,7 +36,7 @@ private: } protected: - explicit SkDWriteGeometrySink(SkPathBuilder*); + explicit SkDWriteGeometrySink(SkPath* path); virtual ~SkDWriteGeometrySink(); public: @@ -50,7 +52,7 @@ public: SK_STDMETHODIMP_(void) EndFigure(D2D1_FIGURE_END figureEnd) override; SK_STDMETHODIMP Close() override; - static HRESULT Create(SkPathBuilder*, IDWriteGeometrySink** geometryToPath); + static HRESULT Create(SkPath* path, IDWriteGeometrySink** geometryToPath); }; #endif diff --git a/gfx/skia/skia/src/utils/win/SkHRESULT.cpp b/gfx/skia/skia/src/utils/win/SkHRESULT.cpp @@ -14,7 +14,7 @@ void SkTraceHR(const char* file, unsigned long line, HRESULT hr, const char* msg if (msg) { SkDebugf("%s\n", msg); } - SkDebugf("%s(%lu) : error 0x%lx: ", file, line, static_cast<unsigned long>(hr)); + SkDebugf("%s(%lu) : error 0x%lx: ", file, line, hr); LPSTR errorText = nullptr; FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | diff --git a/gfx/thebes/gfxFcPlatformFontList.cpp b/gfx/thebes/gfxFcPlatformFontList.cpp @@ -2967,28 +2967,6 @@ void gfxFcPlatformFontList::ClearSystemFontOptions() { cairo_font_options_destroy(mSystemFontOptions); mSystemFontOptions = nullptr; } - Factory::SetSubpixelOrder(SubpixelOrder::UNKNOWN); -} - -static void SetSubpixelOrderFromCairo(const cairo_font_options_t* aOptions) { - SubpixelOrder subpixelOrder = SubpixelOrder::UNKNOWN; - switch (cairo_font_options_get_subpixel_order(aOptions)) { - case CAIRO_SUBPIXEL_ORDER_RGB: - subpixelOrder = SubpixelOrder::RGB; - break; - case CAIRO_SUBPIXEL_ORDER_BGR: - subpixelOrder = SubpixelOrder::BGR; - break; - case CAIRO_SUBPIXEL_ORDER_VRGB: - subpixelOrder = SubpixelOrder::VRGB; - break; - case CAIRO_SUBPIXEL_ORDER_VBGR: - subpixelOrder = SubpixelOrder::VBGR; - break; - default: - break; - } - Factory::SetSubpixelOrder(subpixelOrder); } bool gfxFcPlatformFontList::UpdateSystemFontOptions() { @@ -3026,8 +3004,6 @@ bool gfxFcPlatformFontList::UpdateSystemFontOptions() { return false; } - SetSubpixelOrderFromCairo(options); - ClearSystemFontOptions(); mSystemFontOptions = newOptions; return true; @@ -3059,7 +3035,6 @@ void gfxFcPlatformFontList::UpdateSystemFontOptionsFromIpc( cairo_font_options_set_subpixel_order( mSystemFontOptions, cairo_subpixel_order_t(aOptions.subpixelOrder())); mFreetypeLcdSetting = aOptions.lcdFilter(); - SetSubpixelOrderFromCairo(mSystemFontOptions); } void gfxFcPlatformFontList::SubstituteSystemFontOptions(FcPattern* aPattern) {