Skip to content

Commit 9244c20

Browse files
authored
Use optimized SkiaBacked api that avoid intermediate objects (JetBrains#2647)
Changes the SkiaBackedCanvas to internally use more optimized functions based [on this Skiko PR](JetBrains/skiko#1137) It also includes minimal changes like: 1. [Micro optimizations for DefaultOpenContextMenu to use non iterator-allocator functions](JetBrains@577e1b1) 2. [Update Modifier.drawSelectionHandle to not use Composed Api](JetBrains@1cc7182) 3. [Changed BasicContextMenu's onClose to use method reference](JetBrains@b7d3bac) ## Testing N/A ## Release Notes N/A
1 parent e184d51 commit 9244c20

3 files changed

Lines changed: 85 additions & 69 deletions

File tree

compose/foundation/foundation/src/skikoMain/kotlin/androidx/compose/foundation/BasicContextMenuRepresentation.skiko.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ import androidx.compose.ui.input.key.type
5555
import androidx.compose.ui.semantics.semantics
5656
import androidx.compose.ui.text.TextStyle
5757
import androidx.compose.ui.unit.dp
58+
import androidx.compose.ui.util.fastForEach
5859
import androidx.compose.ui.window.Popup
5960
import androidx.compose.ui.window.PopupPositionProvider
6061
import androidx.compose.ui.window.PopupProperties
@@ -75,7 +76,7 @@ internal fun DefaultOpenContextMenu(
7576

7677
Popup(
7778
properties = PopupProperties(focusable = true),
78-
onDismissRequest = { session.close() },
79+
onDismissRequest = session::close,
7980
popupPositionProvider = popupPositionProvider,
8081
onKeyEvent = {
8182
if (it.type == KeyEventType.KeyDown) {
@@ -105,7 +106,7 @@ internal fun DefaultOpenContextMenu(
105106
.width(IntrinsicSize.Max)
106107
.verticalScroll(rememberScrollState())
107108
) {
108-
components.forEach { component ->
109+
components.fastForEach { component ->
109110
when (component) {
110111
is TextContextMenuSeparator ->
111112
MenuSeparator(colors.textColor)

compose/foundation/foundation/src/skikoMain/kotlin/androidx/compose/foundation/text/selection/SelectionHandles.skiko.kt

Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,15 @@ import androidx.compose.runtime.Composable
2424
import androidx.compose.runtime.remember
2525
import androidx.compose.ui.Alignment
2626
import androidx.compose.ui.Modifier
27-
import androidx.compose.ui.composed
2827
import androidx.compose.ui.draw.drawWithCache
2928
import androidx.compose.ui.geometry.Offset
3029
import androidx.compose.ui.geometry.Size
3130
import androidx.compose.ui.geometry.isSpecified
31+
import androidx.compose.ui.graphics.Color
3232
import androidx.compose.ui.platform.LocalDensity
3333
import androidx.compose.ui.semantics.semantics
3434
import androidx.compose.ui.text.style.ResolvedTextDirection
35+
import androidx.compose.ui.unit.Density
3536
import androidx.compose.ui.unit.DpSize
3637
import androidx.compose.ui.unit.dp
3738
import androidx.compose.ui.window.Popup
@@ -113,50 +114,49 @@ internal fun SelectionHandleIcon(
113114
isLeft: Boolean,
114115
) {
115116
val density = LocalDensity.current
117+
val handleColor = LocalTextSelectionColors.current.handleColor
116118
val lineHeightDp = with(density) { lineHeight.toDp() }
117119
Spacer(
118120
modifier
119121
.size(
120122
width = (PADDING + RADIUS) * 2,
121123
height = RADIUS * 2 + PADDING + lineHeightDp
122124
)
123-
.drawSelectionHandle(iconVisible, lineHeight, isLeft)
125+
.drawSelectionHandle(iconVisible, lineHeight, isLeft, handleColor, density)
124126
)
125127
}
126128

127129
internal fun Modifier.drawSelectionHandle(
128130
iconVisible: () -> Boolean,
129131
lineHeight: Float,
130-
isLeft: Boolean
131-
): Modifier = composed {
132-
val density = LocalDensity.current
132+
isLeft: Boolean,
133+
handleColor: Color,
134+
density: Density
135+
): Modifier = drawWithCache {
133136
val paddingPx = with(density) { PADDING.toPx() }
134137
val radiusPx = with(density) { RADIUS.toPx() }
135138
val thicknessPx = with(density) { THICKNESS.toPx() }
136-
val handleColor = LocalTextSelectionColors.current.handleColor
137-
this.drawWithCache {
138-
onDrawWithContent {
139-
drawContent()
140-
if (!iconVisible()) return@onDrawWithContent
139+
onDrawWithContent {
140+
drawContent()
141+
if (!iconVisible()) return@onDrawWithContent
141142

142-
// vertical line
143-
drawRect(
144-
color = handleColor,
145-
topLeft = Offset(
146-
x = paddingPx + radiusPx - thicknessPx / 2,
147-
y = if (isLeft) paddingPx + radiusPx else 0f
148-
),
149-
size = Size(thicknessPx, lineHeight + radiusPx)
150-
)
151-
// handle circle
152-
drawCircle(
153-
color = handleColor,
154-
radius = radiusPx,
155-
center = center.copy(
156-
y = if (isLeft) paddingPx + radiusPx else lineHeight + radiusPx
157-
)
143+
// vertical line
144+
drawRect(
145+
color = handleColor,
146+
topLeft = Offset(
147+
x = paddingPx + radiusPx - thicknessPx / 2,
148+
y = if (isLeft) paddingPx + radiusPx else 0f
149+
),
150+
size = Size(thicknessPx, lineHeight + radiusPx)
151+
)
152+
// handle circle
153+
drawCircle(
154+
color = handleColor,
155+
radius = radiusPx,
156+
center = center.copy(
157+
y = if (isLeft) paddingPx + radiusPx else lineHeight + radiusPx
158158
)
159-
}
159+
)
160160
}
161161
}
162162

compose/ui/ui-graphics/src/skikoMain/kotlin/androidx/compose/ui/graphics/SkiaBackedCanvas.skiko.kt

Lines changed: 55 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,12 @@
1717
package androidx.compose.ui.graphics
1818

1919
import androidx.compose.runtime.InternalComposeApi
20-
import org.jetbrains.skia.ClipMode as SkClipMode
21-
import org.jetbrains.skia.RRect as SkRRect
22-
import org.jetbrains.skia.Rect as SkRect
2320
import androidx.compose.ui.geometry.Offset
2421
import androidx.compose.ui.geometry.Rect
25-
import androidx.compose.ui.geometry.Size
2622
import androidx.compose.ui.unit.IntOffset
2723
import androidx.compose.ui.unit.IntSize
2824
import androidx.compose.ui.util.fastForEach
25+
import org.jetbrains.skia.ClipMode as SkClipMode
2926
import org.jetbrains.skia.CubicResampler
3027
import org.jetbrains.skia.FilterMipmap
3128
import org.jetbrains.skia.FilterMode
@@ -112,7 +109,14 @@ internal class SkiaBackedCanvas(val skia: org.jetbrains.skia.Canvas) : Canvas {
112109

113110
override fun clipRect(left: Float, top: Float, right: Float, bottom: Float, clipOp: ClipOp) {
114111
val antiAlias = true
115-
skia.clipRect(SkRect.makeLTRB(left, top, right, bottom), clipOp.toSkia(), antiAlias)
112+
skia.clipRect(
113+
left = left,
114+
top = top,
115+
right = right,
116+
bottom = bottom,
117+
mode = clipOp.toSkia(),
118+
antiAlias = antiAlias
119+
)
116120
}
117121

118122
override fun clipPath(path: Path, clipOp: ClipOp) {
@@ -125,7 +129,7 @@ internal class SkiaBackedCanvas(val skia: org.jetbrains.skia.Canvas) : Canvas {
125129
}
126130

127131
override fun drawRect(left: Float, top: Float, right: Float, bottom: Float, paint: Paint) {
128-
skia.drawRect(SkRect.makeLTRB(left, top, right, bottom), paint.skia)
132+
skia.drawRect(left = left, top = top, right = right, bottom = bottom, paint = paint.skia)
129133
}
130134

131135
override fun drawRoundRect(
@@ -138,20 +142,17 @@ internal class SkiaBackedCanvas(val skia: org.jetbrains.skia.Canvas) : Canvas {
138142
paint: Paint
139143
) {
140144
skia.drawRRect(
141-
SkRRect.makeLTRB(
142-
left,
143-
top,
144-
right,
145-
bottom,
146-
radiusX,
147-
radiusY
148-
),
149-
paint.skia
145+
left = left,
146+
top = top,
147+
right = right,
148+
bottom = bottom,
149+
radii = floatArrayOf(radiusX, radiusY),
150+
paint = paint.skia
150151
)
151152
}
152153

153154
override fun drawOval(left: Float, top: Float, right: Float, bottom: Float, paint: Paint) {
154-
skia.drawOval(SkRect.makeLTRB(left, top, right, bottom), paint.skia)
155+
skia.drawOval(left = left, top = top, right = right, bottom = bottom, paint = paint.skia)
155156
}
156157

157158
override fun drawCircle(center: Offset, radius: Float, paint: Paint) {
@@ -185,8 +186,18 @@ internal class SkiaBackedCanvas(val skia: org.jetbrains.skia.Canvas) : Canvas {
185186
}
186187

187188
override fun drawImage(image: ImageBitmap, topLeftOffset: Offset, paint: Paint) {
188-
val size = Size(image.width.toFloat(), image.height.toFloat())
189-
drawImageRect(image, Offset.Zero, size, topLeftOffset, size, paint)
189+
drawImageRect(
190+
image,
191+
0f,
192+
0f,
193+
image.width.toFloat(),
194+
image.height.toFloat(),
195+
topLeftOffset.x,
196+
topLeftOffset.y,
197+
topLeftOffset.x + image.width.toFloat(),
198+
topLeftOffset.y + image.height.toFloat(),
199+
paint
200+
)
190201
}
191202

192203
override fun drawImageRect(
@@ -198,41 +209,45 @@ internal class SkiaBackedCanvas(val skia: org.jetbrains.skia.Canvas) : Canvas {
198209
paint: Paint
199210
) {
200211
drawImageRect(
201-
image,
202-
Offset(srcOffset.x.toFloat(), srcOffset.y.toFloat()),
203-
Size(srcSize.width.toFloat(), srcSize.height.toFloat()),
204-
Offset(dstOffset.x.toFloat(), dstOffset.y.toFloat()),
205-
Size(dstSize.width.toFloat(), dstSize.height.toFloat()),
206-
paint
212+
image = image,
213+
srcLeft = srcOffset.x.toFloat(),
214+
srcTop = srcOffset.y.toFloat(),
215+
srcRight = srcOffset.x.toFloat() + srcSize.width.toFloat(),
216+
srcBottom = srcOffset.y.toFloat() + srcSize.height.toFloat(),
217+
dstLeft = dstOffset.x.toFloat(),
218+
dstTop = dstOffset.y.toFloat(),
219+
dstRight = dstOffset.x.toFloat() + dstSize.width.toFloat(),
220+
dstBottom = dstOffset.y.toFloat() + dstSize.height.toFloat(),
221+
paint = paint
207222
)
208223
}
209224

210225
// TODO(demin): probably this method should be in the common Canvas
211226
private fun drawImageRect(
212227
image: ImageBitmap,
213-
srcOffset: Offset,
214-
srcSize: Size,
215-
dstOffset: Offset,
216-
dstSize: Size,
228+
srcLeft: Float,
229+
srcTop: Float,
230+
srcRight: Float,
231+
srcBottom: Float,
232+
dstLeft: Float,
233+
dstTop: Float,
234+
dstRight: Float,
235+
dstBottom: Float,
217236
paint: Paint
218237
) {
219238
val bitmap = image.asSkiaBitmap()
220239

221240
Image.makeFromBitmap(bitmap).use { skiaImage ->
222241
skia.drawImageRect(
223242
skiaImage,
224-
SkRect.makeXYWH(
225-
srcOffset.x,
226-
srcOffset.y,
227-
srcSize.width,
228-
srcSize.height
229-
),
230-
SkRect.makeXYWH(
231-
dstOffset.x,
232-
dstOffset.y,
233-
dstSize.width,
234-
dstSize.height
235-
),
243+
srcLeft,
244+
srcTop,
245+
srcRight,
246+
srcBottom,
247+
dstLeft,
248+
dstTop,
249+
dstRight,
250+
dstBottom,
236251
paint.filterQuality.toSkia(),
237252
paint.skia,
238253
true

0 commit comments

Comments
 (0)