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:
docker run --rm \
-v "${{ github.workspace }}":/workspace \
-v "${HOME}/.gradle":/gradle-cache \
-v "${HOME}/.m2":/root/.m2 \
-e GRADLE_USER_HOME=/gradle-cache \
-w /workspace/${{ inputs.working_directory }} \
"ghcr.io/${{ steps.vars.outputs.image_namespace }}/${{ inputs.image_name }}:${{ steps.vars.outputs.tag }}" \
......
......@@ -376,8 +376,8 @@ jobs:
git clone https://github.com/emscripten-core/emsdk.git
cd ./emsdk
fi
./emsdk install 3.1.50
./emsdk activate 3.1.50
./emsdk install 4.0.7
./emsdk activate 4.0.7
- shell: bash
name: 'Run WASM Tests'
......
......@@ -89,11 +89,14 @@ open class ClocksAwt(
val y = ypos.toFloat()
canvas.drawString(text, x, y, font, paint)
val style = ParagraphStyle().apply {
fontRastrSettings = FontRastrSettings(FontEdging.SUBPIXEL_ANTI_ALIAS, FontHinting.SLIGHT, true)
}
val style = ParagraphStyle()
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")
.popStyle()
.build()
......@@ -123,4 +126,4 @@ open class ClocksAwt(
xpos = e.x
ypos = e.y
}
}
\ No newline at end of file
}
......@@ -10,7 +10,7 @@ enum class OS(
Linux("linux", arrayOf()),
Android("android", arrayOf()),
Windows("windows", arrayOf()),
MacOS("macos", arrayOf("-mmacosx-version-min=10.15")),
MacOS("macos", arrayOf("-mmacosx-version-min=11.0")),
Wasm("wasm", arrayOf()),
IOS("ios", arrayOf()),
TVOS("tvos", arrayOf())
......
......@@ -65,8 +65,8 @@ fun skiaPreprocessorFlags(os: OS, buildType: SkiaBuildType): Array<String> {
"-DSK_SHAPER_UNICODE_AVAILABLE",
"-DSK_SUPPORT_OPENCL=0",
"-DSK_UNICODE_AVAILABLE",
"-DU_DISABLE_RENAMING",
"-DSK_USING_THIRD_PARTY_ICU",
"-DSK_HIDE_PATH_EDIT_METHODS", // Temporary (m144) skia flag for migration to SkPathBuilder
// For ICU symbols renaming:
"-DU_DISABLE_RENAMING=0",
"-DU_DISABLE_VERSION_SUFFIX=1",
......@@ -355,4 +355,4 @@ fun KotlinTarget.generateVersion(
(this as KotlinCompileTool).source(generatedDir.get().asFile)
}
}
}
\ No newline at end of file
}
......@@ -10,7 +10,7 @@ RUN apt update -y && \
# Install emscripten
ENV EMSDK_DIR=/usr/emsdk
ENV EMSDK_VER=3.1.50
ENV EMSDK_VER=4.0.7
ENV PATH=$EMSDK_DIR/upstream/emscripten:$PATH
RUN git clone https://github.com/emscripten-core/emsdk.git $EMSDK_DIR && \
$EMSDK_DIR/emsdk install $EMSDK_VER && \
......
......@@ -7,7 +7,7 @@ kotlin.mpp.enableCInteropCommonization=true
deploy.version=0.0.0
# 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
dependencies.angle=ec4d8f8e4d
......
......@@ -11,10 +11,12 @@
#if (defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_LINUX)) && !defined(SKIKO_WASM)
#include "ports/SkFontMgr_fontconfig.h"
#include "ports/SkFontScanner_FreeType.h"
#endif
#ifdef SK_BUILD_FOR_ANDROID
#include "ports/SkFontMgr_android.h"
#include "ports/SkFontScanner_FreeType.h"
#endif
#ifdef SKIKO_WASM
......@@ -36,11 +38,11 @@ sk_sp<SkFontMgr> SkFontMgrSkikoDefault() {
#endif
#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
#ifdef SK_BUILD_FOR_ANDROID
return SkFontMgr_New_Android(nullptr);
return SkFontMgr_New_Android(nullptr, SkFontScanner_Make_FreeType());
#endif
#ifdef SKIKO_WASM
......
......@@ -49,7 +49,7 @@ namespace skikoMpp {
if (run.fGlyphCount > 1 && SkScalarNearlyEqual(posBuffer[(run.fGlyphCount - 2) * 2], lastLeft))
lastWidth = 0;
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);
bounds->join(runBounds);
......
#include <SkNWayCanvas.h>
#include <SkPathBuilder.h>
#include <SkPicture.h>
#include <SkShadowUtils.h>
#include "node/RenderNode.h"
......@@ -306,9 +307,9 @@ void RenderNode::updateMatrix() {
void RenderNode::drawShadow(SkCanvas *canvas, const LightGeometry& lightGeometry, const LightInfo& lightInfo) {
SkPath tmpPath, *path = &tmpPath;
if (this->clipRect) {
tmpPath.addRect(*this->clipRect);
tmpPath = SkPathBuilder().addRect(*this->clipRect).detach();
} else if (this->clipRRect) {
tmpPath.addRRect(*this->clipRRect);
tmpPath = SkPathBuilder().addRRect(*this->clipRRect).detach();
} else if (this->clipPath) {
path = &*this->clipPath;
} 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)
*
* @see [https://fiddle.skia.org/c/@Picture_approximateBytesUsed](https://fiddle.skia.org/c/@Picture_approximateBytesUsed)
*/
val approximateBytesUsed: NativePointer
val approximateBytesUsed: Int
get() = try {
Stats.onNativeCall()
_nGetApproximateBytesUsed(_ptr)
......@@ -273,7 +273,7 @@ private external fun _nMakePlaceholder(left: Float, top: Float, right: Float, bo
private external fun _nGetApproximateOpCount(ptr: NativePointer): Int
@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")
private external fun _nMakeShader(
......
......@@ -2,6 +2,7 @@ package org.jetbrains.skia
import org.jetbrains.skia.impl.InteropPointer
import org.jetbrains.skia.impl.InteropScope
import org.jetbrains.skia.impl.withNullableResult
import org.jetbrains.skia.impl.withResult
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)
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
import org.jetbrains.skia.impl.*
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) {
companion object {
init {
......@@ -10,24 +14,47 @@ class Region : Managed(Region_nMake(), _FinalizerHolder.PTR) {
}
}
/** Logical operations used to combine regions. */
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 {
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 {
Stats.onNativeCall()
Region_nSet(_ptr, getPtr(r))
Region_nSet(_ptr, getPtr(src))
} finally {
reachabilityBarrier(this)
reachabilityBarrier(r)
reachabilityBarrier(src)
}
}
/** Returns true if this region is empty. */
val isEmpty: Boolean
get() = try {
Stats.onNativeCall()
......@@ -35,6 +62,8 @@ class Region : Managed(Region_nMake(), _FinalizerHolder.PTR) {
} finally {
reachabilityBarrier(this)
}
/** Returns true if this region is one non-empty rectangle. */
val isRect: Boolean
get() = try {
Stats.onNativeCall()
......@@ -42,6 +71,8 @@ class Region : Managed(Region_nMake(), _FinalizerHolder.PTR) {
} finally {
reachabilityBarrier(this)
}
/** Returns true if this region is represented by more than one rectangle. */
val isComplex: Boolean
get() = try {
Stats.onNativeCall()
......@@ -49,6 +80,8 @@ class Region : Managed(Region_nMake(), _FinalizerHolder.PTR) {
} finally {
reachabilityBarrier(this)
}
/** Returns the union bounds of all rectangles in this region. */
val bounds: IRect
get() = try {
Stats.onNativeCall()
......@@ -60,6 +93,10 @@ class Region : Managed(Region_nMake(), _FinalizerHolder.PTR) {
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 {
return try {
Stats.onNativeCall()
......@@ -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 {
Stats.onNativeCall()
Region_nAddBoundaryPath(_ptr, getPtr(pathBuilder))
} finally {
reachabilityBarrier(this)
reachabilityBarrier(pathBuilder)
}
}
/** Returns the boundary of this region as a new immutable [Path]. */
fun getBoundaryPath(): Path {
return try {
Stats.onNativeCall()
Region_nGetBoundaryPath(
_ptr,
getPtr(p)
)
Path(Region_nGetBoundaryPath(_ptr))
} finally {
reachabilityBarrier(this)
reachabilityBarrier(p)
}
}
/**
* Sets this region to empty.
*
* @return always false
*/
fun setEmpty(): Boolean {
return try {
Stats.onNativeCall()
......@@ -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 {
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 {
return try {
Stats.onNativeCall()
......@@ -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 {
return try {
val arr = IntArray(rects.size * 4)
......@@ -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 {
Stats.onNativeCall()
Region_nSetRegion(_ptr, getPtr(r))
Region_nSetRegion(_ptr, getPtr(region))
} finally {
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 {
Stats.onNativeCall()
Region_nSetPath(
_ptr,
getPtr(path),
getPtr(clip)
)
Region_nSetPath(_ptr, getPtr(path), getPtr(clip))
} finally {
reachabilityBarrier(this)
reachabilityBarrier(path)
......@@ -147,10 +214,12 @@ class Region : Managed(Region_nMake(), _FinalizerHolder.PTR) {
}
}
/** Returns true if this region intersects [rect]. */
fun intersects(rect: IRect): Boolean {
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 {
return try {
Stats.onNativeCall()
......@@ -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 {
Stats.onNativeCall()
Region_nIntersectsRegion(
_ptr,
getPtr(r)
)
Region_nIntersectsRegion(_ptr, getPtr(region))
} finally {
reachabilityBarrier(this)
reachabilityBarrier(r)
reachabilityBarrier(region)
}
}
/** Returns true if point ([x], [y]) is inside this region. */
fun contains(x: Int, y: Int): Boolean {
return try {
Stats.onNativeCall()
......@@ -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 {
return try {
Stats.onNativeCall()
......@@ -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 {
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 {
Stats.onNativeCall()
Region_nContainsRegion(_ptr, getPtr(r))
Region_nContainsRegion(_ptr, getPtr(region))
} finally {
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 {
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 {
return try {
Stats.onNativeCall()
......@@ -217,9 +294,12 @@ class Region : Managed(Region_nMake(), _FinalizerHolder.PTR) {
}
}
/** Fast conservative rejection check for [rect]. */
fun quickReject(rect: IRect): Boolean {
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 {
return try {
Stats.onNativeCall()
......@@ -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 {
Stats.onNativeCall()
Region_nQuickRejectRegion(
_ptr,
getPtr(r)
)
Region_nQuickRejectRegion(_ptr, getPtr(region))
} finally {
reachabilityBarrier(this)
reachabilityBarrier(r)
reachabilityBarrier(region)
}
}
/** Offsets this region by ([dx], [dy]). */
fun translate(dx: Int, dy: Int) {
try {
Stats.onNativeCall()
......@@ -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 {
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 {
return try {
Stats.onNativeCall()
Region_nOpIRect(
_ptr,
left,
top,
right,
bottom,
op.ordinal
)
Region_nOpIRect(_ptr, left, top, right, bottom, op.ordinal)
} finally {
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 {
Stats.onNativeCall()
Region_nOpRegion(
_ptr,
getPtr(r),
op.ordinal
)
Region_nOpRegion(_ptr, getPtr(region), op.ordinal)
} finally {
reachabilityBarrier(this)
reachabilityBarrier(r)
reachabilityBarrier(region)
}
}
fun op(rect: IRect, r: Region?, op: Op): Boolean {
return op(
rect.left,
rect.top,
rect.right,
rect.bottom,
r,
op
)
/** Replaces this region with result of `rect op region`. */
fun op(rect: IRect, region: Region, op: Op): Boolean {
return op(rect.left, rect.top, rect.right, rect.bottom, region, 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 {
Stats.onNativeCall()
Region_nOpIRectRegion(
_ptr,
left,
top,
right,
bottom,
getPtr(r),
op.ordinal
)
Region_nOpIRectRegion(_ptr, left, top, right, bottom, getPtr(region), op.ordinal)
} finally {
reachabilityBarrier(this)
reachabilityBarrier(r)
reachabilityBarrier(region)
}
}
fun op(r: Region?, rect: IRect, op: Op): Boolean {
return op(
r,
rect.left,
rect.top,
rect.right,
rect.bottom,
op
)
/** Replaces this region with result of `region op rect`. */
fun op(region: Region, rect: IRect, op: Op): Boolean {
return op(region, 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 {
Stats.onNativeCall()
Region_nOpRegionIRect(
_ptr,
getPtr(r),
left,
top,
right,
bottom,
op.ordinal
)
Region_nOpRegionIRect(_ptr, getPtr(region), left, top, right, bottom, op.ordinal)
} finally {
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 {
Stats.onNativeCall()
Region_nOpRegionRegion(
_ptr,
getPtr(a),
getPtr(b),
op.ordinal
)
Region_nOpRegionRegion(_ptr, getPtr(regionA), getPtr(regionB), op.ordinal)
} finally {
reachabilityBarrier(this)
reachabilityBarrier(a)
reachabilityBarrier(b)
reachabilityBarrier(regionA)
reachabilityBarrier(regionB)
}
}
......@@ -368,7 +409,6 @@ class Region : Managed(Region_nMake(), _FinalizerHolder.PTR) {
}
}
@ExternalSymbolName("org_jetbrains_skia_Region__1nMake")
private external fun Region_nMake(): NativePointer
......@@ -385,7 +425,7 @@ private external fun Region_nIsRect(ptr: NativePointer): Boolean
private external fun Region_nGetBounds(ptr: NativePointer, ltrb: InteropPointer)
@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")
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")
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")
private external fun Region_nGetBoundaryPath(ptr: NativePointer, pathPtr: NativePointer): Boolean
private external fun Region_nGetBoundaryPath(ptr: NativePointer): NativePointer
@ExternalSymbolName("org_jetbrains_skia_Region__1nSetEmpty")
private external fun Region_nSetEmpty(ptr: NativePointer): Boolean
......@@ -455,7 +498,6 @@ private external fun Region_nOpIRectRegion(
op: Int
): Boolean
@ExternalSymbolName("org_jetbrains_skia_Region__1nOpRegionIRect")
private external fun Region_nOpRegionIRect(
ptr: NativePointer,
......@@ -467,11 +509,10 @@ private external fun Region_nOpRegionIRect(
op: Int
): Boolean
@ExternalSymbolName("org_jetbrains_skia_Region__1nOpRegionRegion")
private external fun Region_nOpRegionRegion(
ptr: NativePointer,
regionPtrA: NativePointer,
regionPtrB: NativePointer,
op: Int
): Boolean
\ No newline at end of file
): Boolean
......@@ -3,6 +3,10 @@ package org.jetbrains.skia.paragraph
import org.jetbrains.skia.FontEdging
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,
val hinting: FontHinting,
val subpixel: Boolean)
\ No newline at end of file
val subpixel: Boolean)
......@@ -190,25 +190,19 @@ class ParagraphStyle : Managed(ParagraphStyle_nMake(), _FinalizerHolder.PTR) {
return this
}
@Suppress("DEPRECATION_ERROR")
@Deprecated(
message = "Replaced by separate properties in TextStyle: fontEdging, fontHinting, subpixel",
level = DeprecationLevel.ERROR,
)
var fontRastrSettings: FontRastrSettings
get() = try {
Stats.onNativeCall()
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)
get() = textStyle.let {
FontRastrSettings(it.fontEdging, it.fontHinting, it.subpixel)
}
set(value) = try {
Stats.onNativeCall()
_nSetFontRastrSettings(_ptr, value.edging.ordinal, value.hinting.ordinal, value.subpixel)
} finally {
reachabilityBarrier(this)
set(value) = textStyle.let {
it.fontEdging = value.edging
it.fontHinting = value.hinting
it.subpixel = value.subpixel
}
var isApplyRoundingHackEnabled: Boolean
......@@ -317,18 +311,6 @@ private external fun _nIsHintingEnabled(ptr: NativePointer): Boolean
@ExternalSymbolName("org_jetbrains_skia_paragraph_ParagraphStyle__1nDisableHinting")
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")
private external fun _nGetApplyRoundingHack(ptr: NativePointer): Boolean
......@@ -345,4 +327,4 @@ private external fun _nGetTextIndent(ptr: NativePointer, result: InteropPointer)
private external fun _nGetReplaceTabCharacters(ptr: NativePointer): Boolean
@ExternalSymbolName("org_jetbrains_skia_paragraph_ParagraphStyle__1nSetReplaceTabCharacters")
private external fun _nSetReplaceTabCharacters(ptr: NativePointer, value: Boolean)
\ No newline at end of file
private external fun _nSetReplaceTabCharacters(ptr: NativePointer, value: Boolean)
......@@ -509,7 +509,6 @@ class TextStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finaliz
return this
}
val fontMetrics: FontMetrics
get() = try {
Stats.onNativeCall()
......@@ -529,11 +528,57 @@ class TextStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finaliz
}
fun setPlaceholder(): TextStyle {
Stats.onNativeCall()
_nSetPlaceholder(_ptr)
try {
Stats.onNativeCall()
_nSetPlaceholder(_ptr)
} finally {
reachabilityBarrier(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 {
val PTR = TextStyle_nGetFinalizer()
}
......@@ -688,3 +733,21 @@ private external fun _nIsPlaceholder(ptr: NativePointer): Boolean
@ExternalSymbolName("org_jetbrains_skia_paragraph_TextStyle__1nSetPlaceholder")
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 {
return Surface.makeRasterN32Premul(100, 100).use { surface ->
val paint = Paint()
paint.color = -0x10000
Path().moveTo(20f, 80f)
PathBuilder()
.moveTo(20f, 80f)
.lineTo(50f, 20f)
.lineTo(80f, 80f)
.closePath()
.detach()
.use { path ->
val canvas = surface.canvas
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 {
@Test
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 ->
assertEquals(RSXform(1.0f, 0f, 0.5f, 0f), measure.getRSXform(0.5f))
}
......@@ -20,7 +20,7 @@ class PathMeasureTest {
@Test
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 ->
assertEquals(Point(1f, 0f), measure.getTangent(2f))
}
......@@ -29,7 +29,7 @@ class PathMeasureTest {
@Test
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 ->
assertCloseEnough(Point(0.89442724f, 9.552787f), measure.getPosition(1f)!!)
}
......@@ -40,9 +40,9 @@ class PathMeasureTest {
@Test
@Ignore
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 ->
Path().lineTo(10f, 10f).use { path2 ->
PathBuilder().lineTo(10f, 10f).detach().use { path2 ->
assertEquals(40f, measure.length)
assertCloseEnough(Point(0f, 0f), measure.getPosition(0f)!!)
assertCloseEnough(Point(1f, 0f), measure.getTangent(0f)!!)
......
package org.jetbrains.skia
import org.jetbrains.skia.impl.Native.Companion.NullPointer
import org.jetbrains.skia.tests.assertCloseEnough
import kotlin.test.Test
import kotlin.test.assertContentEquals
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertNotEquals
import kotlin.test.assertNull
import kotlin.test.assertTrue
class PathTest {
@Test
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(
Path().addPath(subpath),
Path().incReserve(10).addPath(subpath).closePath()
PathBuilder().addPath(subpath).detach(),
PathBuilder().incReserve(10).addPath(subpath).closePath().detach()
)) {
val p0 = Point(0f, 0f)
val p1 = Point(40f, 40f)
......@@ -29,21 +37,22 @@ class PathTest {
assertEquals(p3, p.getPoint(3))
assertEquals(p4, p.getPoint(4))
assertEquals(p4, p.lastPt)
p.lastPt = p5
assertEquals(p5, p.getPoint(4))
assertEquals(p5, p.lastPt)
assertEquals(5, p.getPoints(null, 0))
// lastPt is now read-only, test setLastPt through PathBuilder
val pModified = PathBuilder(p).setLastPt(p5.x, p5.y).detach()
assertEquals(p5, pModified.getPoint(4))
assertEquals(p5, pModified.lastPt)
assertEquals(5, pModified.getPoints(null, 0))
var pts = arrayOfNulls<Point?>(5)
p.getPoints(pts, 5)
pModified.getPoints(pts, 5)
assertContentEquals(arrayOf(p0, p1, p2, p3, p5), pts)
pts = arrayOfNulls(3)
p.getPoints(pts, 3)
pModified.getPoints(pts, 3)
assertContentEquals(arrayOf(p0, p1, p2), pts)
pts = arrayOfNulls(5)
p.getPoints(pts, 3)
pModified.getPoints(pts, 3)
assertContentEquals(arrayOf(p0, p1, p2, null, null), pts)
pts = arrayOfNulls(10)
p.getPoints(pts, 10)
pModified.getPoints(pts, 10)
assertContentEquals(arrayOf(p0, p1, p2, p3, p5, null, null, null, null, null), pts)
assertEquals(6, p.verbsCount)
assertEquals(6, p.getVerbs(null, 0))
......@@ -81,16 +90,103 @@ class PathTest {
null
), verbs
)
assertNotEquals(NullPointer, p.approximateBytesUsed)
assertTrue(p.approximateBytesUsed > 0, "approximateBytesUsed should return positive size")
assertEquals(PathSegmentMask.LINE, p.segmentMasks)
}
}
@Test
fun emptyPath() {
val emptyPath = Path()
assertEquals(0, emptyPath.pointsCount)
assertNull(emptyPath.lastPt)
}
@Test
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)
assertCloseEnough(bounds, path.bounds)
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
class PathTests {
@Test
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 ->
assertTrue(i.hasNext())
......@@ -60,8 +60,8 @@ class PathTests {
@Test
fun convexityTest() = runTest {
Path().lineTo(40f, 20f).lineTo(0f, 40f).lineTo(0f, 0f).closePath().use { p -> assertTrue(p.isConvex) }
Path().lineTo(40f, 40f).lineTo(40f, 0f).lineTo(0f, 40f).lineTo(0f, 0f).closePath().use { p ->
PathBuilder().lineTo(40f, 20f).lineTo(0f, 40f).lineTo(0f, 0f).closePath().detach().use { p -> assertTrue(p.isConvex) }
PathBuilder().lineTo(40f, 40f).lineTo(40f, 0f).lineTo(0f, 40f).lineTo(0f, 0f).closePath().detach().use { p ->
assertFalse(p.isConvex)
}
}
......@@ -71,7 +71,7 @@ class PathTests {
fun isShapeTest() {
for (dir in PathDirection.entries) {
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)
assertNull(p.isOval)
assertNull(p.isRRect)
......@@ -80,7 +80,7 @@ class PathTests {
}
for (dir in PathDirection.entries) {
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)
assertEquals(Rect.makeLTRB(0f, 0f, 40f, 20f), p.isOval)
assertNull(p.isRRect)
......@@ -88,7 +88,7 @@ class PathTests {
}
}
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)
assertEquals(Rect.makeLTRB(0f, 0f, 40f, 40f), p.isOval)
assertNull(p.isRRect)
......@@ -96,24 +96,24 @@ class PathTests {
}
for (dir in PathDirection.entries) {
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.isOval)
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)
assertNull(p.isOval)
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)
assertEquals(Rect.makeLTRB(0f, 0f, 40f, 20f), p.isOval)
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.isOval)
assertNull(p.isRRect)
......@@ -122,43 +122,42 @@ class PathTests {
@Test
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)
p.reset()
assertTrue(p.isEmpty)
}
Path().lineTo(40f, 40f).lineTo(40f, 0f).lineTo(0f, 40f).lineTo(0f, 0f).closePath().use { p ->
assertFalse(p.isEmpty)
p.rewind()
Path().use { p ->
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)
p.closePath()
}
PathBuilder().lineTo(40f, 40f).lineTo(40f, 0f).lineTo(0f, 40f).lineTo(0f, 0f).closePath().detach().use { p ->
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)
p.closePath()
}
PathBuilder().moveTo(100f, 100f).lineTo(140f, 140f).lineTo(140f, 100f).lineTo(100f, 140f).closePath().detach().use { p ->
assertTrue(p.isLastContourClosed)
}
Path().lineTo(40f, 40f).lineTo(40f, 0f).lineTo(0f, 40f).lineTo(0f, 0f).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(40f, 0f).lineTo(0f, 40f).lineTo(0f, 0f).detach().use { p -> assertTrue(p.isFinite) }
PathBuilder().lineTo(40f, 40f).lineTo(Float.POSITIVE_INFINITY, 0f).lineTo(0f, 40f).lineTo(0f, 0f).closePath().detach().use { p ->
assertEquals(
false,
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)
p.setVolatile(true)
assertTrue(p.isVolatile)
p.setVolatile(false)
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)
}
Path().moveTo(20f, 20f).lineTo(40f, 40f).use { p ->
PathBuilder().moveTo(20f, 20f).lineTo(40f, 40f).detach().use { p ->
assertContentEquals(
arrayOf(
Point(20f, 20f),
......@@ -170,18 +169,18 @@ class PathTests {
@Test
fun swapTest() {
Path().lineTo(40f, 40f).lineTo(40f, 0f).lineTo(0f, 40f).lineTo(0f, 0f).closePath().use { p1 ->
Path().lineTo(0f, 0f).lineTo(20f, 20f).use { p2 ->
PathBuilder().lineTo(40f, 40f).lineTo(40f, 0f).lineTo(0f, 40f).lineTo(0f, 0f).closePath().detach().use { p1 ->
PathBuilder().lineTo(0f, 0f).lineTo(20f, 20f).detach().use { p2 ->
p1.swap(p2)
assertEquals(Path().lineTo(0f, 0f).lineTo(20f, 20f), p1)
assertEquals(Path().lineTo(40f, 40f).lineTo(40f, 0f).lineTo(0f, 40f).lineTo(0f, 0f).closePath(), p2)
assertEquals(PathBuilder().lineTo(0f, 0f).lineTo(20f, 20f).detach(), p1)
assertEquals(PathBuilder().lineTo(40f, 40f).lineTo(40f, 0f).lineTo(0f, 40f).lineTo(0f, 0f).closePath().detach(), p2)
}
}
}
@Test
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(25f, 20f, 39f, 120f)))
assertTrue(p.conservativelyContainsRect(Rect.makeLTRB(15f, 25f, 49f, 115f)))
......@@ -215,20 +214,20 @@ class PathTests {
),
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
p.lineTo(10f, 40f)
val g2 = p.generationId
val p2 = PathBuilder(p).lineTo(10f, 40f).detach()
val g2 = p2.generationId
assertNotEquals(g1, g2)
p.fillMode = PathFillMode.EVEN_ODD
val g3 = p.generationId
assertEquals(g2, g3)
val p3 = PathBuilder(p2).lineTo(20f, 20f).detach()
val g3 = p3.generationId
assertNotEquals(g2, g3)
}
}
@Test
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())
assertEquals(p, p2)
}
......@@ -238,4 +237,4 @@ class PathTests {
fun svgTest() {
Path.makeFromSVGString("M0 0 L10 10 Z").use { }
}
}
\ No newline at end of file
}
......@@ -13,7 +13,7 @@ class PathUtilsTest {
strokeWidth = 10f
mode = PaintMode.STROKE
}
val path = Path().lineTo(40f, 40f)
val path = PathBuilder().lineTo(40f, 40f).detach()
val fillPath = PathUtils.fillPathWithPaint(path, paint)
......@@ -29,7 +29,7 @@ class PathUtilsTest {
strokeWidth = 10f
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 fillPath001 = PathUtils.fillPathWithPaint(path, paint,null, 0.01f)
......
......@@ -3,6 +3,7 @@ package org.jetbrains.skia
import org.jetbrains.skia.tests.assertCloseEnough
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue
class PictureTest {
@Test
......@@ -31,6 +32,8 @@ class PictureTest {
val canvas = recorder.beginRecording(size)
canvas.drawRect(Rect(10.0f, 10.0f, 20.0f, 20.0f), Paint().apply { color = Color.RED })
val pic = recorder.finishRecordingAsPicture()
assertTrue(pic.approximateBytesUsed > 0)
assertTrue(pic.approximateOpCount > 0)
val surface = Surface.makeRasterN32Premul(32, 32)
pic.playback(surface.canvas)
......
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.paragraph.FontRastrSettings
import org.jetbrains.skia.paragraph.ParagraphStyle
import org.jetbrains.skia.paragraph.TextIndent
import kotlin.test.Test
......@@ -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
fun paragraphStyleRoundingHackTests() {
ParagraphStyle().use { paragraphStyle ->
......
......@@ -2,12 +2,7 @@ package org.jetbrains.skiko.paragraph
import org.jetbrains.skia.*
import org.jetbrains.skia.impl.use
import org.jetbrains.skia.jbMonoPath
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.paragraph.*
import org.jetbrains.skia.tests.assertContentCloseEnough
import org.jetbrains.skia.tests.makeFromResource
import org.jetbrains.skiko.KotlinBackend
......@@ -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
fun textStyleMetricsContainsMeaningfulValues() = runTest {
val jbMono = Typeface.makeFromResource(jbMonoPath)
......@@ -171,5 +193,4 @@ class TextStyleTest {
assertTrue(!textStyle.fontMetrics.ascent.isNaN())
}
}
}
package org.jetbrains.skiko.tests
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.await
import kotlinx.coroutines.promise
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
/**
......@@ -24,4 +17,4 @@ actual fun runTest(block: suspend () -> Unit): TestReturnType = MainScope().prom
actual typealias SkipJsTarget = kotlin.test.Ignore
actual annotation class SkipWasmTarget
\ No newline at end of file
actual annotation class SkipWasmTarget
......@@ -37,7 +37,7 @@ extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skia_CanvasKt__1nDrawPoints
SkCanvas::PointMode skMode = static_cast<SkCanvas::PointMode>(mode);
jsize len = env->GetArrayLength(coords);
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);
}
......
......@@ -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));
std::vector<jshort> glyphs(uniArrLen);
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->SetShortArrayRegion(resultGlyphs, 0, uniArrLen, glyphs.data());
}
......@@ -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));
std::vector<jfloat> widths(count);
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->SetFloatArrayRegion(res, 0, count, widths.data());
}
......@@ -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));
std::vector<SkRect> bounds(count);
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);
for (int i = 0; i < count; ++i) {
......@@ -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));
std::vector<SkPoint> positions(count);
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);
std::vector<jfloat> r(count * 2);
......@@ -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));
std::vector<jfloat> positions(count);
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->SetFloatArrayRegion(res, 0, count, positions.data());
}
......@@ -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
(JNIEnv* env, jclass jclass, jlong ptr, jshort glyph) {
SkFont* instance = reinterpret_cast<SkFont*>(static_cast<uintptr_t>(ptr));
SkPath* path = new SkPath();
instance->getPath(glyph, path);
return reinterpret_cast<jlong>(path);
std::optional<SkPath> pathOpt = instance->getPath(glyph);
if (pathOpt.has_value()) {
SkPath* path = new SkPath(pathOpt.value());
return reinterpret_cast<jlong>(path);
}
return 0;
}
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;
} 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);
if (orig) {
SkPath* path = new SkPath();
orig->transform(mx, path);
SkPath* path = new SkPath(orig->makeTransform(mx));
ctx->paths->push_back(reinterpret_cast<jlong>(path));
}
}, &ctx);
......
This diff is collapsed.
This diff is collapsed.
......@@ -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) {
jsize len = env->GetArrayLength(intervalsArray);
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);
return reinterpret_cast<jlong>(ptr);
}
......
......@@ -71,10 +71,10 @@ extern "C" JNIEXPORT jint JNICALL Java_org_jetbrains_skia_PictureKt__1nGetApprox
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) {
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
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -75,7 +75,7 @@ extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skia_paragraph_FontCollect
extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skia_paragraph_FontCollectionKt__1nDefaultFallbackChar
(JNIEnv* env, jclass jclass, jlong ptr, jint unicode, jint fontStyle, jstring locale) {
FontCollection* instance = reinterpret_cast<FontCollection*>(static_cast<uintptr_t>(ptr));
return reinterpret_cast<jlong>(instance->defaultFallback(unicode, skija::FontStyle::fromJava(fontStyle), skString(env, locale)).release());
return reinterpret_cast<jlong>(instance->defaultFallback(unicode, skija::FontStyle::fromJava(fontStyle), skString(env, locale), std::nullopt).release());
}
extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skia_paragraph_FontCollectionKt__1nDefaultFallback
......
This diff is collapsed.
......@@ -35,7 +35,7 @@ SKIKO_EXPORT void org_jetbrains_skia_Canvas__1nDrawPoints
SkCanvas* canvas = reinterpret_cast<SkCanvas*>((canvasPtr));
SkPaint* paint = reinterpret_cast<SkPaint*>((paintPtr));
SkCanvas::PointMode skMode = static_cast<SkCanvas::PointMode>(mode);
canvas->drawPoints(skMode, coordsCount / 2, reinterpret_cast<SkPoint*>(coords), *paint);
canvas->drawPoints(skMode, {reinterpret_cast<SkPoint*>(coords), coordsCount / 2}, *paint);
}
SKIKO_EXPORT void org_jetbrains_skia_Canvas__1nDrawLine
......
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