Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
S
skiko
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
liuqiang
skiko
Commits
25ff8bee
Unverified
Commit
25ff8bee
authored
Nov 17, 2021
by
Aleksandr Veselov
Committed by
GitHub
Nov 17, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Implement Paragraph::getRectsForRange for native (#378)
parent
21a47543
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
131 additions
and
47 deletions
+131
-47
IRange.kt
skiko/src/commonMain/kotlin/org/jetbrains/skia/IRange.kt
+11
-0
Paragraph.kt
...mmonMain/kotlin/org/jetbrains/skia/paragraph/Paragraph.kt
+8
-7
TextBox.kt
...commonMain/kotlin/org/jetbrains/skia/paragraph/TextBox.kt
+10
-3
ParagraphTest.kt
...src/commonTest/kotlin/org/jetbrains/skia/ParagraphTest.kt
+75
-14
assertCloseEnough.kt
...nTest/kotlin/org/jetbrains/skia/util/assertCloseEnough.kt
+13
-0
Paragraph.cc
skiko/src/jvmMain/cpp/common/paragraph/Paragraph.cc
+7
-3
Paragraph.cc
skiko/src/nativeJsMain/cpp/paragraph/Paragraph.cc
+7
-20
No files found.
skiko/src/commonMain/kotlin/org/jetbrains/skia/IRange.kt
View file @
25ff8bee
package
org.jetbrains.skia
import
org.jetbrains.skia.impl.InteropPointer
import
org.jetbrains.skia.impl.InteropScope
import
org.jetbrains.skia.impl.withResult
class
IRange
(
val
start
:
Int
,
val
end
:
Int
)
{
override
fun
equals
(
other
:
Any
?):
Boolean
{
if
(
other
===
this
)
return
true
...
...
@@ -19,4 +23,11 @@ class IRange(val start: Int, val end: Int) {
override
fun
toString
():
String
{
return
"IRange(_start=$start, _end=$end)"
}
companion
object
{}
}
internal
fun
IRange
.
Companion
.
fromInteropPointer
(
block
:
InteropScope
.(
InteropPointer
)
->
Unit
):
IRange
{
val
result
=
withResult
(
IntArray
(
2
),
block
)
return
IRange
(
result
[
0
],
result
[
1
])
}
\ No newline at end of file
skiko/src/commonMain/kotlin/org/jetbrains/skia/paragraph/Paragraph.kt
View file @
25ff8bee
package
org.jetbrains.skia.paragraph
import
org.jetbrains.skia.impl.Library.Companion.staticLoad
import
org.jetbrains.skia.*
import
org.jetbrains.skia.ExternalSymbolName
import
org.jetbrains.skia.impl.*
import
org.jetbrains.skia.impl.Library.Companion.staticLoad
class
Paragraph
internal
constructor
(
ptr
:
NativePointer
,
text
:
ManagedString
?)
:
Managed
(
ptr
,
_FinalizerHolder
.
PTR
)
{
companion
object
{
...
...
@@ -108,13 +107,15 @@ class Paragraph internal constructor(ptr: NativePointer, text: ManagedString?) :
):
Array
<
TextBox
>
{
return
try
{
Stats
.
onNativeCall
()
_nGetRectsForRange
(
_ptr
,
start
,
end
,
rectHeightMode
.
ordinal
,
rectWidthMode
.
ordinal
)
interopScope
{
_nGetRectsForRange
(
_ptr
,
start
,
end
,
rectHeightMode
.
ordinal
,
rectWidthMode
.
ordinal
).
fromInterop
(
TextBox
)
}
}
finally
{
reachabilityBarrier
(
this
)
}
}
val
rectsForPlaceholders
:
Array
<
org
.
jetbrains
.
skia
.
paragraph
.
TextBox
>
val
rectsForPlaceholders
:
Array
<
TextBox
>
get
()
=
try
{
Stats
.
onNativeCall
()
interopScope
{
...
...
@@ -142,7 +143,7 @@ class Paragraph internal constructor(ptr: NativePointer, text: ManagedString?) :
fun
getWordBoundary
(
offset
:
Int
):
IRange
{
return
try
{
Stats
.
onNativeCall
()
toIRange
(
_nGetWordBoundary
(
_ptr
,
offset
))
IRange
.
fromInteropPointer
{
_nGetWordBoundary
(
_ptr
,
offset
,
it
)
}
}
finally
{
reachabilityBarrier
(
this
)
}
...
...
@@ -305,7 +306,7 @@ private external fun _nGetRectsForRange(
end
:
Int
,
rectHeightMode
:
Int
,
rectWidthMode
:
Int
):
Array
<
TextBox
>
):
InteropPointer
@ExternalSymbolName
(
"org_jetbrains_skia_paragraph_Paragraph__1nGetRectsForPlaceholders"
)
...
...
@@ -315,7 +316,7 @@ private external fun _nGetRectsForPlaceholders(ptr: NativePointer): InteropPoint
private
external
fun
_nGetGlyphPositionAtCoordinate
(
ptr
:
NativePointer
,
dx
:
Float
,
dy
:
Float
):
Int
@ExternalSymbolName
(
"org_jetbrains_skia_paragraph_Paragraph__1nGetWordBoundary"
)
private
external
fun
_nGetWordBoundary
(
ptr
:
NativePointer
,
offset
:
Int
):
Long
private
external
fun
_nGetWordBoundary
(
ptr
:
NativePointer
,
offset
:
Int
,
result
:
InteropPointer
)
@ExternalSymbolName
(
"org_jetbrains_skia_paragraph_Paragraph__1nGetLineMetrics"
)
private
external
fun
_nGetLineMetrics
(
ptr
:
NativePointer
,
textPtr
:
NativePointer
):
InteropPointer
...
...
skiko/src/commonMain/kotlin/org/jetbrains/skia/paragraph/TextBox.kt
View file @
25ff8bee
package
org.jetbrains.skia.paragraph
import
org.jetbrains.skia.*
import
org.jetbrains.skia.impl.*
import
org.jetbrains.skia.ExternalSymbolName
import
org.jetbrains.skia.Rect
import
org.jetbrains.skia.impl.ArrayInteropDecoder
import
org.jetbrains.skia.impl.InteropPointer
import
org.jetbrains.skia.impl.interopScope
class
TextBox
(
val
rect
:
Rect
,
direction
:
Direction
)
{
val
_direction
:
Direction
...
...
@@ -42,7 +45,11 @@ class TextBox(val rect: Rect, direction: Direction) {
val
rect
=
FloatArray
(
4
)
val
direction
=
IntArray
(
1
)
interopScope
{
TextBox_nGetArrayElement
(
array
,
index
,
toInterop
(
rect
),
toInterop
(
direction
))
val
rectPtr
=
toInterop
(
rect
)
val
directionPtr
=
toInterop
(
direction
)
TextBox_nGetArrayElement
(
array
,
index
,
rectPtr
,
directionPtr
)
rectPtr
.
fromInterop
(
rect
)
directionPtr
.
fromInterop
(
direction
)
}
return
TextBox
(
rect
[
0
],
rect
[
1
],
rect
[
2
],
rect
[
3
],
direction
[
0
])
}
...
...
skiko/src/commonTest/kotlin/org/jetbrains/skia/ParagraphTest.kt
View file @
25ff8bee
package
org.jetbrains.skia
import
org.jetbrains.skia.impl.use
import
org.jetbrains.skia.paragraph.
FontCollection
import
org.jetbrains.skia.
paragraph.LineMetrics
import
org.jetbrains.skia.
paragraph.ParagraphBuilder
import
org.jetbrains.skia.
paragraph.ParagraphStyl
e
import
org.jetbrains.skia.paragraph.
*
import
org.jetbrains.skia.
tests.assertCloseEnough
import
org.jetbrains.skia.
tests.assertContentCloseEnough
import
org.jetbrains.skia.
tests.makeFromResourc
e
import
org.jetbrains.skiko.tests.SkipJsTarget
import
org.jetbrains.skiko.tests.SkipNativeTarget
import
org.jetbrains.skiko.tests.runTest
import
kotlin.test.Test
import
kotlin.test.assertContentEquals
import
kotlin.test.assertEquals
import
kotlin.test.assertTrue
@SkipJsTarget
@SkipNativeTarget
class
ParagraphTest
{
private
val
fontCollection
=
FontCollection
().
setDefaultFontManager
(
FontMgr
.
default
)
private
val
fontCollection
=
suspend
{
FontCollection
().
setDefaultFontManager
(
TypefaceFontProvider
().
apply
{
registerTypeface
(
Typeface
.
makeFromResource
(
"./fonts/Inter-Hinted-Regular.ttf"
),
"Inter"
)
})
}
private
val
style
=
ParagraphStyle
().
apply
{
height
=
40.0f
maxLinesCount
=
2
textStyle
=
TextStyle
().
apply
{
fontFamilies
=
arrayOf
(
"Inter"
)
fontSize
=
14.0f
}
}
@Test
fun
findTypefaces
()
{
fontCollection
.
findTypefaces
(
emptyArray
(),
FontStyle
.
NORMAL
)
@SkipJsTarget
@SkipNativeTarget
fun
findTypefaces
()
=
runTest
{
fontCollection
().
findTypefaces
(
emptyArray
(),
FontStyle
.
NORMAL
)
}
private
fun
singleLineMetrics
(
text
:
String
):
LineMetrics
{
val
style
=
ParagraphStyle
()
return
ParagraphBuilder
(
style
,
fontCollection
).
use
{
private
suspend
fun
singleLineMetrics
(
text
:
String
):
LineMetrics
{
return
ParagraphBuilder
(
style
,
fontCollection
()).
use
{
it
.
addText
(
text
)
it
.
build
()
}.
layout
(
Float
.
POSITIVE_INFINITY
).
lineMetrics
.
first
()
}
@Test
fun
layoutParagraph
()
{
@SkipJsTarget
@SkipNativeTarget
fun
layoutParagraph
()
=
runTest
{
singleLineMetrics
(
"aa"
).
let
{
lineMetrics
->
// latin
assertEquals
(
0
,
lineMetrics
.
startIndex
)
assertEquals
(
2
,
lineMetrics
.
endIndex
)
...
...
@@ -44,4 +59,50 @@ class ParagraphTest {
assertEquals
(
2
,
lineMetrics
.
endExcludingWhitespaces
)
}
}
@Test
fun
canCreate
()
=
runTest
{
val
text
=
"Hello,\n Пользователь1!"
var
paragraph
=
ParagraphBuilder
(
style
,
fontCollection
()).
use
{
it
.
addText
(
text
)
it
.
build
()
}.
layout
(
100.0f
)
assertCloseEnough
(
100.0f
,
paragraph
.
maxWidth
,
0.01f
)
assertCloseEnough
(
102.63f
,
paragraph
.
minIntrinsicWidth
,
0.01f
)
assertCloseEnough
(
110.47f
,
paragraph
.
maxIntrinsicWidth
,
0.01f
)
assertCloseEnough
(
13.5625f
,
paragraph
.
alphabeticBaseline
,
0.01f
)
assertCloseEnough
(
16.943085f
,
paragraph
.
ideographicBaseline
,
0.01f
)
assertCloseEnough
(
92.3125f
,
paragraph
.
longestLine
,
0.01f
)
assertCloseEnough
(
34.0f
,
paragraph
.
height
)
assertTrue
(
paragraph
.
didExceedMaxLines
())
assertEquals
(
IRange
(
0
,
5
),
paragraph
.
getWordBoundary
(
0
))
assertEquals
(
IRange
(
8
,
21
),
paragraph
.
getWordBoundary
(
10
))
assertEquals
(
0
,
paragraph
.
unresolvedGlyphsCount
)
assertEquals
(
2
,
paragraph
.
lineNumber
)
assertContentEquals
(
arrayOf
(),
paragraph
.
rectsForPlaceholders
)
assertContentCloseEnough
(
arrayOf
(
TextBox
(
Rect
(
3.94f
,
17.06f
,
92.31f
,
34.0f
),
Direction
.
LTR
)
),
paragraph
.
getRectsForRange
(
8
,
21
,
RectHeightMode
.
TIGHT
,
RectWidthMode
.
TIGHT
),
0.01f
)
paragraph
=
paragraph
.
updateFontSize
(
8
,
21
,
48.0f
)
.
updateForegroundPaint
(
8
,
21
,
Paint
().
apply
{
color
=
Color
.
RED
})
.
updateBackgroundPaint
(
8
,
21
,
Paint
().
apply
{
color
=
Color
.
BLACK
})
.
updateAlignment
(
Alignment
.
RIGHT
)
.
markDirty
()
assertContentCloseEnough
(
arrayOf
(
TextBox
(
Rect
(
3.94f
,
17.06f
,
92.31f
,
34.0f
),
Direction
.
LTR
)
),
paragraph
.
getRectsForRange
(
8
,
21
,
RectHeightMode
.
TIGHT
,
RectWidthMode
.
TIGHT
),
0.01f
)
assertEquals
(
PositionWithAffinity
(
5
,
Affinity
.
UPSTREAM
),
paragraph
.
getGlyphPositionAtCoordinate
(
30f
,
10f
)
)
}
}
\ No newline at end of file
skiko/src/commonTest/kotlin/org/jetbrains/skia/util/assertCloseEnough.kt
View file @
25ff8bee
...
...
@@ -4,6 +4,7 @@ import org.jetbrains.skia.Color4f
import
org.jetbrains.skia.Matrix33
import
org.jetbrains.skia.Point
import
org.jetbrains.skia.Rect
import
org.jetbrains.skia.paragraph.TextBox
import
org.jetbrains.skiko.KotlinBackend
import
org.jetbrains.skiko.kotlinBackend
import
kotlin.math.abs
...
...
@@ -25,6 +26,10 @@ private inline fun Rect.isCloseEnoughTo(rect: Rect, epsilon: Float): Boolean =
left
.
isCloseEnoughTo
(
rect
.
left
,
epsilon
)
&&
right
.
isCloseEnoughTo
(
rect
.
right
,
epsilon
)
&&
top
.
isCloseEnoughTo
(
rect
.
top
,
epsilon
)
&&
bottom
.
isCloseEnoughTo
(
rect
.
bottom
,
epsilon
)
private
inline
fun
TextBox
.
isCloseEnoughTo
(
textBox
:
TextBox
,
epsilon
:
Float
=
EPSILON
):
Boolean
{
return
(
direction
==
textBox
.
direction
)
&&
rect
.
isCloseEnoughTo
(
textBox
.
rect
,
epsilon
)
}
internal
fun
assertCloseEnough
(
expected
:
Float
,
actual
:
Float
,
epsilon
:
Float
=
EPSILON
)
{
assertTrue
(
expected
.
isCloseEnoughTo
(
actual
,
epsilon
),
message
=
"expected=$expected, actual=$actual, eps=$epsilon"
)
}
...
...
@@ -33,6 +38,10 @@ internal fun assertCloseEnough(expected: Point, actual: Point, epsilon: Float =
assertTrue
(
expected
.
isCloseEnoughTo
(
actual
,
epsilon
),
message
=
"expected=$expected, actual=$actual, eps=$epsilon"
)
}
internal
fun
assertCloseEnough
(
expected
:
TextBox
,
actual
:
TextBox
,
epsilon
:
Float
=
EPSILON
)
{
assertTrue
(
expected
.
isCloseEnoughTo
(
actual
,
epsilon
),
message
=
"expected=$expected, actual=$actual, eps=$epsilon"
)
}
internal
fun
assertCloseEnough
(
expected
:
Matrix33
,
actual
:
Matrix33
,
epsilon
:
Float
=
EPSILON
)
{
assertTrue
(
expected
.
mat
.
zip
(
actual
.
mat
).
all
{
(
a
,
b
)
->
a
.
isCloseEnoughTo
(
b
,
epsilon
)
},
...
...
@@ -80,3 +89,7 @@ internal fun assertContentCloseEnough(expected: FloatArray, actual: FloatArray,
internal
fun
assertContentCloseEnough
(
expected
:
Array
<
Point
>,
actual
:
Array
<
Point
>,
epsilon
:
Float
=
EPSILON
)
{
assertContentEquivalent
(
expected
.
iterator
(),
actual
.
iterator
())
{
a
,
b
->
a
.
isCloseEnoughTo
(
b
,
epsilon
)
}
}
internal
fun
assertContentCloseEnough
(
expected
:
Array
<
TextBox
>,
actual
:
Array
<
TextBox
>,
epsilon
:
Float
=
EPSILON
)
{
assertContentEquivalent
(
expected
.
iterator
(),
actual
.
iterator
())
{
a
,
b
->
a
.
isCloseEnoughTo
(
b
,
epsilon
)
}
}
skiko/src/jvmMain/cpp/common/paragraph/Paragraph.cc
View file @
25ff8bee
...
...
@@ -117,11 +117,15 @@ extern "C" JNIEXPORT jint JNICALL Java_org_jetbrains_skia_paragraph_ParagraphKt_
return
-
p
.
position
-
1
;
}
extern
"C"
JNIEXPORT
jlong
JNICALL
Java_org_jetbrains_skia_paragraph_ParagraphKt__1nGetWordBoundary
(
JNIEnv
*
env
,
jclass
jclass
,
jlong
ptr
,
jint
offset
)
{
extern
"C"
JNIEXPORT
void
JNICALL
Java_org_jetbrains_skia_paragraph_ParagraphKt__1nGetWordBoundary
(
JNIEnv
*
env
,
jclass
jclass
,
jlong
ptr
,
jint
offset
,
jintArray
resultArray
)
{
Paragraph
*
instance
=
reinterpret_cast
<
Paragraph
*>
(
static_cast
<
uintptr_t
>
(
ptr
));
SkRange
<
size_t
>
range
=
instance
->
getWordBoundary
(
offset
);
return
packTwoInts
(
range
.
start
&
0xFFFFFFFF
,
range
.
end
&
0xFFFFFFFF
);
jint
result
[]
{
static_cast
<
jint
>
(
range
.
start
&
0xFFFFFFFF
),
static_cast
<
jint
>
(
range
.
end
&
0xFFFFFFFF
)
};
env
->
SetIntArrayRegion
(
resultArray
,
0
,
2
,
result
);
}
extern
"C"
JNIEXPORT
jobjectArray
JNICALL
Java_org_jetbrains_skia_paragraph_ParagraphKt__1nGetLineMetrics
...
...
skiko/src/nativeJsMain/cpp/paragraph/Paragraph.cc
View file @
25ff8bee
...
...
@@ -78,27 +78,12 @@ SKIKO_EXPORT void org_jetbrains_skia_paragraph_Paragraph__1nPaint
instance
->
paint
(
canvas
,
x
,
y
);
}
SKIKO_EXPORT
KInteropPointerArray
org_jetbrains_skia_paragraph_Paragraph__1nGetRectsForRange
(
KNativePointer
ptr
,
KInt
start
,
KInt
end
,
KInt
rectHeightStyle
,
KInt
rectWidthStyle
)
{
TODO
(
"implement org_jetbrains_skia_paragraph_Paragraph__1nGetRectsForRange"
);
}
#if 0
SKIKO_EXPORT
KInteropPointerArray
org_jetbrains_skia_paragraph_Paragraph__1nGetRectsForRange
(
KNativePointer
ptr
,
KInt
start
,
KInt
end
,
KInt
rectHeightStyle
,
KInt
rectWidthStyle
)
{
Paragraph
*
instance
=
reinterpret_cast
<
Paragraph
*>
((
ptr
));
std::vector<TextBox> rects = instance->getRectsForRange(start, end, static_cast<RectHeightStyle>(rectHeightStyle), static_cast<RectWidthStyle>(rectWidthStyle));
KInteropPointerArray rectsArray = env->NewObjectArray((jsize) rects.size(), skija::paragraph::TextBox::cls, nullptr);
for (int i = 0; i < rects.size(); ++i) {
TextBox box = rects[i];
KInteropPointer boxObj = env->NewObject(skija::paragraph::TextBox::cls, skija::paragraph::TextBox::ctor, box.rect.fLeft, box.rect.fTop, box.rect.fRight, box.rect.fBottom, static_cast<KInt>(box.direction));
env->SetObjectArrayElement(rectsArray, i, boxObj);
env->DeleteLocalRef(boxObj);
}
return rectsArray;
std
::
vector
<
TextBox
>
*
rects
=
new
std
::
vector
<
TextBox
>
(
instance
->
getRectsForRange
(
start
,
end
,
static_cast
<
RectHeightStyle
>
(
rectHeightStyle
),
static_cast
<
RectWidthStyle
>
(
rectWidthStyle
)));
return
rects
;
}
#endif
SKIKO_EXPORT
KInteropPointerArray
org_jetbrains_skia_paragraph_Paragraph__1nGetRectsForPlaceholders
(
KNativePointer
ptr
)
{
...
...
@@ -117,11 +102,13 @@ SKIKO_EXPORT KInt org_jetbrains_skia_paragraph_Paragraph__1nGetGlyphPositionAtCo
return
-
p
.
position
-
1
;
}
SKIKO_EXPORT
KLong
org_jetbrains_skia_paragraph_Paragraph__1nGetWordBoundary
(
KNativePointer
ptr
,
KInt
offset
)
{
SKIKO_EXPORT
void
org_jetbrains_skia_paragraph_Paragraph__1nGetWordBoundary
(
KNativePointer
ptr
,
KInt
offset
,
KInteropPointer
resultPtr
)
{
Paragraph
*
instance
=
reinterpret_cast
<
Paragraph
*>
((
ptr
));
SkRange
<
size_t
>
range
=
instance
->
getWordBoundary
(
offset
);
return
packTwoInts
(
range
.
start
&
0xFFFFFFFF
,
range
.
end
&
0xFFFFFFFF
);
KInt
*
result
=
reinterpret_cast
<
KInt
*>
(
resultPtr
);
result
[
0
]
=
range
.
start
&
0xFFFFFFFF
;
result
[
1
]
=
range
.
end
&
0xFFFFFFFF
;
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment