Unverified Commit a82cbd79 authored by Pavel's avatar Pavel Committed by GitHub

Nan vals in textstyle (#870)

* Include <cstdint> is required because of uintptr_t

* move `generateVersion` to common code

* on Linux with Chinese language chosen in interface this test fails

* this combination leads to NaN values when height is overridden

* test that empty font still have a meaningful metrics, because FontStyle may rely on it

* simplify test

* check that we don't put NaN in skia

this value might be used as part of key in paragraph cache, which may lead to UB atm
parent e463c57d
package tasks.configuration package tasks.configuration
import Arch
import OS import OS
import SkiaBuildType import SkiaBuildType
import SkikoProperties
import org.gradle.api.DefaultTask
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.kotlin.dsl.get
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinCompileTool
import registerSkikoTask
import supportAndroid import supportAndroid
import supportWasm import supportWasm
import toTitleCase
import java.io.File import java.io.File
fun skiaHeadersDirs(skiaDir: File): List<File> = fun skiaHeadersDirs(skiaDir: File): List<File> =
...@@ -135,3 +143,45 @@ fun Project.configureSignAndPublishDependencies() { ...@@ -135,3 +143,45 @@ fun Project.configureSignAndPublishDependencies() {
} }
} }
} }
fun KotlinTarget.generateVersion(
targetOs: OS,
targetArch: Arch,
skikoProperties: SkikoProperties
) {
val targetName = this.name
val isArm64Simulator = isIosSimArm64()
val generatedDir = project.layout.buildDirectory.dir("generated/$targetName")
val generateVersionTask = project.registerSkikoTask<DefaultTask>(
"generateVersion${toTitleCase(platformType.name)}".withSuffix(isIosSim = isArm64Simulator),
targetOs,
targetArch
) {
inputs.property("buildType", skikoProperties.buildType.id)
outputs.dir(generatedDir)
doFirst {
val outDir = generatedDir.get().asFile
outDir.deleteRecursively()
outDir.mkdirs()
val out = "$outDir/Version.kt"
val target = "${targetOs.id}-${targetArch.id}"
val skiaTag = project.property("dependencies.skia.$target") as String
File(out).writeText(
"""
package org.jetbrains.skiko
object Version {
val skiko = "${skikoProperties.deployVersion}"
val skia = "$skiaTag"
}
""".trimIndent()
)
}
}
val compilation = compilations["main"] ?: error("Could not find 'main' compilation for target '$this'")
compilation.compileKotlinTaskProvider.configure {
dependsOn(generateVersionTask)
(this as KotlinCompileTool).source(generatedDir.get().asFile)
}
}
\ No newline at end of file
...@@ -36,48 +36,6 @@ fun String.withSuffix(isIosSim: Boolean = false) = ...@@ -36,48 +36,6 @@ fun String.withSuffix(isIosSim: Boolean = false) =
fun KotlinTarget.isIosSimArm64() = fun KotlinTarget.isIosSimArm64() =
name.contains("iosSimulatorArm64", ignoreCase = true) name.contains("iosSimulatorArm64", ignoreCase = true)
fun KotlinTarget.generateVersion(
targetOs: OS,
targetArch: Arch,
skikoProperties: SkikoProperties
) {
val targetName = this.name
val isArm64Simulator = isIosSimArm64()
val generatedDir = project.layout.buildDirectory.dir("generated/$targetName")
val generateVersionTask = project.registerSkikoTask<DefaultTask>(
"generateVersion${toTitleCase(platformType.name)}".withSuffix(isIosSim = isArm64Simulator),
targetOs,
targetArch
) {
inputs.property("buildType", skikoProperties.buildType.id)
outputs.dir(generatedDir)
doFirst {
val outDir = generatedDir.get().asFile
outDir.deleteRecursively()
outDir.mkdirs()
val out = "$outDir/Version.kt"
val target = "${targetOs.id}-${targetArch.id}"
val skiaTag = project.property("dependencies.skia.$target") as String
File(out).writeText(
"""
package org.jetbrains.skiko
object Version {
val skiko = "${skikoProperties.deployVersion}"
val skia = "$skiaTag"
}
""".trimIndent()
)
}
}
val compilation = compilations["main"] ?: error("Could not find 'main' compilation for target '$this'")
compilation.compileKotlinTaskProvider.configure {
dependsOn(generateVersionTask)
(this as KotlinCompileTool).source(generatedDir.get().asFile)
}
}
fun SkikoProjectContext.compileNativeBridgesTask( fun SkikoProjectContext.compileNativeBridgesTask(
os: OS, arch: Arch, isArm64Simulator: Boolean os: OS, arch: Arch, isArm64Simulator: Boolean
): TaskProvider<CompileSkikoCppTask> = with (this.project) { ): TaskProvider<CompileSkikoCppTask> = with (this.project) {
......
#pragma once #pragma once
#include <cstdint>
#include <jni.h> #include <jni.h>
template<typename T> template<typename T>
......
...@@ -49,6 +49,10 @@ class ParagraphBuilder(style: ParagraphStyle?, fc: FontCollection?) : ...@@ -49,6 +49,10 @@ class ParagraphBuilder(style: ParagraphStyle?, fc: FontCollection?) :
} }
fun addPlaceholder(style: PlaceholderStyle): ParagraphBuilder { fun addPlaceholder(style: PlaceholderStyle): ParagraphBuilder {
check(!style.width.isNaN())
check(!style.height.isNaN())
check(!style.baseline.isNaN())
try {
Stats.onNativeCall() Stats.onNativeCall()
_nAddPlaceholder( _nAddPlaceholder(
_ptr, _ptr,
...@@ -58,6 +62,9 @@ class ParagraphBuilder(style: ParagraphStyle?, fc: FontCollection?) : ...@@ -58,6 +62,9 @@ class ParagraphBuilder(style: ParagraphStyle?, fc: FontCollection?) :
style.baselineMode.ordinal, style.baselineMode.ordinal,
style.baseline style.baseline
) )
} finally {
reachabilityBarrier(this)
}
return this return this
} }
......
...@@ -47,10 +47,14 @@ class StrutStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finali ...@@ -47,10 +47,14 @@ class StrutStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finali
} }
fun setFontFamilies(families: Array<String>): StrutStyle { fun setFontFamilies(families: Array<String>): StrutStyle {
try {
Stats.onNativeCall() Stats.onNativeCall()
interopScope { interopScope {
StrutStyle_nSetFontFamilies(_ptr, toInterop(families), families.size) StrutStyle_nSetFontFamilies(_ptr, toInterop(families), families.size)
} }
} finally {
reachabilityBarrier(this)
}
return this return this
} }
...@@ -69,8 +73,12 @@ class StrutStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finali ...@@ -69,8 +73,12 @@ class StrutStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finali
} }
fun setFontStyle(style: FontStyle): StrutStyle { fun setFontStyle(style: FontStyle): StrutStyle {
try {
Stats.onNativeCall() Stats.onNativeCall()
_nSetFontStyle(_ptr, style._value) _nSetFontStyle(_ptr, style._value)
} finally {
reachabilityBarrier(this)
}
return this return this
} }
...@@ -86,8 +94,13 @@ class StrutStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finali ...@@ -86,8 +94,13 @@ class StrutStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finali
} }
fun setFontSize(value: Float): StrutStyle { fun setFontSize(value: Float): StrutStyle {
check(!value.isNaN())
try {
Stats.onNativeCall() Stats.onNativeCall()
_nSetFontSize(_ptr, value) _nSetFontSize(_ptr, value)
} finally {
reachabilityBarrier(this)
}
return this return this
} }
...@@ -103,8 +116,13 @@ class StrutStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finali ...@@ -103,8 +116,13 @@ class StrutStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finali
} }
fun setHeight(value: Float): StrutStyle { fun setHeight(value: Float): StrutStyle {
check(!value.isNaN())
try {
Stats.onNativeCall() Stats.onNativeCall()
StrutStyle_nSetHeight(_ptr, value) StrutStyle_nSetHeight(_ptr, value)
} finally {
reachabilityBarrier(this)
}
return this return this
} }
...@@ -120,8 +138,13 @@ class StrutStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finali ...@@ -120,8 +138,13 @@ class StrutStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finali
} }
fun setLeading(value: Float): StrutStyle { fun setLeading(value: Float): StrutStyle {
check(!value.isNaN())
try {
Stats.onNativeCall() Stats.onNativeCall()
_nSetLeading(_ptr, value) _nSetLeading(_ptr, value)
} finally {
reachabilityBarrier(this)
}
return this return this
} }
...@@ -137,8 +160,12 @@ class StrutStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finali ...@@ -137,8 +160,12 @@ class StrutStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finali
} }
fun setEnabled(value: Boolean): StrutStyle { fun setEnabled(value: Boolean): StrutStyle {
try {
Stats.onNativeCall() Stats.onNativeCall()
StrutStyle_nSetEnabled(_ptr, value) StrutStyle_nSetEnabled(_ptr, value)
} finally {
reachabilityBarrier(this)
}
return this return this
} }
...@@ -154,8 +181,12 @@ class StrutStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finali ...@@ -154,8 +181,12 @@ class StrutStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finali
} }
fun setHeightForced(value: Boolean): StrutStyle { fun setHeightForced(value: Boolean): StrutStyle {
try {
Stats.onNativeCall() Stats.onNativeCall()
_nSetHeightForced(_ptr, value) _nSetHeightForced(_ptr, value)
} finally {
reachabilityBarrier(this)
}
return this return this
} }
...@@ -171,8 +202,12 @@ class StrutStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finali ...@@ -171,8 +202,12 @@ class StrutStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finali
} }
fun setHeightOverridden(value: Boolean): StrutStyle { fun setHeightOverridden(value: Boolean): StrutStyle {
try {
Stats.onNativeCall() Stats.onNativeCall()
_nSetHeightOverridden(_ptr, value) _nSetHeightOverridden(_ptr, value)
} finally {
reachabilityBarrier(this)
}
return this return this
} }
...@@ -188,8 +223,12 @@ class StrutStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finali ...@@ -188,8 +223,12 @@ class StrutStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finali
} }
fun setHalfLeading(value: Boolean): StrutStyle { fun setHalfLeading(value: Boolean): StrutStyle {
try {
Stats.onNativeCall() Stats.onNativeCall()
_nSetHalfLeading(_ptr, value) _nSetHalfLeading(_ptr, value)
} finally {
reachabilityBarrier(this)
}
return this return this
} }
......
...@@ -54,8 +54,12 @@ class TextStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finaliz ...@@ -54,8 +54,12 @@ class TextStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finaliz
} }
fun setColor(color: Int): TextStyle { fun setColor(color: Int): TextStyle {
try {
Stats.onNativeCall() Stats.onNativeCall()
_nSetColor(_ptr, color) _nSetColor(_ptr, color)
} finally {
reachabilityBarrier(this)
}
return this return this
} }
...@@ -125,6 +129,7 @@ class TextStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finaliz ...@@ -125,6 +129,7 @@ class TextStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finaliz
} }
fun setDecorationStyle(d: DecorationStyle): TextStyle { fun setDecorationStyle(d: DecorationStyle): TextStyle {
try {
Stats.onNativeCall() Stats.onNativeCall()
_nSetDecorationStyle( _nSetDecorationStyle(
_ptr, _ptr,
...@@ -136,6 +141,9 @@ class TextStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finaliz ...@@ -136,6 +141,9 @@ class TextStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finaliz
d._lineStyle.ordinal, d._lineStyle.ordinal,
d.thicknessMultiplier d.thicknessMultiplier
) )
} finally {
reachabilityBarrier(this)
}
return this return this
} }
...@@ -151,8 +159,12 @@ class TextStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finaliz ...@@ -151,8 +159,12 @@ class TextStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finaliz
} }
fun setFontStyle(s: FontStyle): TextStyle { fun setFontStyle(s: FontStyle): TextStyle {
try {
Stats.onNativeCall() Stats.onNativeCall()
TextStyle_nSetFontStyle(_ptr, s._value) TextStyle_nSetFontStyle(_ptr, s._value)
} finally {
reachabilityBarrier(this)
}
return this return this
} }
...@@ -167,8 +179,12 @@ class TextStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finaliz ...@@ -167,8 +179,12 @@ class TextStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finaliz
} }
fun addShadow(s: Shadow): TextStyle { fun addShadow(s: Shadow): TextStyle {
try {
Stats.onNativeCall() Stats.onNativeCall()
_nAddShadow(_ptr, s.color, s.offsetX, s.offsetY, s.blurSigma) _nAddShadow(_ptr, s.color, s.offsetX, s.offsetY, s.blurSigma)
} finally {
reachabilityBarrier(this)
}
return this return this
} }
...@@ -178,17 +194,21 @@ class TextStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finaliz ...@@ -178,17 +194,21 @@ class TextStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finaliz
} }
fun clearShadows(): TextStyle { fun clearShadows(): TextStyle {
try {
Stats.onNativeCall() Stats.onNativeCall()
_nClearShadows(_ptr) _nClearShadows(_ptr)
} finally {
reachabilityBarrier(this)
}
return this return this
} }
val fontFeatures: Array<FontFeature> val fontFeatures: Array<FontFeature>
get() = try { get() = try {
Stats.onNativeCall() Stats.onNativeCall()
val size = _nGetFontFeaturesSize(_ptr) val size = _nGetFontFeaturesSize(_ptr)
withResult(IntArray(size * 2)) { withResult(IntArray(size * 2)) {
Stats.onNativeCall()
_nGetFontFeatures(_ptr, it) _nGetFontFeatures(_ptr, it)
}.let { }.let {
FontFeature.fromInteropEncodedBy2Ints(it) FontFeature.fromInteropEncodedBy2Ints(it)
...@@ -198,10 +218,14 @@ class TextStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finaliz ...@@ -198,10 +218,14 @@ class TextStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finaliz
} }
fun addFontFeature(f: FontFeature): TextStyle { fun addFontFeature(f: FontFeature): TextStyle {
try {
Stats.onNativeCall() Stats.onNativeCall()
interopScope { interopScope {
_nAddFontFeature(_ptr, toInterop(f.tag), f.value) _nAddFontFeature(_ptr, toInterop(f.tag), f.value)
} }
} finally {
reachabilityBarrier(this)
}
return this return this
} }
...@@ -211,8 +235,12 @@ class TextStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finaliz ...@@ -211,8 +235,12 @@ class TextStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finaliz
} }
fun clearFontFeatures(): TextStyle { fun clearFontFeatures(): TextStyle {
try {
Stats.onNativeCall() Stats.onNativeCall()
_nClearFontFeatures(_ptr) _nClearFontFeatures(_ptr)
} finally {
reachabilityBarrier(this)
}
return this return this
} }
...@@ -228,8 +256,13 @@ class TextStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finaliz ...@@ -228,8 +256,13 @@ class TextStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finaliz
} }
fun setFontSize(size: Float): TextStyle { fun setFontSize(size: Float): TextStyle {
check(!size.isNaN())
try {
Stats.onNativeCall() Stats.onNativeCall()
TextStyle_nSetFontSize(_ptr, size) TextStyle_nSetFontSize(_ptr, size)
} finally {
reachabilityBarrier(this)
}
return this return this
} }
...@@ -271,8 +304,22 @@ class TextStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finaliz ...@@ -271,8 +304,22 @@ class TextStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finaliz
} }
fun setHeight(height: Float?): TextStyle { fun setHeight(height: Float?): TextStyle {
if (height == null) {
try {
Stats.onNativeCall() Stats.onNativeCall()
if (height == null) TextStyle_nSetHeight(_ptr, false, 0f) else TextStyle_nSetHeight(_ptr, true, height) TextStyle_nSetHeight(_ptr, false, 0f)
} finally {
reachabilityBarrier(this)
}
} else {
check(!height.isNaN())
try {
Stats.onNativeCall()
TextStyle_nSetHeight(_ptr, true, height)
} finally {
reachabilityBarrier(this)
}
}
return this return this
} }
...@@ -288,8 +335,12 @@ class TextStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finaliz ...@@ -288,8 +335,12 @@ class TextStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finaliz
} }
fun setHalfLeading(value: Boolean): TextStyle { fun setHalfLeading(value: Boolean): TextStyle {
try {
Stats.onNativeCall() Stats.onNativeCall()
TextStyle_nSetHalfLeading(_ptr, value) TextStyle_nSetHalfLeading(_ptr, value)
} finally {
reachabilityBarrier(this)
}
return this return this
} }
...@@ -305,8 +356,13 @@ class TextStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finaliz ...@@ -305,8 +356,13 @@ class TextStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finaliz
} }
fun setLetterSpacing(letterSpacing: Float): TextStyle { fun setLetterSpacing(letterSpacing: Float): TextStyle {
check(!letterSpacing.isNaN())
try {
Stats.onNativeCall() Stats.onNativeCall()
_nSetLetterSpacing(_ptr, letterSpacing) _nSetLetterSpacing(_ptr, letterSpacing)
} finally {
reachabilityBarrier(this)
}
return this return this
} }
...@@ -322,8 +378,13 @@ class TextStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finaliz ...@@ -322,8 +378,13 @@ class TextStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finaliz
} }
fun setBaselineShift(baselineShift: Float): TextStyle { fun setBaselineShift(baselineShift: Float): TextStyle {
check(!baselineShift.isNaN())
try {
Stats.onNativeCall() Stats.onNativeCall()
TextStyle_nSetBaselineShift(_ptr, baselineShift) TextStyle_nSetBaselineShift(_ptr, baselineShift)
} finally {
reachabilityBarrier(this)
}
return this return this
} }
...@@ -339,8 +400,13 @@ class TextStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finaliz ...@@ -339,8 +400,13 @@ class TextStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finaliz
} }
fun setWordSpacing(wordSpacing: Float): TextStyle { fun setWordSpacing(wordSpacing: Float): TextStyle {
check(!wordSpacing.isNaN())
try {
Stats.onNativeCall() Stats.onNativeCall()
_nSetWordSpacing(_ptr, wordSpacing) _nSetWordSpacing(_ptr, wordSpacing)
} finally {
reachabilityBarrier(this)
}
return this return this
} }
...@@ -384,17 +450,21 @@ class TextStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finaliz ...@@ -384,17 +450,21 @@ class TextStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finaliz
} }
fun setLocale(locale: String?): TextStyle { fun setLocale(locale: String?): TextStyle {
try {
Stats.onNativeCall() Stats.onNativeCall()
interopScope { interopScope {
_nSetLocale(_ptr, toInterop(locale)) _nSetLocale(_ptr, toInterop(locale))
} }
} finally {
reachabilityBarrier(this)
}
return this return this
} }
var baselineMode: BaselineMode var baselineMode: BaselineMode
get() = try { get() = try {
Stats.onNativeCall() Stats.onNativeCall()
BaselineMode.values().get(_nGetBaselineMode(_ptr)) BaselineMode.entries[_nGetBaselineMode(_ptr)]
} finally { } finally {
reachabilityBarrier(this) reachabilityBarrier(this)
} }
...@@ -403,8 +473,12 @@ class TextStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finaliz ...@@ -403,8 +473,12 @@ class TextStyle internal constructor(ptr: NativePointer) : Managed(ptr, _Finaliz
} }
fun setBaselineMode(baseline: BaselineMode): TextStyle { fun setBaselineMode(baseline: BaselineMode): TextStyle {
try {
Stats.onNativeCall() Stats.onNativeCall()
_nSetBaselineMode(_ptr, baseline.ordinal) _nSetBaselineMode(_ptr, baseline.ordinal)
} finally {
reachabilityBarrier(this)
}
return this return this
} }
......
...@@ -12,6 +12,7 @@ import org.jetbrains.skiko.tests.runTest ...@@ -12,6 +12,7 @@ import org.jetbrains.skiko.tests.runTest
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
private fun isMac() = (hostOs == OS.MacOS) private fun isMac() = (hostOs == OS.MacOS)
private fun isIos() = (hostOs == OS.Ios) private fun isIos() = (hostOs == OS.Ios)
...@@ -119,4 +120,17 @@ class FontTests { ...@@ -119,4 +120,17 @@ class FontTests {
} }
} }
@Test
fun emptyFontMetrics() {
Font(null).use { font ->
val metrics = font.metrics
assertFalse(
metrics.top == 0f &&
metrics.bottom == 0f &&
metrics.ascent == 0f &&
metrics.descent == 0f
)
}
}
} }
...@@ -132,6 +132,7 @@ class TextStyleTest { ...@@ -132,6 +132,7 @@ class TextStyleTest {
} }
} }
@Test
fun textStyleBaselineTest() { fun textStyleBaselineTest() {
TextStyle().use { textStyle -> TextStyle().use { textStyle ->
assertEquals(0.0f, textStyle.baselineShift) assertEquals(0.0f, textStyle.baselineShift)
...@@ -140,4 +141,20 @@ class TextStyleTest { ...@@ -140,4 +141,20 @@ class TextStyleTest {
} }
} }
@Test
fun textStyleMetricsContainsMeaningfulValues() {
TextStyle().use { textStyle ->
val metrics = textStyle.fontMetrics
assertFalse(metrics.ascent == 0f && metrics.descent == 0f && metrics.leading == 0f)
}
}
@Test
fun textStyleNotContainNaNValues() {
TextStyle().use { textStyle ->
textStyle.height = 32f
assertTrue(!textStyle.fontMetrics.ascent.isNaN())
}
}
} }
#include <iostream> #include <iostream>
#include <cstdint>
#include <jni.h> #include <jni.h>
typedef void (*FreeFunction)(void*); typedef void (*FreeFunction)(void*);
......
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