Unverified Commit 8ea278fd authored by Ivan Matkov's avatar Ivan Matkov Committed by GitHub

Update skia to m144 (#1154)

Fixes [SKIKO-1094](https://youtrack.jetbrains.com/issue/SKIKO-1094)
Update skia to m144
parent 26b421ed
...@@ -90,6 +90,7 @@ runs: ...@@ -90,6 +90,7 @@ runs:
docker run --rm \ docker run --rm \
-v "${{ github.workspace }}":/workspace \ -v "${{ github.workspace }}":/workspace \
-v "${HOME}/.gradle":/gradle-cache \ -v "${HOME}/.gradle":/gradle-cache \
-v "${HOME}/.m2":/root/.m2 \
-e GRADLE_USER_HOME=/gradle-cache \ -e GRADLE_USER_HOME=/gradle-cache \
-w /workspace/${{ inputs.working_directory }} \ -w /workspace/${{ inputs.working_directory }} \
"ghcr.io/${{ steps.vars.outputs.image_namespace }}/${{ inputs.image_name }}:${{ steps.vars.outputs.tag }}" \ "ghcr.io/${{ steps.vars.outputs.image_namespace }}/${{ inputs.image_name }}:${{ steps.vars.outputs.tag }}" \
......
...@@ -376,8 +376,8 @@ jobs: ...@@ -376,8 +376,8 @@ jobs:
git clone https://github.com/emscripten-core/emsdk.git git clone https://github.com/emscripten-core/emsdk.git
cd ./emsdk cd ./emsdk
fi fi
./emsdk install 3.1.50 ./emsdk install 4.0.7
./emsdk activate 3.1.50 ./emsdk activate 4.0.7
- shell: bash - shell: bash
name: 'Run WASM Tests' name: 'Run WASM Tests'
......
...@@ -89,11 +89,14 @@ open class ClocksAwt( ...@@ -89,11 +89,14 @@ open class ClocksAwt(
val y = ypos.toFloat() val y = ypos.toFloat()
canvas.drawString(text, x, y, font, paint) canvas.drawString(text, x, y, font, paint)
val style = ParagraphStyle().apply { val style = ParagraphStyle()
fontRastrSettings = FontRastrSettings(FontEdging.SUBPIXEL_ANTI_ALIAS, FontHinting.SLIGHT, true)
}
val paragraph = ParagraphBuilder(style, fontCollection) val paragraph = ParagraphBuilder(style, fontCollection)
.pushStyle(TextStyle().setColor(0xFF000000.toInt())) .pushStyle(TextStyle().apply {
color = 0xFF000000.toInt()
fontEdging = FontEdging.SUBPIXEL_ANTI_ALIAS
fontHinting = FontHinting.SLIGHT
subpixel = true
})
.addText("Graphic API: ${renderProvider()}, JRE: ${System.getProperty("java.vendor")}, ${System.getProperty("java.runtime.version")} $currentSystemTheme") .addText("Graphic API: ${renderProvider()}, JRE: ${System.getProperty("java.vendor")}, ${System.getProperty("java.runtime.version")} $currentSystemTheme")
.popStyle() .popStyle()
.build() .build()
......
...@@ -10,7 +10,7 @@ enum class OS( ...@@ -10,7 +10,7 @@ enum class OS(
Linux("linux", arrayOf()), Linux("linux", arrayOf()),
Android("android", arrayOf()), Android("android", arrayOf()),
Windows("windows", arrayOf()), Windows("windows", arrayOf()),
MacOS("macos", arrayOf("-mmacosx-version-min=10.15")), MacOS("macos", arrayOf("-mmacosx-version-min=11.0")),
Wasm("wasm", arrayOf()), Wasm("wasm", arrayOf()),
IOS("ios", arrayOf()), IOS("ios", arrayOf()),
TVOS("tvos", arrayOf()) TVOS("tvos", arrayOf())
......
...@@ -65,8 +65,8 @@ fun skiaPreprocessorFlags(os: OS, buildType: SkiaBuildType): Array<String> { ...@@ -65,8 +65,8 @@ fun skiaPreprocessorFlags(os: OS, buildType: SkiaBuildType): Array<String> {
"-DSK_SHAPER_UNICODE_AVAILABLE", "-DSK_SHAPER_UNICODE_AVAILABLE",
"-DSK_SUPPORT_OPENCL=0", "-DSK_SUPPORT_OPENCL=0",
"-DSK_UNICODE_AVAILABLE", "-DSK_UNICODE_AVAILABLE",
"-DU_DISABLE_RENAMING",
"-DSK_USING_THIRD_PARTY_ICU", "-DSK_USING_THIRD_PARTY_ICU",
"-DSK_HIDE_PATH_EDIT_METHODS", // Temporary (m144) skia flag for migration to SkPathBuilder
// For ICU symbols renaming: // For ICU symbols renaming:
"-DU_DISABLE_RENAMING=0", "-DU_DISABLE_RENAMING=0",
"-DU_DISABLE_VERSION_SUFFIX=1", "-DU_DISABLE_VERSION_SUFFIX=1",
......
...@@ -10,7 +10,7 @@ RUN apt update -y && \ ...@@ -10,7 +10,7 @@ RUN apt update -y && \
# Install emscripten # Install emscripten
ENV EMSDK_DIR=/usr/emsdk ENV EMSDK_DIR=/usr/emsdk
ENV EMSDK_VER=3.1.50 ENV EMSDK_VER=4.0.7
ENV PATH=$EMSDK_DIR/upstream/emscripten:$PATH ENV PATH=$EMSDK_DIR/upstream/emscripten:$PATH
RUN git clone https://github.com/emscripten-core/emsdk.git $EMSDK_DIR && \ RUN git clone https://github.com/emscripten-core/emsdk.git $EMSDK_DIR && \
$EMSDK_DIR/emsdk install $EMSDK_VER && \ $EMSDK_DIR/emsdk install $EMSDK_VER && \
......
...@@ -7,7 +7,7 @@ kotlin.mpp.enableCInteropCommonization=true ...@@ -7,7 +7,7 @@ kotlin.mpp.enableCInteropCommonization=true
deploy.version=0.0.0 deploy.version=0.0.0
# a tag from https://github.com/JetBrains/skia-pack # a tag from https://github.com/JetBrains/skia-pack
dependencies.skia=m138-80d088a-2 dependencies.skia=m144-e2e6623374-2
# a tag from https://github.com/JetBrains/angle-pack # a tag from https://github.com/JetBrains/angle-pack
dependencies.angle=ec4d8f8e4d dependencies.angle=ec4d8f8e4d
......
...@@ -11,10 +11,12 @@ ...@@ -11,10 +11,12 @@
#if (defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_LINUX)) && !defined(SKIKO_WASM) #if (defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_LINUX)) && !defined(SKIKO_WASM)
#include "ports/SkFontMgr_fontconfig.h" #include "ports/SkFontMgr_fontconfig.h"
#include "ports/SkFontScanner_FreeType.h"
#endif #endif
#ifdef SK_BUILD_FOR_ANDROID #ifdef SK_BUILD_FOR_ANDROID
#include "ports/SkFontMgr_android.h" #include "ports/SkFontMgr_android.h"
#include "ports/SkFontScanner_FreeType.h"
#endif #endif
#ifdef SKIKO_WASM #ifdef SKIKO_WASM
...@@ -36,11 +38,11 @@ sk_sp<SkFontMgr> SkFontMgrSkikoDefault() { ...@@ -36,11 +38,11 @@ sk_sp<SkFontMgr> SkFontMgrSkikoDefault() {
#endif #endif
#if (defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_LINUX)) && !defined(SKIKO_WASM) #if (defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_LINUX)) && !defined(SKIKO_WASM)
return SkFontMgr_New_FontConfig(nullptr); return SkFontMgr_New_FontConfig(nullptr, SkFontScanner_Make_FreeType());
#endif #endif
#ifdef SK_BUILD_FOR_ANDROID #ifdef SK_BUILD_FOR_ANDROID
return SkFontMgr_New_Android(nullptr); return SkFontMgr_New_Android(nullptr, SkFontScanner_Make_FreeType());
#endif #endif
#ifdef SKIKO_WASM #ifdef SKIKO_WASM
......
...@@ -49,7 +49,7 @@ namespace skikoMpp { ...@@ -49,7 +49,7 @@ namespace skikoMpp {
if (run.fGlyphCount > 1 && SkScalarNearlyEqual(posBuffer[(run.fGlyphCount - 2) * 2], lastLeft)) if (run.fGlyphCount > 1 && SkScalarNearlyEqual(posBuffer[(run.fGlyphCount - 2) * 2], lastLeft))
lastWidth = 0; lastWidth = 0;
else else
font.getWidths(&run.fGlyphIndices[run.fGlyphCount - 1], 1, &lastWidth); font.getWidths({&run.fGlyphIndices[run.fGlyphCount - 1], 1}, {&lastWidth, 1});
auto runBounds = SkRect::MakeLTRB(posBuffer[0], posBuffer[1] + metrics.fAscent, lastLeft + lastWidth, posBuffer[1] + metrics.fDescent); auto runBounds = SkRect::MakeLTRB(posBuffer[0], posBuffer[1] + metrics.fAscent, lastLeft + lastWidth, posBuffer[1] + metrics.fDescent);
bounds->join(runBounds); bounds->join(runBounds);
......
#include <SkNWayCanvas.h> #include <SkNWayCanvas.h>
#include <SkPathBuilder.h>
#include <SkPicture.h> #include <SkPicture.h>
#include <SkShadowUtils.h> #include <SkShadowUtils.h>
#include "node/RenderNode.h" #include "node/RenderNode.h"
...@@ -306,9 +307,9 @@ void RenderNode::updateMatrix() { ...@@ -306,9 +307,9 @@ void RenderNode::updateMatrix() {
void RenderNode::drawShadow(SkCanvas *canvas, const LightGeometry& lightGeometry, const LightInfo& lightInfo) { void RenderNode::drawShadow(SkCanvas *canvas, const LightGeometry& lightGeometry, const LightInfo& lightInfo) {
SkPath tmpPath, *path = &tmpPath; SkPath tmpPath, *path = &tmpPath;
if (this->clipRect) { if (this->clipRect) {
tmpPath.addRect(*this->clipRect); tmpPath = SkPathBuilder().addRect(*this->clipRect).detach();
} else if (this->clipRRect) { } else if (this->clipRRect) {
tmpPath.addRRect(*this->clipRRect); tmpPath = SkPathBuilder().addRRect(*this->clipRRect).detach();
} else if (this->clipPath) { } else if (this->clipPath) {
path = &*this->clipPath; path = &*this->clipPath;
} else { } else {
......
package org.jetbrains.skia
/**
* PathAddMode chooses how addPath() appends paths.
* Adding one Path to another can extend the last contour or start a new contour.
*/
enum class PathAddMode {
/**
* Contours are appended to the destination path as new contours.
*/
APPEND,
/**
* Extends the last contour of the destination path with the first contour
* of the source path, connecting them with a line.
*/
EXTEND
}
package org.jetbrains.skia
/**
* Enum for choosing smaller or larger arc when adding arcs to paths.
*/
enum class PathArcSize {
/** Smaller of arc pair */
SMALL,
/** Larger of arc pair */
LARGE
}
package org.jetbrains.skia
import org.jetbrains.skia.impl.*
import org.jetbrains.skia.impl.Library.Companion.staticLoad
/**
* PathBuilder is used to create immutable Path objects.
*
* PathBuilder follows the builder pattern - all mutation methods return the builder instance
* for method chaining. Once path construction is complete, call [detach] to get an immutable
* Path object, or [snapshot] to get a copy while keeping the builder usable.
*
* This class contains all path construction methods that were previously mutation methods
* on the Path class. Path objects created from PathBuilder are immutable.
*/
class PathBuilder internal constructor(ptr: NativePointer) : Managed(ptr, _FinalizerHolder.PTR) {
companion object {
init {
staticLoad()
}
}
private object _FinalizerHolder {
val PTR = PathBuilder_nGetFinalizer()
}
/**
* Constructs an empty PathBuilder with WINDING fill type.
*/
constructor() : this(PathBuilder_nMake())
/**
* Constructs a PathBuilder with the specified fill type.
*
* @param fillType the fill type for paths created from this builder
*/
constructor(fillType: PathFillMode) : this(PathBuilder_nMakeWithFillType(fillType.ordinal))
/**
* Constructs a PathBuilder by copying an existing Path.
*
* @param path the path to copy into this builder
*/
constructor(path: Path) : this(
PathBuilder_nMakeFromPath(getPtr(path)).also {
reachabilityBarrier(path)
}
)
/**
* Returns an immutable Path representing the current state of this builder.
* The builder remains valid and can continue to be used.
*
* @return a new immutable Path
*/
fun snapshot(): Path {
return try {
Stats.onNativeCall()
Path(PathBuilder_nSnapshot(_ptr))
} finally {
reachabilityBarrier(this)
}
}
/**
* Returns an immutable Path representing the current state of this builder.
* The builder is reset to empty after this call.
*
* @return a new immutable Path
*/
fun detach(): Path {
return try {
Stats.onNativeCall()
Path(PathBuilder_nDetach(_ptr))
} finally {
reachabilityBarrier(this)
}
}
/**
* Sets the fill type for paths created from this builder.
*
* @param fillType the fill type to set
* @return this builder for chaining
*/
fun setFillType(fillType: PathFillMode): PathBuilder {
return try {
Stats.onNativeCall()
PathBuilder_nSetFillType(_ptr, fillType.ordinal)
this
} finally {
reachabilityBarrier(this)
}
}
/**
* Resets the builder to empty, removing all verbs, points, and weights.
* Fill type is reset to WINDING.
*
* @return this builder for chaining
*/
fun reset(): PathBuilder {
return try {
Stats.onNativeCall()
PathBuilder_nReset(_ptr)
this
} finally {
reachabilityBarrier(this)
}
}
/**
* Hints to reserve additional space for approximately [extraPtCount] points and verbs.
* May improve performance by reducing allocations.
*
* @param extraPtCount number of additional points to reserve
* @return this builder for chaining
*/
fun incReserve(extraPtCount: Int): PathBuilder {
return try {
Stats.onNativeCall()
PathBuilder_nIncReserve(_ptr, extraPtCount)
this
} finally {
reachabilityBarrier(this)
}
}
/**
* Adds beginning of contour at point (x, y).
*
* @param x x-axis value of contour start
* @param y y-axis value of contour start
* @return this builder for chaining
*/
fun moveTo(x: Float, y: Float): PathBuilder {
return try {
Stats.onNativeCall()
PathBuilder_nMoveTo(_ptr, x, y)
this
} finally {
reachabilityBarrier(this)
}
}
/**
* Adds beginning of contour at point p.
*
* @param p contour start
* @return this builder for chaining
*/
fun moveTo(p: Point): PathBuilder {
return moveTo(p.x, p.y)
}
/**
* Adds beginning of contour relative to last point.
* If PathBuilder is empty, starts contour at (dx, dy).
* Otherwise, start contour at last point offset by (dx, dy).
*
* @param dx offset from last point to contour start on x-axis
* @param dy offset from last point to contour start on y-axis
* @return this builder for chaining
*/
fun rMoveTo(dx: Float, dy: Float): PathBuilder {
return try {
Stats.onNativeCall()
PathBuilder_nRMoveTo(_ptr, dx, dy)
this
} finally {
reachabilityBarrier(this)
}
}
/**
* Adds line from last point to (x, y). If PathBuilder is empty, or last verb is
* CLOSE, last point is set to (0, 0) before adding line.
*
* @param x end of added line on x-axis
* @param y end of added line on y-axis
* @return this builder for chaining
*/
fun lineTo(x: Float, y: Float): PathBuilder {
return try {
Stats.onNativeCall()
PathBuilder_nLineTo(_ptr, x, y)
this
} finally {
reachabilityBarrier(this)
}
}
/**
* Adds line from last point to point p.
*
* @param p end point of added line
* @return this builder for chaining
*/
fun lineTo(p: Point): PathBuilder {
return lineTo(p.x, p.y)
}
/**
* Adds line from last point to vector (dx, dy). If PathBuilder is empty, or last verb is
* CLOSE, last point is set to (0, 0) before adding line.
* Line end is last point plus vector (dx, dy).
*
* @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 this builder for chaining
*/
fun rLineTo(dx: Float, dy: Float): PathBuilder {
return try {
Stats.onNativeCall()
PathBuilder_nRLineTo(_ptr, dx, dy)
this
} finally {
reachabilityBarrier(this)
}
}
/**
* Adds quad from last point towards (x1, y1), to (x2, y2).
*
* @param x1 control point of quad on x-axis
* @param y1 control point of quad on y-axis
* @param x2 end point of quad on x-axis
* @param y2 end point of quad on y-axis
* @return this builder for chaining
*/
fun quadTo(x1: Float, y1: Float, x2: Float, y2: Float): PathBuilder {
return try {
Stats.onNativeCall()
PathBuilder_nQuadTo(_ptr, x1, y1, x2, y2)
this
} finally {
reachabilityBarrier(this)
}
}
/**
* Adds quad from last point towards point p1, to point p2.
*
* @param p1 control point of added quad
* @param p2 end point of added quad
* @return this builder for chaining
*/
fun quadTo(p1: Point, p2: Point): PathBuilder {
return quadTo(p1.x, p1.y, p2.x, p2.y)
}
/**
* Adds quad from last point towards vector (dx1, dy1), to vector (dx2, dy2).
* Quad control is last point plus vector (dx1, dy1).
* Quad end is last point plus vector (dx2, dy2).
*
* @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 this builder for chaining
*/
fun rQuadTo(dx1: Float, dy1: Float, dx2: Float, dy2: Float): PathBuilder {
return try {
Stats.onNativeCall()
PathBuilder_nRQuadTo(_ptr, dx1, dy1, dx2, dy2)
this
} finally {
reachabilityBarrier(this)
}
}
/**
* Adds conic from last point towards (x1, y1), to (x2, y2), weighted by w.
*
* @param x1 control point of conic on x-axis
* @param y1 control point of conic on y-axis
* @param x2 end point of conic on x-axis
* @param y2 end point of conic on y-axis
* @param w weight of added conic
* @return this builder for chaining
*/
fun conicTo(x1: Float, y1: Float, x2: Float, y2: Float, w: Float): PathBuilder {
return try {
Stats.onNativeCall()
PathBuilder_nConicTo(_ptr, x1, y1, x2, y2, w)
this
} finally {
reachabilityBarrier(this)
}
}
/**
* Adds conic from last point towards point p1, to point p2, weighted by w.
*
* @param p1 control point of added conic
* @param p2 end point of added conic
* @param w weight of added conic
* @return this builder for chaining
*/
fun conicTo(p1: Point, p2: Point, w: Float): PathBuilder {
return conicTo(p1.x, p1.y, p2.x, p2.y, w)
}
/**
* Adds conic from last point towards vector (dx1, dy1), to vector (dx2, dy2), weighted by w.
* Control is last point plus vector (dx1, dy1).
* End is last point plus vector (dx2, dy2).
*
* @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 this builder for chaining
*/
fun rConicTo(dx1: Float, dy1: Float, dx2: Float, dy2: Float, w: Float): PathBuilder {
return try {
Stats.onNativeCall()
PathBuilder_nRConicTo(_ptr, dx1, dy1, dx2, dy2, w)
this
} finally {
reachabilityBarrier(this)
}
}
/**
* Adds cubic from last point towards (x1, y1), then towards (x2, y2), ending at (x3, y3).
*
* @param x1 first control point of cubic on x-axis
* @param y1 first control point of cubic on y-axis
* @param x2 second control point of cubic on x-axis
* @param y2 second control point of cubic on y-axis
* @param x3 end point of cubic on x-axis
* @param y3 end point of cubic on y-axis
* @return this builder for chaining
*/
fun cubicTo(x1: Float, y1: Float, x2: Float, y2: Float, x3: Float, y3: Float): PathBuilder {
return try {
Stats.onNativeCall()
PathBuilder_nCubicTo(_ptr, x1, y1, x2, y2, x3, y3)
this
} finally {
reachabilityBarrier(this)
}
}
/**
* Adds cubic from last point towards point p1, then towards point p2, ending at point p3.
*
* @param p1 first control point of cubic
* @param p2 second control point of cubic
* @param p3 end point of cubic
* @return this builder for chaining
*/
fun cubicTo(p1: Point, p2: Point, p3: Point): PathBuilder {
return cubicTo(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y)
}
/**
* Adds cubic from last point towards vector (dx1, dy1), then towards (dx2, dy2), to (dx3, dy3).
*
* @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 this builder for chaining
*/
fun rCubicTo(dx1: Float, dy1: Float, dx2: Float, dy2: Float, dx3: Float, dy3: Float): PathBuilder {
return try {
Stats.onNativeCall()
PathBuilder_nRCubicTo(_ptr, dx1, dy1, dx2, dy2, dx3, dy3)
this
} finally {
reachabilityBarrier(this)
}
}
/**
* Appends arc to path. Arc is part of ellipse bounded by oval, from startAngle through sweepAngle.
*
* @param oval bounds of ellipse containing arc
* @param startAngle starting angle of arc in degrees
* @param sweepAngle sweep, in degrees. Positive is clockwise
* @param forceMoveTo true to start a new contour with arc
* @return this builder for chaining
*/
fun arcTo(oval: Rect, startAngle: Float, sweepAngle: Float, forceMoveTo: Boolean): PathBuilder {
return arcTo(oval.left, oval.top, oval.right, oval.bottom, startAngle, sweepAngle, forceMoveTo)
}
/**
* Appends arc to path. Arc is part of ellipse bounded by rectangle.
*
* @param left left edge of oval bounding ellipse
* @param top top edge of oval bounding ellipse
* @param right right edge of oval bounding ellipse
* @param bottom bottom edge of oval bounding ellipse
* @param startAngle starting angle of arc in degrees
* @param sweepAngle sweep, in degrees. Positive is clockwise
* @param forceMoveTo true to start a new contour with arc
* @return this builder for chaining
*/
fun arcTo(left: Float, top: Float, right: Float, bottom: Float, startAngle: Float, sweepAngle: Float, forceMoveTo: Boolean): PathBuilder {
return try {
Stats.onNativeCall()
PathBuilder_nArcTo(_ptr, left, top, right, bottom, startAngle, sweepAngle, forceMoveTo)
this
} finally {
reachabilityBarrier(this)
}
}
/**
* Appends arc to path, after appending line if needed. Arc is contained by tangent from
* last point to (x1, y1), and tangent from (x1, y1) to (x2, y2).
*
* @param x1 x-axis value common to pair of tangents
* @param y1 y-axis value common to pair of tangents
* @param x2 x-axis value end of second tangent
* @param y2 y-axis value end of second tangent
* @param radius distance from arc to circle center
* @return this builder for chaining
*/
fun tangentArcTo(x1: Float, y1: Float, x2: Float, y2: Float, radius: Float): PathBuilder {
return try {
Stats.onNativeCall()
PathBuilder_nTangentArcTo(_ptr, x1, y1, x2, y2, radius)
this
} finally {
reachabilityBarrier(this)
}
}
/**
* Appends arc to path, after appending line if needed.
*
* @param p1 point common to pair of tangents
* @param p2 end of second tangent
* @param radius distance from arc to circle center
* @return this builder for chaining
*/
fun tangentArcTo(p1: Point, p2: Point, radius: Float): PathBuilder {
return tangentArcTo(p1.x, p1.y, p2.x, p2.y, radius)
}
/**
* Appends arc to path. Arc is implemented by one or more conics weighted to describe
* part of oval with radii (rx, ry) rotated by xAxisRotate degrees.
*
* @param rx radius on x-axis before x-axis rotation
* @param ry radius on y-axis before x-axis rotation
* @param xAxisRotate x-axis rotation in degrees; positive values are clockwise
* @param arc chooses smaller or larger arc
* @param direction chooses clockwise or counterclockwise arc
* @param x end of arc on x-axis
* @param y end of arc on y-axis
* @return this builder for chaining
*/
fun ellipticalArcTo(
rx: Float,
ry: Float,
xAxisRotate: Float,
arc: PathEllipseArc,
direction: PathDirection,
x: Float,
y: Float
): PathBuilder {
return try {
Stats.onNativeCall()
PathBuilder_nEllipticalArcTo(_ptr, rx, ry, xAxisRotate, arc.ordinal, direction.ordinal, x, y)
this
} finally {
reachabilityBarrier(this)
}
}
/**
* Appends arc to path using point parameters.
*
* @param r radii on axes before x-axis rotation
* @param xAxisRotate x-axis rotation in degrees
* @param arc chooses smaller or larger arc
* @param direction chooses clockwise or counterclockwise arc
* @param xy end of arc
* @return this builder for chaining
*/
fun ellipticalArcTo(r: Point, xAxisRotate: Float, arc: PathEllipseArc, direction: PathDirection, xy: Point): PathBuilder {
return ellipticalArcTo(r.x, r.y, xAxisRotate, arc, direction, xy.x, xy.y)
}
/**
* Appends arc to path, relative to last point.
*
* @param rx radius before x-axis rotation
* @param ry radius before x-axis rotation
* @param xAxisRotate x-axis rotation in degrees
* @param arc chooses smaller or larger arc
* @param direction chooses clockwise or counterclockwise arc
* @param dx x-axis offset end of arc from last point
* @param dy y-axis offset end of arc from last point
* @return this builder for chaining
*/
fun rEllipticalArcTo(
rx: Float,
ry: Float,
xAxisRotate: Float,
arc: PathEllipseArc,
direction: PathDirection,
dx: Float,
dy: Float
): PathBuilder {
return try {
Stats.onNativeCall()
PathBuilder_nREllipticalArcTo(_ptr, rx, ry, xAxisRotate, arc.ordinal, direction.ordinal, dx, dy)
this
} finally {
reachabilityBarrier(this)
}
}
/**
* Appends CLOSE verb to path. A closed contour connects the first and last point with line.
*
* @return this builder for chaining
*/
fun closePath(): PathBuilder {
return try {
Stats.onNativeCall()
PathBuilder_nClosePath(_ptr)
this
} finally {
reachabilityBarrier(this)
}
}
/**
* Adds rectangle to path, appending MOVE, three LINE, and CLOSE verbs.
*
* @param rect rectangle to add as a closed contour
* @param dir direction to wind added contour
* @param start initial corner: 0=top-left, 1=top-right, 2=bottom-right, 3=bottom-left
* @return this builder for chaining
*/
fun addRect(rect: Rect, dir: PathDirection = PathDirection.CLOCKWISE, start: Int = 0): PathBuilder {
return addRect(rect.left, rect.top, rect.right, rect.bottom, dir, start)
}
/**
* Adds rectangle to path, appending MOVE, three LINE, and CLOSE verbs.
*
* @param left left edge of rectangle
* @param top top edge of rectangle
* @param right right edge of rectangle
* @param bottom bottom edge of rectangle
* @param dir direction to wind added contour
* @param start initial corner: 0=top-left, 1=top-right, 2=bottom-right, 3=bottom-left
* @return this builder for chaining
*/
fun addRect(left: Float, top: Float, right: Float, bottom: Float, dir: PathDirection = PathDirection.CLOCKWISE, start: Int = 0): PathBuilder {
return try {
Stats.onNativeCall()
PathBuilder_nAddRect(_ptr, left, top, right, bottom, dir.ordinal, start)
this
} finally {
reachabilityBarrier(this)
}
}
/**
* Adds oval to path, appending MOVE, four CONIC, and CLOSE verbs.
*
* @param oval bounds of ellipse
* @param dir direction to wind ellipse
* @param start index of initial point: 0=top, 1=right, 2=bottom, 3=left
* @return this builder for chaining
*/
fun addOval(oval: Rect, dir: PathDirection = PathDirection.CLOCKWISE, start: Int = 1): PathBuilder {
return addOval(oval.left, oval.top, oval.right, oval.bottom, dir, start)
}
/**
* Adds oval to path, appending MOVE, four CONIC, and CLOSE verbs.
*
* @param left left edge of oval
* @param top top edge of oval
* @param right right edge of oval
* @param bottom bottom edge of oval
* @param dir direction to wind ellipse
* @param start index of initial point: 0=top, 1=right, 2=bottom, 3=left
* @return this builder for chaining
*/
fun addOval(left: Float, top: Float, right: Float, bottom: Float, dir: PathDirection = PathDirection.CLOCKWISE, start: Int = 1): PathBuilder {
return try {
Stats.onNativeCall()
PathBuilder_nAddOval(_ptr, left, top, right, bottom, dir.ordinal, start)
this
} finally {
reachabilityBarrier(this)
}
}
/**
* Adds circle centered at (x, y) of size radius to path.
*
* @param x center of circle on x-axis
* @param y center of circle on y-axis
* @param radius distance from center to edge
* @param dir direction to wind circle
* @return this builder for chaining
*/
fun addCircle(x: Float, y: Float, radius: Float, dir: PathDirection = PathDirection.CLOCKWISE): PathBuilder {
return try {
Stats.onNativeCall()
PathBuilder_nAddCircle(_ptr, x, y, radius, dir.ordinal)
this
} finally {
reachabilityBarrier(this)
}
}
/**
* Appends arc to path, as the start of new contour.
*
* @param oval bounds of ellipse containing arc
* @param startAngle starting angle of arc in degrees
* @param sweepAngle sweep, in degrees. Positive is clockwise
* @return this builder for chaining
*/
fun addArc(oval: Rect, startAngle: Float, sweepAngle: Float): PathBuilder {
return addArc(oval.left, oval.top, oval.right, oval.bottom, startAngle, sweepAngle)
}
/**
* Appends arc to path, as the start of new contour.
*
* @param left left edge of oval bounding ellipse
* @param top top edge of oval bounding ellipse
* @param right right edge of oval bounding ellipse
* @param bottom bottom edge of oval bounding ellipse
* @param startAngle starting angle of arc in degrees
* @param sweepAngle sweep, in degrees. Positive is clockwise
* @return this builder for chaining
*/
fun addArc(left: Float, top: Float, right: Float, bottom: Float, startAngle: Float, sweepAngle: Float): PathBuilder {
return try {
Stats.onNativeCall()
PathBuilder_nAddArc(_ptr, left, top, right, bottom, startAngle, sweepAngle)
this
} finally {
reachabilityBarrier(this)
}
}
/**
* Adds rounded rectangle to path, creating a new closed contour.
*
* @param rrect bounds and radii of rounded rectangle
* @param dir direction to wind rounded rectangle
* @param start index of initial point
* @return this builder for chaining
*/
fun addRRect(rrect: RRect, dir: PathDirection = PathDirection.CLOCKWISE, start: Int = 6): PathBuilder {
return addRRect(rrect.left, rrect.top, rrect.right, rrect.bottom, rrect.radii, dir, start)
}
/**
* Adds rounded rectangle to path, creating a new closed contour.
*
* @param left left edge of rounded rectangle
* @param top top edge of rounded rectangle
* @param right right edge of rounded rectangle
* @param bottom bottom edge of rounded rectangle
* @param radii array of 8 radius values, 2 for each corner
* @param dir direction to wind rounded rectangle
* @param start index of initial point
* @return this builder for chaining
*/
fun addRRect(left: Float, top: Float, right: Float, bottom: Float, radii: FloatArray, dir: PathDirection = PathDirection.CLOCKWISE, start: Int = 6): PathBuilder {
return try {
Stats.onNativeCall()
interopScope {
PathBuilder_nAddRRect(_ptr, left, top, right, bottom, toInterop(radii), radii.size, dir.ordinal, start)
}
this
} finally {
reachabilityBarrier(this)
}
}
/**
* Adds contour created from line array.
*
* @param pts array of points
* @param close true to add line connecting contour end and start
* @return this builder for chaining
*/
fun addPoly(pts: Array<Point>, close: Boolean): PathBuilder {
val flat = FloatArray(pts.size * 2)
for (i in pts.indices) {
flat[i * 2] = pts[i].x
flat[i * 2 + 1] = pts[i].y
}
return addPoly(flat, close)
}
/**
* Adds contour created from line array.
*
* @param coords flat array of point coordinates (x, y pairs)
* @param close true to add line connecting contour end and start
* @return this builder for chaining
*/
fun addPoly(coords: FloatArray, close: Boolean): PathBuilder {
return try {
Stats.onNativeCall()
interopScope {
PathBuilder_nAddPoly(_ptr, toInterop(coords), coords.size / 2, close)
}
this
} finally {
reachabilityBarrier(this)
}
}
/**
* Appends src path to this builder.
*
* @param src path to add
* @param mode how to append the path (APPEND or EXTEND)
* @return this builder for chaining
*/
fun addPath(src: Path, mode: PathAddMode = PathAddMode.APPEND): PathBuilder {
return try {
Stats.onNativeCall()
PathBuilder_nAddPath(_ptr, getPtr(src), mode.ordinal)
this
} finally {
reachabilityBarrier(src)
}
}
/**
* Appends src path offset by (dx, dy).
*
* @param src path to add
* @param dx offset on x-axis
* @param dy offset on y-axis
* @param mode how to append the path (APPEND or EXTEND)
* @return this builder for chaining
*/
fun addPath(src: Path, dx: Float, dy: Float, mode: PathAddMode = PathAddMode.APPEND): PathBuilder {
return try {
Stats.onNativeCall()
PathBuilder_nAddPathOffset(_ptr, getPtr(src), dx, dy, mode.ordinal)
this
} finally {
reachabilityBarrier(src)
}
}
/**
* Appends src path transformed by matrix.
*
* @param src path to add
* @param matrix transform to apply
* @param mode how to append the path (APPEND or EXTEND)
* @return this builder for chaining
*/
fun addPath(src: Path, matrix: Matrix33, mode: PathAddMode = PathAddMode.APPEND): PathBuilder {
return try {
Stats.onNativeCall()
interopScope {
PathBuilder_nAddPathTransform(_ptr, getPtr(src), toInterop(matrix.mat), mode.ordinal)
}
this
} finally {
reachabilityBarrier(src)
reachabilityBarrier(matrix)
}
}
/**
* Sets last point to (x, y).
*
* @param x x-coordinate
* @param y y-coordinate
* @return this builder for chaining
*/
fun setLastPt(x: Float, y: Float): PathBuilder {
return try {
Stats.onNativeCall()
PathBuilder_nSetLastPt(_ptr, x, y)
this
} finally {
reachabilityBarrier(this)
}
}
}
@ExternalSymbolName("org_jetbrains_skia_PathBuilder__1nGetFinalizer")
private external fun PathBuilder_nGetFinalizer(): NativePointer
@ExternalSymbolName("org_jetbrains_skia_PathBuilder__1nMake")
private external fun PathBuilder_nMake(): NativePointer
@ExternalSymbolName("org_jetbrains_skia_PathBuilder__1nMakeWithFillType")
private external fun PathBuilder_nMakeWithFillType(fillType: Int): NativePointer
@ExternalSymbolName("org_jetbrains_skia_PathBuilder__1nMakeFromPath")
private external fun PathBuilder_nMakeFromPath(pathPtr: NativePointer): NativePointer
@ExternalSymbolName("org_jetbrains_skia_PathBuilder__1nSnapshot")
private external fun PathBuilder_nSnapshot(ptr: NativePointer): NativePointer
@ExternalSymbolName("org_jetbrains_skia_PathBuilder__1nDetach")
private external fun PathBuilder_nDetach(ptr: NativePointer): NativePointer
@ExternalSymbolName("org_jetbrains_skia_PathBuilder__1nSetFillType")
private external fun PathBuilder_nSetFillType(ptr: NativePointer, fillType: Int)
@ExternalSymbolName("org_jetbrains_skia_PathBuilder__1nReset")
private external fun PathBuilder_nReset(ptr: NativePointer)
@ExternalSymbolName("org_jetbrains_skia_PathBuilder__1nIncReserve")
private external fun PathBuilder_nIncReserve(ptr: NativePointer, extraPtCount: Int)
@ExternalSymbolName("org_jetbrains_skia_PathBuilder__1nMoveTo")
private external fun PathBuilder_nMoveTo(ptr: NativePointer, x: Float, y: Float)
@ExternalSymbolName("org_jetbrains_skia_PathBuilder__1nRMoveTo")
private external fun PathBuilder_nRMoveTo(ptr: NativePointer, dx: Float, dy: Float)
@ExternalSymbolName("org_jetbrains_skia_PathBuilder__1nLineTo")
private external fun PathBuilder_nLineTo(ptr: NativePointer, x: Float, y: Float)
@ExternalSymbolName("org_jetbrains_skia_PathBuilder__1nRLineTo")
private external fun PathBuilder_nRLineTo(ptr: NativePointer, dx: Float, dy: Float)
@ExternalSymbolName("org_jetbrains_skia_PathBuilder__1nQuadTo")
private external fun PathBuilder_nQuadTo(ptr: NativePointer, x1: Float, y1: Float, x2: Float, y2: Float)
@ExternalSymbolName("org_jetbrains_skia_PathBuilder__1nRQuadTo")
private external fun PathBuilder_nRQuadTo(ptr: NativePointer, dx1: Float, dy1: Float, dx2: Float, dy2: Float)
@ExternalSymbolName("org_jetbrains_skia_PathBuilder__1nConicTo")
private external fun PathBuilder_nConicTo(ptr: NativePointer, x1: Float, y1: Float, x2: Float, y2: Float, w: Float)
@ExternalSymbolName("org_jetbrains_skia_PathBuilder__1nRConicTo")
private external fun PathBuilder_nRConicTo(ptr: NativePointer, dx1: Float, dy1: Float, dx2: Float, dy2: Float, w: Float)
@ExternalSymbolName("org_jetbrains_skia_PathBuilder__1nCubicTo")
private external fun PathBuilder_nCubicTo(ptr: NativePointer, x1: Float, y1: Float, x2: Float, y2: Float, x3: Float, y3: Float)
@ExternalSymbolName("org_jetbrains_skia_PathBuilder__1nRCubicTo")
private external fun PathBuilder_nRCubicTo(ptr: NativePointer, dx1: Float, dy1: Float, dx2: Float, dy2: Float, dx3: Float, dy3: Float)
@ExternalSymbolName("org_jetbrains_skia_PathBuilder__1nArcTo")
private external fun PathBuilder_nArcTo(ptr: NativePointer, left: Float, top: Float, right: Float, bottom: Float, startAngle: Float, sweepAngle: Float, forceMoveTo: Boolean)
@ExternalSymbolName("org_jetbrains_skia_PathBuilder__1nTangentArcTo")
private external fun PathBuilder_nTangentArcTo(ptr: NativePointer, x1: Float, y1: Float, x2: Float, y2: Float, radius: Float)
@ExternalSymbolName("org_jetbrains_skia_PathBuilder__1nEllipticalArcTo")
private external fun PathBuilder_nEllipticalArcTo(ptr: NativePointer, rx: Float, ry: Float, xAxisRotate: Float, arc: Int, direction: Int, x: Float, y: Float)
@ExternalSymbolName("org_jetbrains_skia_PathBuilder__1nREllipticalArcTo")
private external fun PathBuilder_nREllipticalArcTo(ptr: NativePointer, rx: Float, ry: Float, xAxisRotate: Float, arc: Int, direction: Int, dx: Float, dy: Float)
@ExternalSymbolName("org_jetbrains_skia_PathBuilder__1nClosePath")
private external fun PathBuilder_nClosePath(ptr: NativePointer)
@ExternalSymbolName("org_jetbrains_skia_PathBuilder__1nAddRect")
private external fun PathBuilder_nAddRect(ptr: NativePointer, left: Float, top: Float, right: Float, bottom: Float, dir: Int, start: Int)
@ExternalSymbolName("org_jetbrains_skia_PathBuilder__1nAddOval")
private external fun PathBuilder_nAddOval(ptr: NativePointer, left: Float, top: Float, right: Float, bottom: Float, dir: Int, start: Int)
@ExternalSymbolName("org_jetbrains_skia_PathBuilder__1nAddCircle")
private external fun PathBuilder_nAddCircle(ptr: NativePointer, x: Float, y: Float, radius: Float, dir: Int)
@ExternalSymbolName("org_jetbrains_skia_PathBuilder__1nAddArc")
private external fun PathBuilder_nAddArc(ptr: NativePointer, left: Float, top: Float, right: Float, bottom: Float, startAngle: Float, sweepAngle: Float)
@ExternalSymbolName("org_jetbrains_skia_PathBuilder__1nAddRRect")
private external fun PathBuilder_nAddRRect(ptr: NativePointer, left: Float, top: Float, right: Float, bottom: Float, radii: InteropPointer, size: Int, dir: Int, start: Int)
@ExternalSymbolName("org_jetbrains_skia_PathBuilder__1nAddPoly")
private external fun PathBuilder_nAddPoly(ptr: NativePointer, coords: InteropPointer, count: Int, close: Boolean)
@ExternalSymbolName("org_jetbrains_skia_PathBuilder__1nAddPath")
private external fun PathBuilder_nAddPath(ptr: NativePointer, srcPtr: NativePointer, mode: Int)
@ExternalSymbolName("org_jetbrains_skia_PathBuilder__1nAddPathOffset")
private external fun PathBuilder_nAddPathOffset(ptr: NativePointer, srcPtr: NativePointer, dx: Float, dy: Float, mode: Int)
@ExternalSymbolName("org_jetbrains_skia_PathBuilder__1nAddPathTransform")
private external fun PathBuilder_nAddPathTransform(ptr: NativePointer, srcPtr: NativePointer, matrix: InteropPointer, mode: Int)
@ExternalSymbolName("org_jetbrains_skia_PathBuilder__1nSetLastPt")
private external fun PathBuilder_nSetLastPt(ptr: NativePointer, x: Float, y: Float)
...@@ -182,7 +182,7 @@ class Picture internal constructor(ptr: NativePointer, managed: Boolean = true) ...@@ -182,7 +182,7 @@ class Picture internal constructor(ptr: NativePointer, managed: Boolean = true)
* *
* @see [https://fiddle.skia.org/c/@Picture_approximateBytesUsed](https://fiddle.skia.org/c/@Picture_approximateBytesUsed) * @see [https://fiddle.skia.org/c/@Picture_approximateBytesUsed](https://fiddle.skia.org/c/@Picture_approximateBytesUsed)
*/ */
val approximateBytesUsed: NativePointer val approximateBytesUsed: Int
get() = try { get() = try {
Stats.onNativeCall() Stats.onNativeCall()
_nGetApproximateBytesUsed(_ptr) _nGetApproximateBytesUsed(_ptr)
...@@ -273,7 +273,7 @@ private external fun _nMakePlaceholder(left: Float, top: Float, right: Float, bo ...@@ -273,7 +273,7 @@ private external fun _nMakePlaceholder(left: Float, top: Float, right: Float, bo
private external fun _nGetApproximateOpCount(ptr: NativePointer): Int private external fun _nGetApproximateOpCount(ptr: NativePointer): Int
@ExternalSymbolName("org_jetbrains_skia_Picture__1nGetApproximateBytesUsed") @ExternalSymbolName("org_jetbrains_skia_Picture__1nGetApproximateBytesUsed")
private external fun _nGetApproximateBytesUsed(ptr: NativePointer): NativePointer private external fun _nGetApproximateBytesUsed(ptr: NativePointer): Int
@ExternalSymbolName("org_jetbrains_skia_Picture__1nMakeShader") @ExternalSymbolName("org_jetbrains_skia_Picture__1nMakeShader")
private external fun _nMakeShader( private external fun _nMakeShader(
......
...@@ -2,6 +2,7 @@ package org.jetbrains.skia ...@@ -2,6 +2,7 @@ package org.jetbrains.skia
import org.jetbrains.skia.impl.InteropPointer import org.jetbrains.skia.impl.InteropPointer
import org.jetbrains.skia.impl.InteropScope import org.jetbrains.skia.impl.InteropScope
import org.jetbrains.skia.impl.withNullableResult
import org.jetbrains.skia.impl.withResult import org.jetbrains.skia.impl.withResult
class Point(val x: Float, val y: Float) { class Point(val x: Float, val y: Float) {
...@@ -68,5 +69,10 @@ class Point(val x: Float, val y: Float) { ...@@ -68,5 +69,10 @@ class Point(val x: Float, val y: Float) {
val result = withResult(FloatArray(2), block) val result = withResult(FloatArray(2), block)
return Point(result[0], result[1]) return Point(result[0], result[1])
} }
internal fun fromNullableInteropPointer(block: InteropScope.(InteropPointer) -> Boolean): Point? {
val result = withNullableResult(FloatArray(2), block) ?: return null
return Point(result[0], result[1])
}
} }
} }
\ No newline at end of file
...@@ -3,6 +3,10 @@ package org.jetbrains.skia ...@@ -3,6 +3,10 @@ package org.jetbrains.skia
import org.jetbrains.skia.impl.* import org.jetbrains.skia.impl.*
import org.jetbrains.skia.impl.Library.Companion.staticLoad import org.jetbrains.skia.impl.Library.Companion.staticLoad
/**
* Describes a set of pixels as either one integer rectangle or a run-length encoded
* set of rectangles.
*/
class Region : Managed(Region_nMake(), _FinalizerHolder.PTR) { class Region : Managed(Region_nMake(), _FinalizerHolder.PTR) {
companion object { companion object {
init { init {
...@@ -10,24 +14,47 @@ class Region : Managed(Region_nMake(), _FinalizerHolder.PTR) { ...@@ -10,24 +14,47 @@ class Region : Managed(Region_nMake(), _FinalizerHolder.PTR) {
} }
} }
/** Logical operations used to combine regions. */
enum class Op { enum class Op {
DIFFERENCE, INTERSECT, UNION, XOR, REVERSE_DIFFERENCE, REPLACE; /** target minus operand */
DIFFERENCE,
/** target intersected with operand */
INTERSECT,
/** target unioned with operand */
UNION,
/** target exclusive-or with operand */
XOR,
/** operand minus target */
REVERSE_DIFFERENCE,
/** replace target with operand */
REPLACE;
companion object { companion object {
internal val _values = Op.entries.toTypedArray() internal val _values = Op.entries.toTypedArray()
} }
} }
fun set(r: Region?): Boolean { /**
* Sets this region to [src].
*
* @return true if the resulting region is not empty
*/
fun set(src: Region): Boolean {
return try { return try {
Stats.onNativeCall() Stats.onNativeCall()
Region_nSet(_ptr, getPtr(r)) Region_nSet(_ptr, getPtr(src))
} finally { } finally {
reachabilityBarrier(this) reachabilityBarrier(this)
reachabilityBarrier(r) reachabilityBarrier(src)
} }
} }
/** Returns true if this region is empty. */
val isEmpty: Boolean val isEmpty: Boolean
get() = try { get() = try {
Stats.onNativeCall() Stats.onNativeCall()
...@@ -35,6 +62,8 @@ class Region : Managed(Region_nMake(), _FinalizerHolder.PTR) { ...@@ -35,6 +62,8 @@ class Region : Managed(Region_nMake(), _FinalizerHolder.PTR) {
} finally { } finally {
reachabilityBarrier(this) reachabilityBarrier(this)
} }
/** Returns true if this region is one non-empty rectangle. */
val isRect: Boolean val isRect: Boolean
get() = try { get() = try {
Stats.onNativeCall() Stats.onNativeCall()
...@@ -42,6 +71,8 @@ class Region : Managed(Region_nMake(), _FinalizerHolder.PTR) { ...@@ -42,6 +71,8 @@ class Region : Managed(Region_nMake(), _FinalizerHolder.PTR) {
} finally { } finally {
reachabilityBarrier(this) reachabilityBarrier(this)
} }
/** Returns true if this region is represented by more than one rectangle. */
val isComplex: Boolean val isComplex: Boolean
get() = try { get() = try {
Stats.onNativeCall() Stats.onNativeCall()
...@@ -49,6 +80,8 @@ class Region : Managed(Region_nMake(), _FinalizerHolder.PTR) { ...@@ -49,6 +80,8 @@ class Region : Managed(Region_nMake(), _FinalizerHolder.PTR) {
} finally { } finally {
reachabilityBarrier(this) reachabilityBarrier(this)
} }
/** Returns the union bounds of all rectangles in this region. */
val bounds: IRect val bounds: IRect
get() = try { get() = try {
Stats.onNativeCall() Stats.onNativeCall()
...@@ -60,6 +93,10 @@ class Region : Managed(Region_nMake(), _FinalizerHolder.PTR) { ...@@ -60,6 +93,10 @@ class Region : Managed(Region_nMake(), _FinalizerHolder.PTR) {
reachabilityBarrier(this) reachabilityBarrier(this)
} }
/**
* Returns a value that increases with the number of elements in the region.
* Returns 0 for empty, 1 for a single rectangle, and larger values for complex regions.
*/
fun computeRegionComplexity(): Int { fun computeRegionComplexity(): Int {
return try { return try {
Stats.onNativeCall() Stats.onNativeCall()
...@@ -69,19 +106,36 @@ class Region : Managed(Region_nMake(), _FinalizerHolder.PTR) { ...@@ -69,19 +106,36 @@ class Region : Managed(Region_nMake(), _FinalizerHolder.PTR) {
} }
} }
fun getBoundaryPath(p: Path?): Boolean { /**
* Appends the boundary outline of this region into [pathBuilder].
*
* @return true if this region is not empty and the builder changed
*/
fun addBoundaryPath(pathBuilder: PathBuilder): Boolean {
return try { return try {
Stats.onNativeCall() Stats.onNativeCall()
Region_nGetBoundaryPath( Region_nAddBoundaryPath(_ptr, getPtr(pathBuilder))
_ptr,
getPtr(p)
)
} finally { } finally {
reachabilityBarrier(this) reachabilityBarrier(this)
reachabilityBarrier(p) reachabilityBarrier(pathBuilder)
} }
} }
/** Returns the boundary of this region as a new immutable [Path]. */
fun getBoundaryPath(): Path {
return try {
Stats.onNativeCall()
Path(Region_nGetBoundaryPath(_ptr))
} finally {
reachabilityBarrier(this)
}
}
/**
* Sets this region to empty.
*
* @return always false
*/
fun setEmpty(): Boolean { fun setEmpty(): Boolean {
return try { return try {
Stats.onNativeCall() Stats.onNativeCall()
...@@ -91,10 +145,12 @@ class Region : Managed(Region_nMake(), _FinalizerHolder.PTR) { ...@@ -91,10 +145,12 @@ class Region : Managed(Region_nMake(), _FinalizerHolder.PTR) {
} }
} }
/** Sets this region to [rect]. Returns true if [rect] is not empty. */
fun setRect(rect: IRect): Boolean { fun setRect(rect: IRect): Boolean {
return setRect(rect.left, rect.top, rect.right, rect.bottom) return setRect(rect.left, rect.top, rect.right, rect.bottom)
} }
/** Sets this region to [left], [top], [right], [bottom]. */
fun setRect(left: Int, top: Int, right: Int, bottom: Int): Boolean { fun setRect(left: Int, top: Int, right: Int, bottom: Int): Boolean {
return try { return try {
Stats.onNativeCall() Stats.onNativeCall()
...@@ -104,6 +160,11 @@ class Region : Managed(Region_nMake(), _FinalizerHolder.PTR) { ...@@ -104,6 +160,11 @@ class Region : Managed(Region_nMake(), _FinalizerHolder.PTR) {
} }
} }
/**
* Sets this region to a union of [rects].
*
* @return true if the resulting region is not empty
*/
fun setRects(rects: Array<IRect>): Boolean { fun setRects(rects: Array<IRect>): Boolean {
return try { return try {
val arr = IntArray(rects.size * 4) val arr = IntArray(rects.size * 4)
...@@ -122,24 +183,30 @@ class Region : Managed(Region_nMake(), _FinalizerHolder.PTR) { ...@@ -122,24 +183,30 @@ class Region : Managed(Region_nMake(), _FinalizerHolder.PTR) {
} }
} }
fun setRegion(r: Region?): Boolean { /**
* Sets this region to [region].
*
* @return true if [region] is not empty
*/
fun setRegion(region: Region): Boolean {
return try { return try {
Stats.onNativeCall() Stats.onNativeCall()
Region_nSetRegion(_ptr, getPtr(r)) Region_nSetRegion(_ptr, getPtr(region))
} finally { } finally {
reachabilityBarrier(this) reachabilityBarrier(this)
reachabilityBarrier(r) reachabilityBarrier(region)
} }
} }
fun setPath(path: Path?, clip: Region?): Boolean { /**
* Sets this region to the rasterized outline of [path] clipped by [clip].
*
* @return true if the resulting region is not empty
*/
fun setPath(path: Path, clip: Region): Boolean {
return try { return try {
Stats.onNativeCall() Stats.onNativeCall()
Region_nSetPath( Region_nSetPath(_ptr, getPtr(path), getPtr(clip))
_ptr,
getPtr(path),
getPtr(clip)
)
} finally { } finally {
reachabilityBarrier(this) reachabilityBarrier(this)
reachabilityBarrier(path) reachabilityBarrier(path)
...@@ -147,10 +214,12 @@ class Region : Managed(Region_nMake(), _FinalizerHolder.PTR) { ...@@ -147,10 +214,12 @@ class Region : Managed(Region_nMake(), _FinalizerHolder.PTR) {
} }
} }
/** Returns true if this region intersects [rect]. */
fun intersects(rect: IRect): Boolean { fun intersects(rect: IRect): Boolean {
return intersects(rect.left, rect.top, rect.right, rect.bottom) return intersects(rect.left, rect.top, rect.right, rect.bottom)
} }
/** Returns true if this region intersects [left], [top], [right], [bottom]. */
fun intersects(left: Int, top: Int, right: Int, bottom: Int): Boolean { fun intersects(left: Int, top: Int, right: Int, bottom: Int): Boolean {
return try { return try {
Stats.onNativeCall() Stats.onNativeCall()
...@@ -160,19 +229,18 @@ class Region : Managed(Region_nMake(), _FinalizerHolder.PTR) { ...@@ -160,19 +229,18 @@ class Region : Managed(Region_nMake(), _FinalizerHolder.PTR) {
} }
} }
fun intersects(r: Region?): Boolean { /** Returns true if this region intersects [region]. */
fun intersects(region: Region): Boolean {
return try { return try {
Stats.onNativeCall() Stats.onNativeCall()
Region_nIntersectsRegion( Region_nIntersectsRegion(_ptr, getPtr(region))
_ptr,
getPtr(r)
)
} finally { } finally {
reachabilityBarrier(this) reachabilityBarrier(this)
reachabilityBarrier(r) reachabilityBarrier(region)
} }
} }
/** Returns true if point ([x], [y]) is inside this region. */
fun contains(x: Int, y: Int): Boolean { fun contains(x: Int, y: Int): Boolean {
return try { return try {
Stats.onNativeCall() Stats.onNativeCall()
...@@ -182,6 +250,7 @@ class Region : Managed(Region_nMake(), _FinalizerHolder.PTR) { ...@@ -182,6 +250,7 @@ class Region : Managed(Region_nMake(), _FinalizerHolder.PTR) {
} }
} }
/** Returns true if rectangle ([left], [top], [right], [bottom]) is fully inside this region. */
fun contains(left: Int, top: Int, right: Int, bottom: Int): Boolean { fun contains(left: Int, top: Int, right: Int, bottom: Int): Boolean {
return try { return try {
Stats.onNativeCall() Stats.onNativeCall()
...@@ -191,23 +260,31 @@ class Region : Managed(Region_nMake(), _FinalizerHolder.PTR) { ...@@ -191,23 +260,31 @@ class Region : Managed(Region_nMake(), _FinalizerHolder.PTR) {
} }
} }
/** Returns true if [rect] is fully inside this region. */
operator fun contains(rect: IRect): Boolean { operator fun contains(rect: IRect): Boolean {
return contains(rect.left, rect.top, rect.right, rect.bottom) return contains(rect.left, rect.top, rect.right, rect.bottom)
} }
operator fun contains(r: Region?): Boolean { /** Returns true if [region] is fully inside this region. */
operator fun contains(region: Region): Boolean {
return try { return try {
Stats.onNativeCall() Stats.onNativeCall()
Region_nContainsRegion(_ptr, getPtr(r)) Region_nContainsRegion(_ptr, getPtr(region))
} finally { } finally {
reachabilityBarrier(this) reachabilityBarrier(this)
reachabilityBarrier(r) reachabilityBarrier(region)
} }
} }
/**
* Fast conservative containment check.
* Returns true only when this region is a single rectangle containing [rect].
*/
fun quickContains(rect: IRect): Boolean { fun quickContains(rect: IRect): Boolean {
return quickContains(rect.left, rect.top, rect.right, rect.bottom) return quickContains(rect.left, rect.top, rect.right, rect.bottom)
} }
/** Fast conservative containment check for rectangle coordinates. */
fun quickContains(left: Int, top: Int, right: Int, bottom: Int): Boolean { fun quickContains(left: Int, top: Int, right: Int, bottom: Int): Boolean {
return try { return try {
Stats.onNativeCall() Stats.onNativeCall()
...@@ -217,9 +294,12 @@ class Region : Managed(Region_nMake(), _FinalizerHolder.PTR) { ...@@ -217,9 +294,12 @@ class Region : Managed(Region_nMake(), _FinalizerHolder.PTR) {
} }
} }
/** Fast conservative rejection check for [rect]. */
fun quickReject(rect: IRect): Boolean { fun quickReject(rect: IRect): Boolean {
return quickReject(rect.left, rect.top, rect.right, rect.bottom) return quickReject(rect.left, rect.top, rect.right, rect.bottom)
} }
/** Fast conservative rejection check for rectangle coordinates. */
fun quickReject(left: Int, top: Int, right: Int, bottom: Int): Boolean { fun quickReject(left: Int, top: Int, right: Int, bottom: Int): Boolean {
return try { return try {
Stats.onNativeCall() Stats.onNativeCall()
...@@ -229,19 +309,18 @@ class Region : Managed(Region_nMake(), _FinalizerHolder.PTR) { ...@@ -229,19 +309,18 @@ class Region : Managed(Region_nMake(), _FinalizerHolder.PTR) {
} }
} }
fun quickReject(r: Region?): Boolean { /** Fast conservative rejection check for [region]. */
fun quickReject(region: Region): Boolean {
return try { return try {
Stats.onNativeCall() Stats.onNativeCall()
Region_nQuickRejectRegion( Region_nQuickRejectRegion(_ptr, getPtr(region))
_ptr,
getPtr(r)
)
} finally { } finally {
reachabilityBarrier(this) reachabilityBarrier(this)
reachabilityBarrier(r) reachabilityBarrier(region)
} }
} }
/** Offsets this region by ([dx], [dy]). */
fun translate(dx: Int, dy: Int) { fun translate(dx: Int, dy: Int) {
try { try {
Stats.onNativeCall() Stats.onNativeCall()
...@@ -251,111 +330,73 @@ class Region : Managed(Region_nMake(), _FinalizerHolder.PTR) { ...@@ -251,111 +330,73 @@ class Region : Managed(Region_nMake(), _FinalizerHolder.PTR) {
} }
} }
/** Replaces this region with result of `this op rect`. */
fun op(rect: IRect, op: Op): Boolean { fun op(rect: IRect, op: Op): Boolean {
return op(rect.left, rect.top, rect.right, rect.bottom, op) return op(rect.left, rect.top, rect.right, rect.bottom, op)
} }
/** Replaces this region with result of `this op rect`. */
fun op(left: Int, top: Int, right: Int, bottom: Int, op: Op): Boolean { fun op(left: Int, top: Int, right: Int, bottom: Int, op: Op): Boolean {
return try { return try {
Stats.onNativeCall() Stats.onNativeCall()
Region_nOpIRect( Region_nOpIRect(_ptr, left, top, right, bottom, op.ordinal)
_ptr,
left,
top,
right,
bottom,
op.ordinal
)
} finally { } finally {
reachabilityBarrier(this) reachabilityBarrier(this)
} }
} }
fun op(r: Region?, op: Op): Boolean { /** Replaces this region with result of `this op region`. */
fun op(region: Region, op: Op): Boolean {
return try { return try {
Stats.onNativeCall() Stats.onNativeCall()
Region_nOpRegion( Region_nOpRegion(_ptr, getPtr(region), op.ordinal)
_ptr,
getPtr(r),
op.ordinal
)
} finally { } finally {
reachabilityBarrier(this) reachabilityBarrier(this)
reachabilityBarrier(r) reachabilityBarrier(region)
} }
} }
fun op(rect: IRect, r: Region?, op: Op): Boolean { /** Replaces this region with result of `rect op region`. */
return op( fun op(rect: IRect, region: Region, op: Op): Boolean {
rect.left, return op(rect.left, rect.top, rect.right, rect.bottom, region, op)
rect.top,
rect.right,
rect.bottom,
r,
op
)
} }
fun op(left: Int, top: Int, right: Int, bottom: Int, r: Region?, op: Op): Boolean { /** Replaces this region with result of `rect op region`. */
fun op(left: Int, top: Int, right: Int, bottom: Int, region: Region, op: Op): Boolean {
return try { return try {
Stats.onNativeCall() Stats.onNativeCall()
Region_nOpIRectRegion( Region_nOpIRectRegion(_ptr, left, top, right, bottom, getPtr(region), op.ordinal)
_ptr,
left,
top,
right,
bottom,
getPtr(r),
op.ordinal
)
} finally { } finally {
reachabilityBarrier(this) reachabilityBarrier(this)
reachabilityBarrier(r) reachabilityBarrier(region)
} }
} }
fun op(r: Region?, rect: IRect, op: Op): Boolean { /** Replaces this region with result of `region op rect`. */
return op( fun op(region: Region, rect: IRect, op: Op): Boolean {
r, return op(region, rect.left, rect.top, rect.right, rect.bottom, op)
rect.left,
rect.top,
rect.right,
rect.bottom,
op
)
} }
fun op(r: Region?, left: Int, top: Int, right: Int, bottom: Int, op: Op): Boolean { /** Replaces this region with result of `region op rect`. */
fun op(region: Region, left: Int, top: Int, right: Int, bottom: Int, op: Op): Boolean {
return try { return try {
Stats.onNativeCall() Stats.onNativeCall()
Region_nOpRegionIRect( Region_nOpRegionIRect(_ptr, getPtr(region), left, top, right, bottom, op.ordinal)
_ptr,
getPtr(r),
left,
top,
right,
bottom,
op.ordinal
)
} finally { } finally {
reachabilityBarrier(this) reachabilityBarrier(this)
reachabilityBarrier(r) reachabilityBarrier(region)
} }
} }
fun op(a: Region?, b: Region?, op: Op): Boolean { /** Replaces this region with result of `regionA op regionB`. */
fun op(regionA: Region, regionB: Region, op: Op): Boolean {
return try { return try {
Stats.onNativeCall() Stats.onNativeCall()
Region_nOpRegionRegion( Region_nOpRegionRegion(_ptr, getPtr(regionA), getPtr(regionB), op.ordinal)
_ptr,
getPtr(a),
getPtr(b),
op.ordinal
)
} finally { } finally {
reachabilityBarrier(this) reachabilityBarrier(this)
reachabilityBarrier(a) reachabilityBarrier(regionA)
reachabilityBarrier(b) reachabilityBarrier(regionB)
} }
} }
...@@ -368,7 +409,6 @@ class Region : Managed(Region_nMake(), _FinalizerHolder.PTR) { ...@@ -368,7 +409,6 @@ class Region : Managed(Region_nMake(), _FinalizerHolder.PTR) {
} }
} }
@ExternalSymbolName("org_jetbrains_skia_Region__1nMake") @ExternalSymbolName("org_jetbrains_skia_Region__1nMake")
private external fun Region_nMake(): NativePointer private external fun Region_nMake(): NativePointer
...@@ -385,7 +425,7 @@ private external fun Region_nIsRect(ptr: NativePointer): Boolean ...@@ -385,7 +425,7 @@ private external fun Region_nIsRect(ptr: NativePointer): Boolean
private external fun Region_nGetBounds(ptr: NativePointer, ltrb: InteropPointer) private external fun Region_nGetBounds(ptr: NativePointer, ltrb: InteropPointer)
@ExternalSymbolName("org_jetbrains_skia_Region__1nSet") @ExternalSymbolName("org_jetbrains_skia_Region__1nSet")
private external fun Region_nSet(ptr: NativePointer, regoinPtr: NativePointer): Boolean private external fun Region_nSet(ptr: NativePointer, regionPtr: NativePointer): Boolean
@ExternalSymbolName("org_jetbrains_skia_Region__1nIsComplex") @ExternalSymbolName("org_jetbrains_skia_Region__1nIsComplex")
private external fun Region_nIsComplex(ptr: NativePointer): Boolean private external fun Region_nIsComplex(ptr: NativePointer): Boolean
...@@ -393,8 +433,11 @@ private external fun Region_nIsComplex(ptr: NativePointer): Boolean ...@@ -393,8 +433,11 @@ private external fun Region_nIsComplex(ptr: NativePointer): Boolean
@ExternalSymbolName("org_jetbrains_skia_Region__1nComputeRegionComplexity") @ExternalSymbolName("org_jetbrains_skia_Region__1nComputeRegionComplexity")
private external fun Region_nComputeRegionComplexity(ptr: NativePointer): Int private external fun Region_nComputeRegionComplexity(ptr: NativePointer): Int
@ExternalSymbolName("org_jetbrains_skia_Region__1nAddBoundaryPath")
private external fun Region_nAddBoundaryPath(ptr: NativePointer, pathBuilderPtr: NativePointer): Boolean
@ExternalSymbolName("org_jetbrains_skia_Region__1nGetBoundaryPath") @ExternalSymbolName("org_jetbrains_skia_Region__1nGetBoundaryPath")
private external fun Region_nGetBoundaryPath(ptr: NativePointer, pathPtr: NativePointer): Boolean private external fun Region_nGetBoundaryPath(ptr: NativePointer): NativePointer
@ExternalSymbolName("org_jetbrains_skia_Region__1nSetEmpty") @ExternalSymbolName("org_jetbrains_skia_Region__1nSetEmpty")
private external fun Region_nSetEmpty(ptr: NativePointer): Boolean private external fun Region_nSetEmpty(ptr: NativePointer): Boolean
...@@ -455,7 +498,6 @@ private external fun Region_nOpIRectRegion( ...@@ -455,7 +498,6 @@ private external fun Region_nOpIRectRegion(
op: Int op: Int
): Boolean ): Boolean
@ExternalSymbolName("org_jetbrains_skia_Region__1nOpRegionIRect") @ExternalSymbolName("org_jetbrains_skia_Region__1nOpRegionIRect")
private external fun Region_nOpRegionIRect( private external fun Region_nOpRegionIRect(
ptr: NativePointer, ptr: NativePointer,
...@@ -467,7 +509,6 @@ private external fun Region_nOpRegionIRect( ...@@ -467,7 +509,6 @@ private external fun Region_nOpRegionIRect(
op: Int op: Int
): Boolean ): Boolean
@ExternalSymbolName("org_jetbrains_skia_Region__1nOpRegionRegion") @ExternalSymbolName("org_jetbrains_skia_Region__1nOpRegionRegion")
private external fun Region_nOpRegionRegion( private external fun Region_nOpRegionRegion(
ptr: NativePointer, ptr: NativePointer,
......
...@@ -3,6 +3,10 @@ package org.jetbrains.skia.paragraph ...@@ -3,6 +3,10 @@ package org.jetbrains.skia.paragraph
import org.jetbrains.skia.FontEdging import org.jetbrains.skia.FontEdging
import org.jetbrains.skia.FontHinting import org.jetbrains.skia.FontHinting
@Deprecated(
message = "Replaced by separate properties in TextStyle: fontEdging, fontHinting, subpixel",
level = DeprecationLevel.ERROR,
)
data class FontRastrSettings(val edging: FontEdging, data class FontRastrSettings(val edging: FontEdging,
val hinting: FontHinting, val hinting: FontHinting,
val subpixel: Boolean) val subpixel: Boolean)
...@@ -190,25 +190,19 @@ class ParagraphStyle : Managed(ParagraphStyle_nMake(), _FinalizerHolder.PTR) { ...@@ -190,25 +190,19 @@ class ParagraphStyle : Managed(ParagraphStyle_nMake(), _FinalizerHolder.PTR) {
return this return this
} }
@Suppress("DEPRECATION_ERROR")
@Deprecated(
message = "Replaced by separate properties in TextStyle: fontEdging, fontHinting, subpixel",
level = DeprecationLevel.ERROR,
)
var fontRastrSettings: FontRastrSettings var fontRastrSettings: FontRastrSettings
get() = try { get() = textStyle.let {
Stats.onNativeCall() FontRastrSettings(it.fontEdging, it.fontHinting, it.subpixel)
val edging = FontEdging.entries[_nGetEdging(_ptr)]
Stats.onNativeCall()
val hinting = FontHinting.entries[_nGetHinting(_ptr)]
Stats.onNativeCall()
// by some obscure reason kotlinjs makes difference between number encoded booleans returned from `_nGetSubpixel` and regular booleans
// AssertionError: Expected <FontRastrSettings(edging=ALIAS, hinting=NONE, subpixel=false)>, actual <FontRastrSettings(edging=ALIAS, hinting=NONE, subpixel=0)>
val subpixel = _nGetSubpixel(_ptr).not().not()
FontRastrSettings(edging, hinting, subpixel)
} finally {
reachabilityBarrier(this)
} }
set(value) = try { set(value) = textStyle.let {
Stats.onNativeCall() it.fontEdging = value.edging
_nSetFontRastrSettings(_ptr, value.edging.ordinal, value.hinting.ordinal, value.subpixel) it.fontHinting = value.hinting
} finally { it.subpixel = value.subpixel
reachabilityBarrier(this)
} }
var isApplyRoundingHackEnabled: Boolean var isApplyRoundingHackEnabled: Boolean
...@@ -317,18 +311,6 @@ private external fun _nIsHintingEnabled(ptr: NativePointer): Boolean ...@@ -317,18 +311,6 @@ private external fun _nIsHintingEnabled(ptr: NativePointer): Boolean
@ExternalSymbolName("org_jetbrains_skia_paragraph_ParagraphStyle__1nDisableHinting") @ExternalSymbolName("org_jetbrains_skia_paragraph_ParagraphStyle__1nDisableHinting")
private external fun _nDisableHinting(ptr: NativePointer) private external fun _nDisableHinting(ptr: NativePointer)
@ExternalSymbolName("org_jetbrains_skia_paragraph_ParagraphStyle__1nSetFontRastrSettings")
private external fun _nSetFontRastrSettings(ptr: NativePointer, edging: Int, hinting: Int, subpixel: Boolean)
@ExternalSymbolName("org_jetbrains_skia_paragraph_ParagraphStyle__1nGetEdging")
private external fun _nGetEdging(ptr: NativePointer): Int
@ExternalSymbolName("org_jetbrains_skia_paragraph_ParagraphStyle__1nGetHinting")
private external fun _nGetHinting(ptr: NativePointer): Int
@ExternalSymbolName("org_jetbrains_skia_paragraph_ParagraphStyle__1nGetSubpixel")
private external fun _nGetSubpixel(ptr: NativePointer): Boolean
@ExternalSymbolName("org_jetbrains_skia_paragraph_ParagraphStyle__1nGetApplyRoundingHack") @ExternalSymbolName("org_jetbrains_skia_paragraph_ParagraphStyle__1nGetApplyRoundingHack")
private external fun _nGetApplyRoundingHack(ptr: NativePointer): Boolean private external fun _nGetApplyRoundingHack(ptr: NativePointer): Boolean
......
...@@ -509,7 +509,6 @@ class TextStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finaliz ...@@ -509,7 +509,6 @@ class TextStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finaliz
return this return this
} }
val fontMetrics: FontMetrics val fontMetrics: FontMetrics
get() = try { get() = try {
Stats.onNativeCall() Stats.onNativeCall()
...@@ -529,11 +528,57 @@ class TextStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finaliz ...@@ -529,11 +528,57 @@ class TextStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finaliz
} }
fun setPlaceholder(): TextStyle { fun setPlaceholder(): TextStyle {
try {
Stats.onNativeCall() Stats.onNativeCall()
_nSetPlaceholder(_ptr) _nSetPlaceholder(_ptr)
} finally {
reachabilityBarrier(this)
}
return this return this
} }
var fontEdging: FontEdging
get() = try {
Stats.onNativeCall()
FontEdging.entries[_nGetFontEdging(_ptr)]
} finally {
reachabilityBarrier(this)
}
set(value) = try {
Stats.onNativeCall()
_nSetFontEdging(_ptr, value.ordinal)
} finally {
reachabilityBarrier(this)
}
var subpixel: Boolean
get() = try {
Stats.onNativeCall()
_nGetSubpixel(_ptr)
} finally {
reachabilityBarrier(this)
}
set(value) = try {
Stats.onNativeCall()
_nSetSubpixel(_ptr, value)
} finally {
reachabilityBarrier(this)
}
var fontHinting: FontHinting
get() = try {
Stats.onNativeCall()
FontHinting.entries[_nGetFontHinting(_ptr)]
} finally {
reachabilityBarrier(this)
}
set(value) = try {
Stats.onNativeCall()
_nSetFontHinting(_ptr, value.ordinal)
} finally {
reachabilityBarrier(this)
}
internal object _FinalizerHolder { internal object _FinalizerHolder {
val PTR = TextStyle_nGetFinalizer() val PTR = TextStyle_nGetFinalizer()
} }
...@@ -688,3 +733,21 @@ private external fun _nIsPlaceholder(ptr: NativePointer): Boolean ...@@ -688,3 +733,21 @@ private external fun _nIsPlaceholder(ptr: NativePointer): Boolean
@ExternalSymbolName("org_jetbrains_skia_paragraph_TextStyle__1nSetPlaceholder") @ExternalSymbolName("org_jetbrains_skia_paragraph_TextStyle__1nSetPlaceholder")
private external fun _nSetPlaceholder(ptr: NativePointer) private external fun _nSetPlaceholder(ptr: NativePointer)
@ExternalSymbolName("org_jetbrains_skia_paragraph_TextStyle__1nGetFontEdging")
private external fun _nGetFontEdging(ptr: NativePointer): Int
@ExternalSymbolName("org_jetbrains_skia_paragraph_TextStyle__1nSetFontEdging")
private external fun _nSetFontEdging(ptr: NativePointer, fontEdging: Int)
@ExternalSymbolName("org_jetbrains_skia_paragraph_TextStyle__1nGetSubpixel")
private external fun _nGetSubpixel(ptr: NativePointer): Boolean
@ExternalSymbolName("org_jetbrains_skia_paragraph_TextStyle__1nSetSubpixel")
private external fun _nSetSubpixel(ptr: NativePointer, subpixel: Boolean)
@ExternalSymbolName("org_jetbrains_skia_paragraph_TextStyle__1nGetFontHinting")
private external fun _nGetFontHinting(ptr: NativePointer): Int
@ExternalSymbolName("org_jetbrains_skia_paragraph_TextStyle__1nSetFontHinting")
private external fun _nSetFontHinting(ptr: NativePointer, fontHinting: Int)
...@@ -96,10 +96,12 @@ class ImageTest { ...@@ -96,10 +96,12 @@ class ImageTest {
return Surface.makeRasterN32Premul(100, 100).use { surface -> return Surface.makeRasterN32Premul(100, 100).use { surface ->
val paint = Paint() val paint = Paint()
paint.color = -0x10000 paint.color = -0x10000
Path().moveTo(20f, 80f) PathBuilder()
.moveTo(20f, 80f)
.lineTo(50f, 20f) .lineTo(50f, 20f)
.lineTo(80f, 80f) .lineTo(80f, 80f)
.closePath() .closePath()
.detach()
.use { path -> .use { path ->
val canvas = surface.canvas val canvas = surface.canvas
canvas.drawPath(path, paint) canvas.drawPath(path, paint)
......
package org.jetbrains.skia
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
class PathBuilderTest {
@Test
fun testConstructors() {
// Default constructor
val builder1 = PathBuilder()
assertNotNull(builder1)
// Constructor with fillType
val builder2 = PathBuilder(PathFillMode.EVEN_ODD)
val path2 = builder2.detach()
assertEquals(PathFillMode.EVEN_ODD, path2.fillMode)
// Constructor from existing path
val existingPath = PathBuilder().moveTo(10f, 10f).lineTo(20f, 20f).detach()
val builder3 = PathBuilder(existingPath)
assertNotNull(builder3)
}
@Test
fun testSnapshotAndDetach() {
val builder = PathBuilder().moveTo(0f, 0f).lineTo(10f, 10f)
// snapshot() creates a copy without consuming builder
val snapshot1 = builder.snapshot()
assertNotNull(snapshot1)
assertEquals(2, snapshot1.pointsCount)
// Builder is still usable after snapshot
val snapshot2 = builder.lineTo(20f, 20f).snapshot()
assertEquals(3, snapshot2.pointsCount)
// detach() returns path and consumes builder
val detached = builder.detach()
assertNotNull(detached)
assertEquals(3, detached.pointsCount)
}
@Test
fun testSetFillType() {
val path = PathBuilder()
.setFillType(PathFillMode.INVERSE_WINDING)
.moveTo(0f, 0f)
.detach()
assertEquals(PathFillMode.INVERSE_WINDING, path.fillMode)
}
@Test
fun testReset() {
val builder = PathBuilder()
.moveTo(10f, 10f)
.lineTo(20f, 20f)
.reset()
.moveTo(5f, 5f)
val path = builder.detach()
assertEquals(1, path.pointsCount)
assertEquals(Point(5f, 5f), path.getPoint(0))
}
@Test
fun testIncReserve() {
// Test that incReserve doesn't crash (performance optimization)
val path = PathBuilder()
.incReserve(100)
.moveTo(0f, 0f)
.lineTo(10f, 10f)
.detach()
assertEquals(2, path.pointsCount)
}
@Test
fun testMoveToAndLineTo() {
// Test moveTo(x, y)
val path1 = PathBuilder()
.moveTo(10f, 20f)
.lineTo(30f, 40f)
.detach()
assertEquals(2, path1.pointsCount)
assertEquals(Point(10f, 20f), path1.getPoint(0))
assertEquals(Point(30f, 40f), path1.getPoint(1))
// Test moveTo(Point) and lineTo(Point)
val path2 = PathBuilder()
.moveTo(Point(5f, 10f))
.lineTo(Point(15f, 20f))
.detach()
assertEquals(2, path2.pointsCount)
assertEquals(Point(5f, 10f), path2.getPoint(0))
}
@Test
fun testRelativeMoveToAndLineTo() {
val path = PathBuilder()
.moveTo(10f, 10f)
.rMoveTo(5f, 5f) // Should be at (15, 15)
.rLineTo(10f, 10f) // Should be at (25, 25)
.detach()
assertEquals(2, path.pointsCount)
assertEquals(Point(15f, 15f), path.getPoint(0))
assertEquals(Point(25f, 25f), path.getPoint(1))
}
@Test
fun testQuadTo() {
// Test quadTo(x1, y1, x2, y2)
val path1 = PathBuilder()
.moveTo(0f, 0f)
.quadTo(10f, 20f, 20f, 0f)
.detach()
assertEquals(3, path1.pointsCount)
// Test quadTo(Point, Point)
val path2 = PathBuilder()
.moveTo(0f, 0f)
.quadTo(Point(10f, 20f), Point(20f, 0f))
.detach()
assertEquals(3, path2.pointsCount)
// Test rQuadTo
val path3 = PathBuilder()
.moveTo(0f, 0f)
.rQuadTo(10f, 20f, 20f, 0f)
.detach()
assertEquals(3, path3.pointsCount)
}
@Test
fun testConicTo() {
// Test conicTo(x1, y1, x2, y2, w)
val path1 = PathBuilder()
.moveTo(0f, 0f)
.conicTo(10f, 20f, 20f, 0f, 0.5f)
.detach()
assertEquals(3, path1.pointsCount)
// Test conicTo(Point, Point, w)
val path2 = PathBuilder()
.moveTo(0f, 0f)
.conicTo(Point(10f, 20f), Point(20f, 0f), 0.5f)
.detach()
assertEquals(3, path2.pointsCount)
// Test rConicTo
val path3 = PathBuilder()
.moveTo(0f, 0f)
.rConicTo(10f, 20f, 20f, 0f, 0.5f)
.detach()
assertEquals(3, path3.pointsCount)
}
@Test
fun testCubicTo() {
// Test cubicTo(x1, y1, x2, y2, x3, y3)
val path1 = PathBuilder()
.moveTo(0f, 0f)
.cubicTo(10f, 20f, 15f, 30f, 20f, 0f)
.detach()
assertEquals(4, path1.pointsCount)
// Test cubicTo(Point, Point, Point)
val path2 = PathBuilder()
.moveTo(0f, 0f)
.cubicTo(Point(10f, 20f), Point(15f, 30f), Point(20f, 0f))
.detach()
assertEquals(4, path2.pointsCount)
// Test rCubicTo
val path3 = PathBuilder()
.moveTo(0f, 0f)
.rCubicTo(10f, 20f, 15f, 30f, 20f, 0f)
.detach()
assertEquals(4, path3.pointsCount)
}
@Test
fun testArcTo() {
// Test arcTo with Rect
val path1 = PathBuilder()
.moveTo(0f, 0f)
.arcTo(Rect.makeXYWH(10f, 10f, 20f, 20f), 0f, 90f, false)
.detach()
assertTrue(path1.pointsCount > 0)
// Test arcTo with coordinates
val path2 = PathBuilder()
.moveTo(0f, 0f)
.arcTo(10f, 10f, 30f, 30f, 0f, 90f, false)
.detach()
assertTrue(path2.pointsCount > 0)
}
@Test
fun testTangentArcTo() {
// Test tangentArcTo with coordinates
val path1 = PathBuilder()
.moveTo(0f, 0f)
.tangentArcTo(10f, 0f, 10f, 10f, 5f)
.detach()
assertTrue(path1.pointsCount > 0)
// Test tangentArcTo with Points
val path2 = PathBuilder()
.moveTo(0f, 0f)
.tangentArcTo(Point(10f, 0f), Point(10f, 10f), 5f)
.detach()
assertTrue(path2.pointsCount > 0)
}
@Test
fun testEllipticalArcTo() {
// Test ellipticalArcTo
val path1 = PathBuilder()
.moveTo(0f, 0f)
.ellipticalArcTo(10f, 5f, 0f, PathEllipseArc.SMALLER, PathDirection.CLOCKWISE, 20f, 0f)
.detach()
assertTrue(path1.pointsCount > 0)
// Test ellipticalArcTo with Points
val path2 = PathBuilder()
.moveTo(0f, 0f)
.ellipticalArcTo(Point(10f, 5f), 0f, PathEllipseArc.SMALLER, PathDirection.CLOCKWISE, Point(20f, 0f))
.detach()
assertTrue(path2.pointsCount > 0)
// Test rEllipticalArcTo
val path3 = PathBuilder()
.moveTo(0f, 0f)
.rEllipticalArcTo(10f, 5f, 0f, PathEllipseArc.SMALLER, PathDirection.CLOCKWISE, 20f, 0f)
.detach()
assertTrue(path3.pointsCount > 0)
}
@Test
fun testClosePath() {
val path = PathBuilder()
.moveTo(0f, 0f)
.lineTo(10f, 10f)
.lineTo(10f, 0f)
.closePath()
.detach()
assertTrue(path.isLastContourClosed)
assertEquals(3, path.pointsCount)
}
@Test
fun testAddRect() {
// Test addRect with Rect
val path1 = PathBuilder()
.addRect(Rect.makeXYWH(10f, 10f, 20f, 20f))
.detach()
assertTrue(path1.pointsCount >= 4)
// Test addRect with coordinates
val path2 = PathBuilder()
.addRect(10f, 10f, 30f, 30f, PathDirection.COUNTER_CLOCKWISE, 1)
.detach()
assertTrue(path2.pointsCount >= 4)
}
@Test
fun testAddOval() {
// Test addOval with Rect
val path1 = PathBuilder()
.addOval(Rect.makeXYWH(10f, 10f, 20f, 20f))
.detach()
assertTrue(path1.pointsCount > 4)
// Test addOval with coordinates
val path2 = PathBuilder()
.addOval(10f, 10f, 30f, 30f, PathDirection.CLOCKWISE, 2)
.detach()
assertTrue(path2.pointsCount > 4)
}
@Test
fun testAddCircle() {
val path = PathBuilder()
.addCircle(50f, 50f, 25f, PathDirection.CLOCKWISE)
.detach()
assertTrue(path.pointsCount > 4)
}
@Test
fun testAddArc() {
// Test addArc with Rect
val path1 = PathBuilder()
.addArc(Rect.makeXYWH(10f, 10f, 20f, 20f), 0f, 90f)
.detach()
assertTrue(path1.pointsCount > 0)
// Test addArc with coordinates
val path2 = PathBuilder()
.addArc(10f, 10f, 30f, 30f, 0f, 180f)
.detach()
assertTrue(path2.pointsCount > 0)
}
@Test
fun testAddRRect() {
// Test addRRect with RRect
val rrect = RRect.makeXYWH(10f, 10f, 20f, 20f, 5f)
val path1 = PathBuilder()
.addRRect(rrect)
.detach()
assertTrue(path1.pointsCount > 4)
// Test addRRect with coordinates and radii
val radii = floatArrayOf(5f, 5f, 5f, 5f, 5f, 5f, 5f, 5f)
val path2 = PathBuilder()
.addRRect(10f, 10f, 30f, 30f, radii, PathDirection.CLOCKWISE)
.detach()
assertTrue(path2.pointsCount > 4)
}
@Test
fun testAddPoly() {
// Test addPoly with Array<Point>
val points = arrayOf(Point(0f, 0f), Point(10f, 10f), Point(20f, 0f))
val path1 = PathBuilder()
.addPoly(points, true)
.detach()
assertEquals(3, path1.pointsCount)
// Test addPoly with FloatArray
val coords = floatArrayOf(0f, 0f, 10f, 10f, 20f, 0f)
val path2 = PathBuilder()
.addPoly(coords, false)
.detach()
assertEquals(3, path2.pointsCount)
}
@Test
fun testAddPath() {
val srcPath = PathBuilder()
.moveTo(0f, 0f)
.lineTo(10f, 10f)
.detach()
// Test addPath with mode=APPEND
// Note: moveTo alone doesn't add a point until a drawing command follows
val path1 = PathBuilder()
.moveTo(20f, 20f)
.addPath(srcPath, PathAddMode.APPEND)
.detach()
assertEquals(2, path1.pointsCount)
// Test addPath with offset
val path2 = PathBuilder()
.addPath(srcPath, 5f, 5f, PathAddMode.APPEND)
.detach()
assertEquals(Point(5f, 5f), path2.getPoint(0))
// Test addPath with matrix
val matrix = Matrix33.makeTranslate(10f, 10f)
val path3 = PathBuilder()
.addPath(srcPath, matrix, PathAddMode.APPEND)
.detach()
assertEquals(Point(10f, 10f), path3.getPoint(0))
}
@Test
fun testSetLastPt() {
val path = PathBuilder()
.moveTo(0f, 0f)
.lineTo(10f, 10f)
.setLastPt(15f, 15f)
.detach()
assertEquals(Point(15f, 15f), path.lastPt)
}
@Test
fun testBuilderChaining() {
// Verify that all methods return PathBuilder for chaining
val path = PathBuilder()
.setFillType(PathFillMode.WINDING)
.incReserve(10)
.moveTo(0f, 0f)
.lineTo(10f, 0f)
.lineTo(10f, 10f)
.lineTo(0f, 10f)
.closePath()
.detach()
assertEquals(4, path.pointsCount)
assertTrue(path.isLastContourClosed)
}
@Test
fun testComplexPath() {
// Test a complex path that uses multiple operations
val path = PathBuilder()
.moveTo(0f, 0f)
.lineTo(50f, 0f)
.quadTo(75f, 25f, 50f, 50f)
.cubicTo(40f, 60f, 30f, 60f, 20f, 50f)
.arcTo(0f, 25f, 25f, 50f, 180f, 90f, false)
.closePath()
.addCircle(100f, 100f, 20f)
.addRect(150f, 150f, 200f, 200f)
.detach()
assertTrue(path.pointsCount > 10)
assertNotNull(path.bounds)
}
}
...@@ -11,7 +11,7 @@ class PathMeasureTest { ...@@ -11,7 +11,7 @@ class PathMeasureTest {
@Test @Test
fun getRSXformTest() = runTest { fun getRSXformTest() = runTest {
Path().moveTo(0f, 0f).lineTo(40f, 0f).moveTo(0f, 40f).lineTo(10f, 50f).use { path -> PathBuilder().moveTo(0f, 0f).lineTo(40f, 0f).moveTo(0f, 40f).lineTo(10f, 50f).detach().use { path ->
PathMeasure(path, false).use { measure -> PathMeasure(path, false).use { measure ->
assertEquals(RSXform(1.0f, 0f, 0.5f, 0f), measure.getRSXform(0.5f)) assertEquals(RSXform(1.0f, 0f, 0.5f, 0f), measure.getRSXform(0.5f))
} }
...@@ -20,7 +20,7 @@ class PathMeasureTest { ...@@ -20,7 +20,7 @@ class PathMeasureTest {
@Test @Test
fun getTangent() = runTest { fun getTangent() = runTest {
Path().moveTo(0f, 0f).lineTo(20f, 0f).moveTo(0f, 40f).lineTo(30f, 50f).use { path -> PathBuilder().moveTo(0f, 0f).lineTo(20f, 0f).moveTo(0f, 40f).lineTo(30f, 50f).detach().use { path ->
PathMeasure(path, false).use { measure -> PathMeasure(path, false).use { measure ->
assertEquals(Point(1f, 0f), measure.getTangent(2f)) assertEquals(Point(1f, 0f), measure.getTangent(2f))
} }
...@@ -29,7 +29,7 @@ class PathMeasureTest { ...@@ -29,7 +29,7 @@ class PathMeasureTest {
@Test @Test
fun getPosition() = runTest { fun getPosition() = runTest {
Path().moveTo(0f, 10f).lineTo(20f, 0f).moveTo(0f, 40f).lineTo(30f, 50f).use { path -> PathBuilder().moveTo(0f, 10f).lineTo(20f, 0f).moveTo(0f, 40f).lineTo(30f, 50f).detach().use { path ->
PathMeasure(path, false).use { measure -> PathMeasure(path, false).use { measure ->
assertCloseEnough(Point(0.89442724f, 9.552787f), measure.getPosition(1f)!!) assertCloseEnough(Point(0.89442724f, 9.552787f), measure.getPosition(1f)!!)
} }
...@@ -40,9 +40,9 @@ class PathMeasureTest { ...@@ -40,9 +40,9 @@ class PathMeasureTest {
@Test @Test
@Ignore @Ignore
fun pathMeasureTest() = runTest { fun pathMeasureTest() = runTest {
Path().moveTo(0f, 0f).lineTo(40f, 0f).moveTo(0f, 40f).lineTo(10f, 50f).use { path -> PathBuilder().moveTo(0f, 0f).lineTo(40f, 0f).moveTo(0f, 40f).lineTo(10f, 50f).detach().use { path ->
PathMeasure(path, false).use { measure -> PathMeasure(path, false).use { measure ->
Path().lineTo(10f, 10f).use { path2 -> PathBuilder().lineTo(10f, 10f).detach().use { path2 ->
assertEquals(40f, measure.length) assertEquals(40f, measure.length)
assertCloseEnough(Point(0f, 0f), measure.getPosition(0f)!!) assertCloseEnough(Point(0f, 0f), measure.getPosition(0f)!!)
assertCloseEnough(Point(1f, 0f), measure.getTangent(0f)!!) assertCloseEnough(Point(1f, 0f), measure.getTangent(0f)!!)
......
package org.jetbrains.skia package org.jetbrains.skia
import org.jetbrains.skia.impl.Native.Companion.NullPointer
import org.jetbrains.skia.tests.assertCloseEnough import org.jetbrains.skia.tests.assertCloseEnough
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertContentEquals import kotlin.test.assertContentEquals
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertNotEquals import kotlin.test.assertNotEquals
import kotlin.test.assertNull
import kotlin.test.assertTrue
class PathTest { class PathTest {
@Test @Test
fun storageTest() { fun storageTest() {
val subpath: Path = Path().lineTo(40f, 40f).lineTo(40f, 0f).lineTo(0f, 40f).lineTo(0f, 0f).closePath() val subpath: Path = PathBuilder()
.lineTo(40f, 40f)
.lineTo(40f, 0f)
.lineTo(0f, 40f)
.lineTo(0f, 0f)
.closePath()
.detach()
for (p in arrayOf( for (p in arrayOf(
Path().addPath(subpath), PathBuilder().addPath(subpath).detach(),
Path().incReserve(10).addPath(subpath).closePath() PathBuilder().incReserve(10).addPath(subpath).closePath().detach()
)) { )) {
val p0 = Point(0f, 0f) val p0 = Point(0f, 0f)
val p1 = Point(40f, 40f) val p1 = Point(40f, 40f)
...@@ -29,21 +37,22 @@ class PathTest { ...@@ -29,21 +37,22 @@ class PathTest {
assertEquals(p3, p.getPoint(3)) assertEquals(p3, p.getPoint(3))
assertEquals(p4, p.getPoint(4)) assertEquals(p4, p.getPoint(4))
assertEquals(p4, p.lastPt) assertEquals(p4, p.lastPt)
p.lastPt = p5 // lastPt is now read-only, test setLastPt through PathBuilder
assertEquals(p5, p.getPoint(4)) val pModified = PathBuilder(p).setLastPt(p5.x, p5.y).detach()
assertEquals(p5, p.lastPt) assertEquals(p5, pModified.getPoint(4))
assertEquals(5, p.getPoints(null, 0)) assertEquals(p5, pModified.lastPt)
assertEquals(5, pModified.getPoints(null, 0))
var pts = arrayOfNulls<Point?>(5) var pts = arrayOfNulls<Point?>(5)
p.getPoints(pts, 5) pModified.getPoints(pts, 5)
assertContentEquals(arrayOf(p0, p1, p2, p3, p5), pts) assertContentEquals(arrayOf(p0, p1, p2, p3, p5), pts)
pts = arrayOfNulls(3) pts = arrayOfNulls(3)
p.getPoints(pts, 3) pModified.getPoints(pts, 3)
assertContentEquals(arrayOf(p0, p1, p2), pts) assertContentEquals(arrayOf(p0, p1, p2), pts)
pts = arrayOfNulls(5) pts = arrayOfNulls(5)
p.getPoints(pts, 3) pModified.getPoints(pts, 3)
assertContentEquals(arrayOf(p0, p1, p2, null, null), pts) assertContentEquals(arrayOf(p0, p1, p2, null, null), pts)
pts = arrayOfNulls(10) pts = arrayOfNulls(10)
p.getPoints(pts, 10) pModified.getPoints(pts, 10)
assertContentEquals(arrayOf(p0, p1, p2, p3, p5, null, null, null, null, null), pts) assertContentEquals(arrayOf(p0, p1, p2, p3, p5, null, null, null, null, null), pts)
assertEquals(6, p.verbsCount) assertEquals(6, p.verbsCount)
assertEquals(6, p.getVerbs(null, 0)) assertEquals(6, p.getVerbs(null, 0))
...@@ -81,16 +90,103 @@ class PathTest { ...@@ -81,16 +90,103 @@ class PathTest {
null null
), verbs ), verbs
) )
assertNotEquals(NullPointer, p.approximateBytesUsed) assertTrue(p.approximateBytesUsed > 0, "approximateBytesUsed should return positive size")
assertEquals(PathSegmentMask.LINE, p.segmentMasks) assertEquals(PathSegmentMask.LINE, p.segmentMasks)
} }
} }
@Test
fun emptyPath() {
val emptyPath = Path()
assertEquals(0, emptyPath.pointsCount)
assertNull(emptyPath.lastPt)
}
@Test @Test
fun canGetBounds() { fun canGetBounds() {
val path = Path().lineTo(40f, 40f).lineTo(40f, 0f).lineTo(0f, 40f).lineTo(0f, 0f).closePath() val path = PathBuilder().lineTo(40f, 40f).lineTo(40f, 0f).lineTo(0f, 40f).lineTo(0f, 0f).closePath().detach()
val bounds = Rect(0.0f, 0.0f, 40.0f, 40.0f) val bounds = Rect(0.0f, 0.0f, 40.0f, 40.0f)
assertCloseEnough(bounds, path.bounds) assertCloseEnough(bounds, path.bounds)
assertCloseEnough(bounds, path.computeTightBounds()) assertCloseEnough(bounds, path.computeTightBounds())
} }
@Test
fun rawTest() {
val pts = arrayOf(Point(0f, 0f), Point(10f, 10f), Point(20f, 0f))
val verbs = arrayOf(PathVerb.MOVE, PathVerb.LINE, PathVerb.LINE)
val path = Path.Raw(pts, verbs)
assertEquals(3, path.pointsCount)
assertEquals(3, path.verbsCount)
assertCloseEnough(Rect(0f, 0f, 20f, 10f), path.bounds)
}
@Test
fun rectTest() {
val rect = Rect(10f, 20f, 30f, 40f)
val path = Path.Rect(rect)
assertEquals(4, path.pointsCount) // 4 corners
assertEquals(rect, path.isRect)
assertCloseEnough(rect, path.bounds)
}
@Test
fun ovalTest() {
val rect = Rect(10f, 20f, 30f, 40f)
val path = Path.Oval(rect)
assertEquals(rect, path.isOval)
assertCloseEnough(rect, path.bounds)
}
@Test
fun circleTest() {
val centerX = 50f
val centerY = 50f
val radius = 25f
val path = Path.Circle(centerX, centerY, radius)
val expectedBounds = Rect(centerX - radius, centerY - radius, centerX + radius, centerY + radius)
assertCloseEnough(expectedBounds, path.bounds)
assertEquals(expectedBounds, path.isOval)
}
@Test
fun rrectTest() {
val rect = Rect(10f, 20f, 110f, 120f)
val rx = 10f
val ry = 10f
val path = Path.RRect(rect, rx, ry)
assertCloseEnough(rect, path.bounds)
val rrect = path.isRRect
assertEquals(rect, rrect?.let { Rect(it.left, it.top, it.right, it.bottom) })
}
@Test
fun polygonTest() {
val pts = arrayOf(Point(0f, 0f), Point(10f, 0f), Point(10f, 10f), Point(0f, 10f))
val pathClosed = Path.Polygon(pts, true)
val pathOpen = Path.Polygon(pts, false)
assertEquals(4, pathClosed.pointsCount)
assertEquals(4, pathOpen.pointsCount)
assertTrue(pathClosed.isLastContourClosed)
assertFalse(pathOpen.isLastContourClosed)
}
@Test
fun lineTest() {
val p0 = Point(10f, 20f)
val p1 = Point(30f, 40f)
val path = Path.Line(p0, p1)
assertEquals(2, path.pointsCount)
val line = path.asLine
assertEquals(2, line?.size)
assertEquals(p0, line?.get(0))
assertEquals(p1, line?.get(1))
}
} }
\ No newline at end of file
...@@ -16,7 +16,7 @@ import kotlin.test.assertNull ...@@ -16,7 +16,7 @@ import kotlin.test.assertNull
class PathTests { class PathTests {
@Test @Test
fun iterTest() = runTest { fun iterTest() = runTest {
Path().moveTo(10f, 10f).lineTo(20f, 0f).lineTo(20f, 20f).closePath().use { p -> PathBuilder().moveTo(10f, 10f).lineTo(20f, 0f).lineTo(20f, 20f).closePath().detach().use { p ->
p.iterator().use { i -> p.iterator().use { i ->
assertTrue(i.hasNext()) assertTrue(i.hasNext())
...@@ -60,8 +60,8 @@ class PathTests { ...@@ -60,8 +60,8 @@ class PathTests {
@Test @Test
fun convexityTest() = runTest { fun convexityTest() = runTest {
Path().lineTo(40f, 20f).lineTo(0f, 40f).lineTo(0f, 0f).closePath().use { p -> assertTrue(p.isConvex) } PathBuilder().lineTo(40f, 20f).lineTo(0f, 40f).lineTo(0f, 0f).closePath().detach().use { p -> assertTrue(p.isConvex) }
Path().lineTo(40f, 40f).lineTo(40f, 0f).lineTo(0f, 40f).lineTo(0f, 0f).closePath().use { p -> PathBuilder().lineTo(40f, 40f).lineTo(40f, 0f).lineTo(0f, 40f).lineTo(0f, 0f).closePath().detach().use { p ->
assertFalse(p.isConvex) assertFalse(p.isConvex)
} }
} }
...@@ -71,7 +71,7 @@ class PathTests { ...@@ -71,7 +71,7 @@ class PathTests {
fun isShapeTest() { fun isShapeTest() {
for (dir in PathDirection.entries) { for (dir in PathDirection.entries) {
for (start in 0..3) { for (start in 0..3) {
Path().addRect(Rect.makeLTRB(0f, 0f, 40f, 20f), dir, start).use { p -> PathBuilder().addRect(Rect.makeLTRB(0f, 0f, 40f, 20f), dir, start).detach().use { p ->
assertEquals(Rect.makeLTRB(0f, 0f, 40f, 20f), p.isRect) assertEquals(Rect.makeLTRB(0f, 0f, 40f, 20f), p.isRect)
assertNull(p.isOval) assertNull(p.isOval)
assertNull(p.isRRect) assertNull(p.isRRect)
...@@ -80,7 +80,7 @@ class PathTests { ...@@ -80,7 +80,7 @@ class PathTests {
} }
for (dir in PathDirection.entries) { for (dir in PathDirection.entries) {
for (start in 0..3) { for (start in 0..3) {
Path().addOval(Rect.makeLTRB(0f, 0f, 40f, 20f), dir, start).use { p -> PathBuilder().addOval(Rect.makeLTRB(0f, 0f, 40f, 20f), dir, start).detach().use { p ->
assertNull(p.isRect) assertNull(p.isRect)
assertEquals(Rect.makeLTRB(0f, 0f, 40f, 20f), p.isOval) assertEquals(Rect.makeLTRB(0f, 0f, 40f, 20f), p.isOval)
assertNull(p.isRRect) assertNull(p.isRRect)
...@@ -88,7 +88,7 @@ class PathTests { ...@@ -88,7 +88,7 @@ class PathTests {
} }
} }
for (dir in PathDirection.entries) { for (dir in PathDirection.entries) {
Path().addCircle(20f, 20f, 20f, dir).use { p -> PathBuilder().addCircle(20f, 20f, 20f, dir).detach().use { p ->
assertNull(p.isRect) assertNull(p.isRect)
assertEquals(Rect.makeLTRB(0f, 0f, 40f, 40f), p.isOval) assertEquals(Rect.makeLTRB(0f, 0f, 40f, 40f), p.isOval)
assertNull(p.isRRect) assertNull(p.isRRect)
...@@ -96,24 +96,24 @@ class PathTests { ...@@ -96,24 +96,24 @@ class PathTests {
} }
for (dir in PathDirection.entries) { for (dir in PathDirection.entries) {
for (start in 0..7) { for (start in 0..7) {
Path().addRRect(RRect.makeLTRB(0f, 0f, 40f, 20f, 5f), dir, start).use { p -> PathBuilder().addRRect(RRect.makeLTRB(0f, 0f, 40f, 20f, 5f), dir, start).detach().use { p ->
assertNull(p.isRect) assertNull(p.isRect)
assertNull(p.isOval) assertNull(p.isOval)
assertEquals(RRect.makeLTRB(0f, 0f, 40f, 20f, 5f), p.isRRect) assertEquals(RRect.makeLTRB(0f, 0f, 40f, 20f, 5f), p.isRRect)
} }
Path().addRRect(RRect.makeLTRB(0f, 0f, 40f, 20f, 0f), dir, start).use { p -> PathBuilder().addRRect(RRect.makeLTRB(0f, 0f, 40f, 20f, 0f), dir, start).detach().use { p ->
assertEquals(Rect.makeLTRB(0f, 0f, 40f, 20f), p.isRect) assertEquals(Rect.makeLTRB(0f, 0f, 40f, 20f), p.isRect)
assertNull(p.isOval) assertNull(p.isOval)
assertNull(p.isRRect) assertNull(p.isRRect)
} }
Path().addRRect(RRect.makeLTRB(0f, 0f, 40f, 20f, 20f, 10f), dir, start).use { p -> PathBuilder().addRRect(RRect.makeLTRB(0f, 0f, 40f, 20f, 20f, 10f), dir, start).detach().use { p ->
assertNull(p.isRect) assertNull(p.isRect)
assertEquals(Rect.makeLTRB(0f, 0f, 40f, 20f), p.isOval) assertEquals(Rect.makeLTRB(0f, 0f, 40f, 20f), p.isOval)
assertNull(p.isRRect) assertNull(p.isRRect)
} }
} }
} }
Path().lineTo(40f, 40f).lineTo(40f, 0f).lineTo(0f, 40f).lineTo(0f, 0f).closePath().use { p -> PathBuilder().lineTo(40f, 40f).lineTo(40f, 0f).lineTo(0f, 40f).lineTo(0f, 0f).closePath().detach().use { p ->
assertNull(p.isRect) assertNull(p.isRect)
assertNull(p.isOval) assertNull(p.isOval)
assertNull(p.isRRect) assertNull(p.isRRect)
...@@ -122,43 +122,42 @@ class PathTests { ...@@ -122,43 +122,42 @@ class PathTests {
@Test @Test
fun checksTest() { fun checksTest() {
Path().lineTo(40f, 40f).lineTo(40f, 0f).lineTo(0f, 40f).lineTo(0f, 0f).closePath().use { p -> PathBuilder().lineTo(40f, 40f).lineTo(40f, 0f).lineTo(0f, 40f).lineTo(0f, 0f).closePath().detach().use { p ->
assertFalse(p.isEmpty) assertFalse(p.isEmpty)
p.reset()
assertTrue(p.isEmpty)
} }
Path().lineTo(40f, 40f).lineTo(40f, 0f).lineTo(0f, 40f).lineTo(0f, 0f).closePath().use { p -> Path().use { p ->
assertFalse(p.isEmpty)
p.rewind()
assertTrue(p.isEmpty) assertTrue(p.isEmpty)
} }
Path().lineTo(40f, 40f).lineTo(40f, 0f).lineTo(0f, 40f).lineTo(0f, 0f).use { p -> PathBuilder().lineTo(40f, 40f).lineTo(40f, 0f).lineTo(0f, 40f).lineTo(0f, 0f).detach().use { p ->
assertFalse(p.isLastContourClosed) assertFalse(p.isLastContourClosed)
p.closePath() }
PathBuilder().lineTo(40f, 40f).lineTo(40f, 0f).lineTo(0f, 40f).lineTo(0f, 0f).closePath().detach().use { p ->
assertTrue(p.isLastContourClosed) assertTrue(p.isLastContourClosed)
p.moveTo(100f, 100f).lineTo(140f, 140f).lineTo(140f, 100f).lineTo(100f, 140f) }
PathBuilder().moveTo(100f, 100f).lineTo(140f, 140f).lineTo(140f, 100f).lineTo(100f, 140f).detach().use { p ->
assertFalse(p.isLastContourClosed) assertFalse(p.isLastContourClosed)
p.closePath() }
PathBuilder().moveTo(100f, 100f).lineTo(140f, 140f).lineTo(140f, 100f).lineTo(100f, 140f).closePath().detach().use { p ->
assertTrue(p.isLastContourClosed) assertTrue(p.isLastContourClosed)
} }
Path().lineTo(40f, 40f).lineTo(40f, 0f).lineTo(0f, 40f).lineTo(0f, 0f).use { p -> assertTrue(p.isFinite) } PathBuilder().lineTo(40f, 40f).lineTo(40f, 0f).lineTo(0f, 40f).lineTo(0f, 0f).detach().use { p -> assertTrue(p.isFinite) }
Path().lineTo(40f, 40f).lineTo(Float.POSITIVE_INFINITY, 0f).lineTo(0f, 40f).lineTo(0f, 0f).closePath().use { p -> PathBuilder().lineTo(40f, 40f).lineTo(Float.POSITIVE_INFINITY, 0f).lineTo(0f, 40f).lineTo(0f, 0f).closePath().detach().use { p ->
assertEquals( assertEquals(
false, false,
p.isFinite p.isFinite
) )
} }
Path().lineTo(40f, 40f).lineTo(40f, 0f).lineTo(0f, 40f).lineTo(0f, 0f).use { p -> PathBuilder().lineTo(40f, 40f).lineTo(40f, 0f).lineTo(0f, 40f).lineTo(0f, 0f).detach().use { p ->
assertFalse(p.isVolatile) assertFalse(p.isVolatile)
p.setVolatile(true) p.setVolatile(true)
assertTrue(p.isVolatile) assertTrue(p.isVolatile)
p.setVolatile(false) p.setVolatile(false)
assertFalse(p.isVolatile) assertFalse(p.isVolatile)
} }
Path().lineTo(40f, 40f).lineTo(40f, 0f).lineTo(0f, 40f).lineTo(0f, 0f).closePath().use { p -> PathBuilder().lineTo(40f, 40f).lineTo(40f, 0f).lineTo(0f, 40f).lineTo(0f, 0f).closePath().detach().use { p ->
assertNull(p.asLine) assertNull(p.asLine)
} }
Path().moveTo(20f, 20f).lineTo(40f, 40f).use { p -> PathBuilder().moveTo(20f, 20f).lineTo(40f, 40f).detach().use { p ->
assertContentEquals( assertContentEquals(
arrayOf( arrayOf(
Point(20f, 20f), Point(20f, 20f),
...@@ -170,18 +169,18 @@ class PathTests { ...@@ -170,18 +169,18 @@ class PathTests {
@Test @Test
fun swapTest() { fun swapTest() {
Path().lineTo(40f, 40f).lineTo(40f, 0f).lineTo(0f, 40f).lineTo(0f, 0f).closePath().use { p1 -> PathBuilder().lineTo(40f, 40f).lineTo(40f, 0f).lineTo(0f, 40f).lineTo(0f, 0f).closePath().detach().use { p1 ->
Path().lineTo(0f, 0f).lineTo(20f, 20f).use { p2 -> PathBuilder().lineTo(0f, 0f).lineTo(20f, 20f).detach().use { p2 ->
p1.swap(p2) p1.swap(p2)
assertEquals(Path().lineTo(0f, 0f).lineTo(20f, 20f), p1) assertEquals(PathBuilder().lineTo(0f, 0f).lineTo(20f, 20f).detach(), p1)
assertEquals(Path().lineTo(40f, 40f).lineTo(40f, 0f).lineTo(0f, 40f).lineTo(0f, 0f).closePath(), p2) assertEquals(PathBuilder().lineTo(40f, 40f).lineTo(40f, 0f).lineTo(0f, 40f).lineTo(0f, 0f).closePath().detach(), p2)
} }
} }
} }
@Test @Test
fun containsTest() { fun containsTest() {
Path().addRRect(RRect.makeLTRB(10f, 20f, 54f, 120f, 10f, 20f)).use { p -> PathBuilder().addRRect(RRect.makeLTRB(10f, 20f, 54f, 120f, 10f, 20f)).detach().use { p ->
assertTrue(p.conservativelyContainsRect(Rect.makeLTRB(10f, 40f, 54f, 80f))) assertTrue(p.conservativelyContainsRect(Rect.makeLTRB(10f, 40f, 54f, 80f)))
assertTrue(p.conservativelyContainsRect(Rect.makeLTRB(25f, 20f, 39f, 120f))) assertTrue(p.conservativelyContainsRect(Rect.makeLTRB(25f, 20f, 39f, 120f)))
assertTrue(p.conservativelyContainsRect(Rect.makeLTRB(15f, 25f, 49f, 115f))) assertTrue(p.conservativelyContainsRect(Rect.makeLTRB(15f, 25f, 49f, 115f)))
...@@ -215,20 +214,20 @@ class PathTests { ...@@ -215,20 +214,20 @@ class PathTests {
), ),
Path.convertConicToQuads(Point(0f, 20f), Point(20f, 0f), Point(40f, 20f), 0.5f, 2) Path.convertConicToQuads(Point(0f, 20f), Point(20f, 0f), Point(40f, 20f), 0.5f, 2)
) )
Path().lineTo(40f, 40f).use { p -> PathBuilder().lineTo(40f, 40f).detach().use { p ->
val g1 = p.generationId val g1 = p.generationId
p.lineTo(10f, 40f) val p2 = PathBuilder(p).lineTo(10f, 40f).detach()
val g2 = p.generationId val g2 = p2.generationId
assertNotEquals(g1, g2) assertNotEquals(g1, g2)
p.fillMode = PathFillMode.EVEN_ODD val p3 = PathBuilder(p2).lineTo(20f, 20f).detach()
val g3 = p.generationId val g3 = p3.generationId
assertEquals(g2, g3) assertNotEquals(g2, g3)
} }
} }
@Test @Test
fun serializeTest() { fun serializeTest() {
Path().lineTo(40f, 40f).lineTo(40f, 0f).lineTo(0f, 40f).lineTo(0f, 0f).closePath().use { p -> PathBuilder().lineTo(40f, 40f).lineTo(40f, 0f).lineTo(0f, 40f).lineTo(0f, 0f).closePath().detach().use { p ->
val p2: Path = Path.makeFromBytes(p.serializeToBytes()) val p2: Path = Path.makeFromBytes(p.serializeToBytes())
assertEquals(p, p2) assertEquals(p, p2)
} }
......
...@@ -13,7 +13,7 @@ class PathUtilsTest { ...@@ -13,7 +13,7 @@ class PathUtilsTest {
strokeWidth = 10f strokeWidth = 10f
mode = PaintMode.STROKE mode = PaintMode.STROKE
} }
val path = Path().lineTo(40f, 40f) val path = PathBuilder().lineTo(40f, 40f).detach()
val fillPath = PathUtils.fillPathWithPaint(path, paint) val fillPath = PathUtils.fillPathWithPaint(path, paint)
...@@ -29,7 +29,7 @@ class PathUtilsTest { ...@@ -29,7 +29,7 @@ class PathUtilsTest {
strokeWidth = 10f strokeWidth = 10f
mode = PaintMode.STROKE mode = PaintMode.STROKE
} }
val path = Path().arcTo(0f, 0f, 40f, 40f, 0f, 90f, false) val path = PathBuilder().arcTo(0f, 0f, 40f, 40f, 0f, 90f, false).detach()
val fillPath1 = PathUtils.fillPathWithPaint(path, paint,null, 1f) val fillPath1 = PathUtils.fillPathWithPaint(path, paint,null, 1f)
val fillPath001 = PathUtils.fillPathWithPaint(path, paint,null, 0.01f) val fillPath001 = PathUtils.fillPathWithPaint(path, paint,null, 0.01f)
......
...@@ -3,6 +3,7 @@ package org.jetbrains.skia ...@@ -3,6 +3,7 @@ package org.jetbrains.skia
import org.jetbrains.skia.tests.assertCloseEnough import org.jetbrains.skia.tests.assertCloseEnough
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertTrue
class PictureTest { class PictureTest {
@Test @Test
...@@ -31,6 +32,8 @@ class PictureTest { ...@@ -31,6 +32,8 @@ class PictureTest {
val canvas = recorder.beginRecording(size) val canvas = recorder.beginRecording(size)
canvas.drawRect(Rect(10.0f, 10.0f, 20.0f, 20.0f), Paint().apply { color = Color.RED }) canvas.drawRect(Rect(10.0f, 10.0f, 20.0f, 20.0f), Paint().apply { color = Color.RED })
val pic = recorder.finishRecordingAsPicture() val pic = recorder.finishRecordingAsPicture()
assertTrue(pic.approximateBytesUsed > 0)
assertTrue(pic.approximateOpCount > 0)
val surface = Surface.makeRasterN32Premul(32, 32) val surface = Surface.makeRasterN32Premul(32, 32)
pic.playback(surface.canvas) pic.playback(surface.canvas)
......
package org.jetbrains.skiko.paragraph package org.jetbrains.skiko.paragraph
import org.jetbrains.skia.FontEdging
import org.jetbrains.skia.FontHinting
import org.jetbrains.skia.impl.use import org.jetbrains.skia.impl.use
import org.jetbrains.skia.paragraph.FontRastrSettings
import org.jetbrains.skia.paragraph.ParagraphStyle import org.jetbrains.skia.paragraph.ParagraphStyle
import org.jetbrains.skia.paragraph.TextIndent import org.jetbrains.skia.paragraph.TextIndent
import kotlin.test.Test import kotlin.test.Test
...@@ -36,26 +33,6 @@ class ParagraphStyleTests { ...@@ -36,26 +33,6 @@ class ParagraphStyleTests {
} }
} }
@Test
fun paragraphStyleFontRastrSettingsTests() {
ParagraphStyle().use { paragraphStyle ->
val poorRasterSettings = FontRastrSettings(
edging = FontEdging.ALIAS,
hinting = FontHinting.NONE,
subpixel = false)
paragraphStyle.fontRastrSettings = poorRasterSettings
assertEquals(poorRasterSettings, paragraphStyle.fontRastrSettings)
val gloriousRasterSettings = FontRastrSettings(
edging = FontEdging.SUBPIXEL_ANTI_ALIAS,
hinting = FontHinting.FULL,
subpixel = true)
paragraphStyle.fontRastrSettings = gloriousRasterSettings
assertEquals(gloriousRasterSettings, paragraphStyle.fontRastrSettings)
}
}
@Test @Test
fun paragraphStyleRoundingHackTests() { fun paragraphStyleRoundingHackTests() {
ParagraphStyle().use { paragraphStyle -> ParagraphStyle().use { paragraphStyle ->
......
...@@ -2,12 +2,7 @@ package org.jetbrains.skiko.paragraph ...@@ -2,12 +2,7 @@ package org.jetbrains.skiko.paragraph
import org.jetbrains.skia.* import org.jetbrains.skia.*
import org.jetbrains.skia.impl.use import org.jetbrains.skia.impl.use
import org.jetbrains.skia.jbMonoPath import org.jetbrains.skia.paragraph.*
import org.jetbrains.skia.paragraph.DecorationLineStyle
import org.jetbrains.skia.paragraph.DecorationStyle
import org.jetbrains.skia.paragraph.Shadow
import org.jetbrains.skia.paragraph.TextStyle
import org.jetbrains.skia.paragraph.TextStyleAttribute
import org.jetbrains.skia.tests.assertContentCloseEnough import org.jetbrains.skia.tests.assertContentCloseEnough
import org.jetbrains.skia.tests.makeFromResource import org.jetbrains.skia.tests.makeFromResource
import org.jetbrains.skiko.KotlinBackend import org.jetbrains.skiko.KotlinBackend
...@@ -152,6 +147,33 @@ class TextStyleTest { ...@@ -152,6 +147,33 @@ class TextStyleTest {
} }
} }
@Test
fun textStyleFontEdgingTest() {
TextStyle().use { textStyle ->
assertEquals(FontEdging.ANTI_ALIAS, textStyle.fontEdging)
textStyle.fontEdging = FontEdging.SUBPIXEL_ANTI_ALIAS
assertEquals(FontEdging.SUBPIXEL_ANTI_ALIAS, textStyle.fontEdging)
}
}
@Test
fun textStyleSubpixelTest() {
TextStyle().use { textStyle ->
assertTrue(textStyle.subpixel)
textStyle.subpixel = false
assertFalse(textStyle.subpixel)
}
}
@Test
fun textStyleFontHintingTest() {
TextStyle().use { textStyle ->
assertEquals(FontHinting.SLIGHT, textStyle.fontHinting)
textStyle.fontHinting = FontHinting.FULL
assertEquals(FontHinting.FULL, textStyle.fontHinting)
}
}
@Test @Test
fun textStyleMetricsContainsMeaningfulValues() = runTest { fun textStyleMetricsContainsMeaningfulValues() = runTest {
val jbMono = Typeface.makeFromResource(jbMonoPath) val jbMono = Typeface.makeFromResource(jbMonoPath)
...@@ -171,5 +193,4 @@ class TextStyleTest { ...@@ -171,5 +193,4 @@ class TextStyleTest {
assertTrue(!textStyle.fontMetrics.ascent.isNaN()) assertTrue(!textStyle.fontMetrics.ascent.isNaN())
} }
} }
} }
package org.jetbrains.skiko.tests package org.jetbrains.skiko.tests
import kotlinx.coroutines.MainScope import kotlinx.coroutines.MainScope
import kotlinx.coroutines.await
import kotlinx.coroutines.promise import kotlinx.coroutines.promise
import org.jetbrains.skiko.wasm.awaitSkiko import org.jetbrains.skiko.wasm.awaitSkiko
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import kotlin.coroutines.suspendCoroutine
import kotlin.js.Promise
private suspend fun <T> Promise<T>.await(): T = suspendCoroutine { cont ->
then({ cont.resume(it) }, { cont.resumeWithException(it) })
}
actual typealias TestReturnType = Any actual typealias TestReturnType = Any
/** /**
......
...@@ -37,7 +37,7 @@ extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skia_CanvasKt__1nDrawPoints ...@@ -37,7 +37,7 @@ extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skia_CanvasKt__1nDrawPoints
SkCanvas::PointMode skMode = static_cast<SkCanvas::PointMode>(mode); SkCanvas::PointMode skMode = static_cast<SkCanvas::PointMode>(mode);
jsize len = env->GetArrayLength(coords); jsize len = env->GetArrayLength(coords);
jfloat* arr = static_cast<jfloat*>(env->GetPrimitiveArrayCritical(coords, 0)); jfloat* arr = static_cast<jfloat*>(env->GetPrimitiveArrayCritical(coords, 0));
canvas->drawPoints(skMode, len / 2, reinterpret_cast<SkPoint*>(arr), *paint); canvas->drawPoints(skMode, {reinterpret_cast<SkPoint*>(arr), len / 2}, *paint);
env->ReleasePrimitiveArrayCritical(coords, arr, 0); env->ReleasePrimitiveArrayCritical(coords, arr, 0);
} }
......
...@@ -206,7 +206,7 @@ extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skia_FontKt__1nGetUTF32Glyp ...@@ -206,7 +206,7 @@ extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skia_FontKt__1nGetUTF32Glyp
SkFont* instance = reinterpret_cast<SkFont*>(static_cast<uintptr_t>(ptr)); SkFont* instance = reinterpret_cast<SkFont*>(static_cast<uintptr_t>(ptr));
std::vector<jshort> glyphs(uniArrLen); std::vector<jshort> glyphs(uniArrLen);
jint* uni = env->GetIntArrayElements(uniArr, nullptr); jint* uni = env->GetIntArrayElements(uniArr, nullptr);
instance->unicharsToGlyphs(reinterpret_cast<SkUnichar*>(uni), uniArrLen, reinterpret_cast<SkGlyphID*>(glyphs.data())); instance->unicharsToGlyphs({reinterpret_cast<SkUnichar*>(uni), uniArrLen}, {reinterpret_cast<SkGlyphID*>(glyphs.data()), uniArrLen});
env->ReleaseIntArrayElements(uniArr, uni, 0); env->ReleaseIntArrayElements(uniArr, uni, 0);
env->SetShortArrayRegion(resultGlyphs, 0, uniArrLen, glyphs.data()); env->SetShortArrayRegion(resultGlyphs, 0, uniArrLen, glyphs.data());
} }
...@@ -253,7 +253,7 @@ extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skia_FontKt__1nGetWidths ...@@ -253,7 +253,7 @@ extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skia_FontKt__1nGetWidths
SkFont* instance = reinterpret_cast<SkFont*>(static_cast<uintptr_t>(ptr)); SkFont* instance = reinterpret_cast<SkFont*>(static_cast<uintptr_t>(ptr));
std::vector<jfloat> widths(count); std::vector<jfloat> widths(count);
jshort* glyphs = env->GetShortArrayElements(glyphsArr, nullptr); jshort* glyphs = env->GetShortArrayElements(glyphsArr, nullptr);
instance->getWidths(reinterpret_cast<SkGlyphID*>(glyphs), count, widths.data()); instance->getWidths({reinterpret_cast<SkGlyphID*>(glyphs), count}, {widths.data(), count});
env->ReleaseShortArrayElements(glyphsArr, glyphs, 0); env->ReleaseShortArrayElements(glyphsArr, glyphs, 0);
env->SetFloatArrayRegion(res, 0, count, widths.data()); env->SetFloatArrayRegion(res, 0, count, widths.data());
} }
...@@ -264,7 +264,7 @@ extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skia_FontKt__1nGetBounds ...@@ -264,7 +264,7 @@ extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skia_FontKt__1nGetBounds
SkPaint* paint = reinterpret_cast<SkPaint*>(static_cast<uintptr_t>(paintPtr)); SkPaint* paint = reinterpret_cast<SkPaint*>(static_cast<uintptr_t>(paintPtr));
std::vector<SkRect> bounds(count); std::vector<SkRect> bounds(count);
jshort* glyphs = env->GetShortArrayElements(glyphsArr, nullptr); jshort* glyphs = env->GetShortArrayElements(glyphsArr, nullptr);
instance->getBounds(reinterpret_cast<SkGlyphID*>(glyphs), count, bounds.data(), paint); instance->getBounds({reinterpret_cast<SkGlyphID*>(glyphs), count}, {bounds.data(), count}, paint);
env->ReleaseShortArrayElements(glyphsArr, glyphs, 0); env->ReleaseShortArrayElements(glyphsArr, glyphs, 0);
for (int i = 0; i < count; ++i) { for (int i = 0; i < count; ++i) {
...@@ -279,7 +279,7 @@ extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skia_FontKt__1nGetPositions ...@@ -279,7 +279,7 @@ extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skia_FontKt__1nGetPositions
SkFont* instance = reinterpret_cast<SkFont*>(static_cast<uintptr_t>(ptr)); SkFont* instance = reinterpret_cast<SkFont*>(static_cast<uintptr_t>(ptr));
std::vector<SkPoint> positions(count); std::vector<SkPoint> positions(count);
jshort* glyphs = env->GetShortArrayElements(glyphsArr, nullptr); jshort* glyphs = env->GetShortArrayElements(glyphsArr, nullptr);
instance->getPos(reinterpret_cast<SkGlyphID*>(glyphs), count, positions.data(), {dx, dy}); instance->getPos({reinterpret_cast<SkGlyphID*>(glyphs), count}, {positions.data(), count}, {dx, dy});
env->ReleaseShortArrayElements(glyphsArr, glyphs, 0); env->ReleaseShortArrayElements(glyphsArr, glyphs, 0);
std::vector<jfloat> r(count * 2); std::vector<jfloat> r(count * 2);
...@@ -296,7 +296,7 @@ extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skia_FontKt__1nGetXPosition ...@@ -296,7 +296,7 @@ extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skia_FontKt__1nGetXPosition
SkFont* instance = reinterpret_cast<SkFont*>(static_cast<uintptr_t>(ptr)); SkFont* instance = reinterpret_cast<SkFont*>(static_cast<uintptr_t>(ptr));
std::vector<jfloat> positions(count); std::vector<jfloat> positions(count);
jshort* glyphs = env->GetShortArrayElements(glyphsArr, nullptr); jshort* glyphs = env->GetShortArrayElements(glyphsArr, nullptr);
instance->getXPos(reinterpret_cast<SkGlyphID*>(glyphs), count, positions.data(), dx); instance->getXPos({reinterpret_cast<SkGlyphID*>(glyphs), count}, {positions.data(), count}, dx);
env->ReleaseShortArrayElements(glyphsArr, glyphs, 0); env->ReleaseShortArrayElements(glyphsArr, glyphs, 0);
env->SetFloatArrayRegion(res, 0, count, positions.data()); env->SetFloatArrayRegion(res, 0, count, positions.data());
} }
...@@ -304,9 +304,12 @@ extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skia_FontKt__1nGetXPosition ...@@ -304,9 +304,12 @@ extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skia_FontKt__1nGetXPosition
extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skia_FontKt__1nGetPath extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skia_FontKt__1nGetPath
(JNIEnv* env, jclass jclass, jlong ptr, jshort glyph) { (JNIEnv* env, jclass jclass, jlong ptr, jshort glyph) {
SkFont* instance = reinterpret_cast<SkFont*>(static_cast<uintptr_t>(ptr)); SkFont* instance = reinterpret_cast<SkFont*>(static_cast<uintptr_t>(ptr));
SkPath* path = new SkPath(); std::optional<SkPath> pathOpt = instance->getPath(glyph);
instance->getPath(glyph, path); if (pathOpt.has_value()) {
SkPath* path = new SkPath(pathOpt.value());
return reinterpret_cast<jlong>(path); return reinterpret_cast<jlong>(path);
}
return 0;
} }
extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skia_FontKt__1nGetPaths extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skia_FontKt__1nGetPaths
...@@ -319,11 +322,10 @@ extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skia_FontKt__1nGetPaths ...@@ -319,11 +322,10 @@ extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skia_FontKt__1nGetPaths
std::vector<jlong>* paths; std::vector<jlong>* paths;
} ctx = { new std::vector<jlong>() }; } ctx = { new std::vector<jlong>() };
instance->getPaths(reinterpret_cast<SkGlyphID*>(glyphs), count, [](const SkPath* orig, const SkMatrix& mx, void* voidCtx) { instance->getPaths({reinterpret_cast<SkGlyphID*>(glyphs), count}, [](const SkPath* orig, const SkMatrix& mx, void* voidCtx) {
Ctx* ctx = static_cast<Ctx*>(voidCtx); Ctx* ctx = static_cast<Ctx*>(voidCtx);
if (orig) { if (orig) {
SkPath* path = new SkPath(); SkPath* path = new SkPath(orig->makeTransform(mx));
orig->transform(mx, path);
ctx->paths->push_back(reinterpret_cast<jlong>(path)); ctx->paths->push_back(reinterpret_cast<jlong>(path));
} }
}, &ctx); }, &ctx);
......
This diff is collapsed.
This diff is collapsed.
...@@ -57,7 +57,7 @@ extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skia_PathEffectKt__1nMakeD ...@@ -57,7 +57,7 @@ extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skia_PathEffectKt__1nMakeD
(JNIEnv* env, jclass jclass, jfloatArray intervalsArray, jint _count, jfloat phase) { (JNIEnv* env, jclass jclass, jfloatArray intervalsArray, jint _count, jfloat phase) {
jsize len = env->GetArrayLength(intervalsArray); jsize len = env->GetArrayLength(intervalsArray);
jfloat* intervals = env->GetFloatArrayElements(intervalsArray, 0); jfloat* intervals = env->GetFloatArrayElements(intervalsArray, 0);
SkPathEffect* ptr = SkDashPathEffect::Make(intervals, len, phase).release(); SkPathEffect* ptr = SkDashPathEffect::Make({intervals, len}, phase).release();
env->ReleaseFloatArrayElements(intervalsArray, intervals, 0); env->ReleaseFloatArrayElements(intervalsArray, intervals, 0);
return reinterpret_cast<jlong>(ptr); return reinterpret_cast<jlong>(ptr);
} }
......
...@@ -71,10 +71,10 @@ extern "C" JNIEXPORT jint JNICALL Java_org_jetbrains_skia_PictureKt__1nGetApprox ...@@ -71,10 +71,10 @@ extern "C" JNIEXPORT jint JNICALL Java_org_jetbrains_skia_PictureKt__1nGetApprox
return instance->approximateOpCount(); return instance->approximateOpCount();
} }
extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skia_PictureKt__1nGetApproximateBytesUsed extern "C" JNIEXPORT jint JNICALL Java_org_jetbrains_skia_PictureKt__1nGetApproximateBytesUsed
(JNIEnv* env, jclass jclass, jlong ptr) { (JNIEnv* env, jclass jclass, jlong ptr) {
SkPicture* instance = reinterpret_cast<SkPicture*>(static_cast<uintptr_t>(ptr)); SkPicture* instance = reinterpret_cast<SkPicture*>(static_cast<uintptr_t>(ptr));
return instance->approximateBytesUsed(); return static_cast<jint>(instance->approximateBytesUsed());
} }
extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skia_PictureKt__1nMakeShader extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skia_PictureKt__1nMakeShader
......
#include <iostream> #include <iostream>
#include <jni.h> #include <jni.h>
#include "SkRegion.h" #include "SkRegion.h"
#include "SkPathBuilder.h"
#include "interop.hh" #include "interop.hh"
static void deleteRegion(SkRegion* region) { static void deleteRegion(SkRegion* region) {
...@@ -50,10 +51,15 @@ extern "C" JNIEXPORT jint JNICALL Java_org_jetbrains_skia_RegionKt_Region_1nComp ...@@ -50,10 +51,15 @@ extern "C" JNIEXPORT jint JNICALL Java_org_jetbrains_skia_RegionKt_Region_1nComp
return instance->computeRegionComplexity(); return instance->computeRegionComplexity();
} }
extern "C" JNIEXPORT jboolean JNICALL Java_org_jetbrains_skia_RegionKt_Region_1nGetBoundaryPath(JNIEnv* env, jclass jclass, jlong ptr, jlong pathPtr) { extern "C" JNIEXPORT jboolean JNICALL Java_org_jetbrains_skia_RegionKt_Region_1nAddBoundaryPath(JNIEnv* env, jclass jclass, jlong ptr, jlong pathBuilderPtr) {
SkRegion* instance = reinterpret_cast<SkRegion*>(static_cast<uintptr_t>(ptr)); SkRegion* instance = reinterpret_cast<SkRegion*>(static_cast<uintptr_t>(ptr));
SkPath* path = reinterpret_cast<SkPath*>(static_cast<uintptr_t>(pathPtr)); SkPathBuilder* pathBuilder = reinterpret_cast<SkPathBuilder*>(static_cast<uintptr_t>(pathBuilderPtr));
return instance->getBoundaryPath(path); return instance->addBoundaryPath(pathBuilder);
}
extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skia_RegionKt_Region_1nGetBoundaryPath(JNIEnv* env, jclass jclass, jlong ptr) {
SkRegion* instance = reinterpret_cast<SkRegion*>(static_cast<uintptr_t>(ptr));
return reinterpret_cast<jlong>(new SkPath(instance->getBoundaryPath()));
} }
extern "C" JNIEXPORT jboolean JNICALL Java_org_jetbrains_skia_RegionKt_Region_1nSetEmpty(JNIEnv* env, jclass jclass, jlong ptr) { extern "C" JNIEXPORT jboolean JNICALL Java_org_jetbrains_skia_RegionKt_Region_1nSetEmpty(JNIEnv* env, jclass jclass, jlong ptr) {
...@@ -130,7 +136,7 @@ extern "C" JNIEXPORT jboolean JNICALL Java_org_jetbrains_skia_RegionKt_Region_1n ...@@ -130,7 +136,7 @@ extern "C" JNIEXPORT jboolean JNICALL Java_org_jetbrains_skia_RegionKt_Region_1n
extern "C" JNIEXPORT jboolean JNICALL Java_org_jetbrains_skia_RegionKt_Region_1nQuickRejectRegion(JNIEnv* env, jclass jclass, jlong ptr, jlong regionPtr) { extern "C" JNIEXPORT jboolean JNICALL Java_org_jetbrains_skia_RegionKt_Region_1nQuickRejectRegion(JNIEnv* env, jclass jclass, jlong ptr, jlong regionPtr) {
SkRegion* instance = reinterpret_cast<SkRegion*>(static_cast<uintptr_t>(ptr)); SkRegion* instance = reinterpret_cast<SkRegion*>(static_cast<uintptr_t>(ptr));
SkRegion* region = reinterpret_cast<SkRegion*>(static_cast<uintptr_t>(regionPtr)); SkRegion* region = reinterpret_cast<SkRegion*>(static_cast<uintptr_t>(regionPtr));
return instance->contains(*region); return instance->quickReject(*region);
} }
extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skia_RegionKt_Region_1nTranslate(JNIEnv* env, jclass jclass, jlong ptr, jint dx, jint dy) { extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skia_RegionKt_Region_1nTranslate(JNIEnv* env, jclass jclass, jlong ptr, jint dx, jint dy) {
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment