Skip to content

Commit 7c8c91a

Browse files
committed
Extract the Skiko graphics backend into a new ui-skiko module
Move the Skia-backed implementation of ui-graphics into a new :compose:ui:ui-skiko module, leaving ui-graphics' nonAndroidMain Skia-free. The common code keeps thin expect/actual delegators that resolve a registered ComposeUiGraphicsImplementation at runtime; the real org.jetbrains.skia code lives in ui-skiko's Skiko*Support classes. Source-set topology: commonMain -> nonAndroidMain (Skia-free) -> skikoMain (Skia compat) -> desktop/native/web leaves. - Canvas/Paint/Path/PathEffect/PathMeasure/PathIterator/ImageBitmap/ Matrices/GraphicsLayer/Blur/Rects relocate to ui-skiko as registry- backed delegators. - The Skia-holding expect-class actuals (Shader, ColorFilter, BlurEffect/OffsetEffect/RenderEffect) become Skiko-free via the PlatformBindings pattern: an opaque @InternalComposeUiApi Platform* binding in ui-graphics, implemented in ui-skiko. - Public Skia-interop extensions (skiaPaint, skiaImageFilter, asComposePath, ...) move to ui-skiko keeping their original package and @file:JvmName facade anchors, preserving desktop binary compatibility. Deprecated Skia-returning members that cannot be extensions delegate through a temporary SkikoGraphicsCompat registry. API baselines are regenerated in a later commit.
1 parent 94de830 commit 7c8c91a

74 files changed

Lines changed: 2845 additions & 1219 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

compose/ui/ui-graphics/build.gradle

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -95,38 +95,50 @@ androidXMultiplatform {
9595
implementation(project(":compose:test-utils"))
9696
}
9797

98-
// TODO: Align naming: nonAndroidMain
99-
skikoMain {
98+
nonAndroidMain {
10099
dependsOn(commonMain)
101100
dependencies {
102-
api(libs.skiko)
101+
// No skiko here: nonAndroidMain is skia-free. Skia lives in skikoMain (below).
102+
implementation(libs.atomicFu)
103103
}
104104
}
105105

106-
skikoTest {
106+
nonAndroidTest {
107107
dependsOn(commonTest)
108108
dependencies {
109109
implementation(project(":compose:ui:ui-test"))
110+
implementation(project(":compose:ui:ui-skiko"))
110111
implementation(project(":kruth:kruth"))
111112
}
112113
}
113114

114-
skikoExcludingWebMain {
115+
// Skia-bearing compatibility layer between the skia-free nonAndroidMain and the platform
116+
// leaves. Holds public skia-typed API structurally pinned to ui-graphics (deprecated
117+
// skia-returning members + Native* typealiases).
118+
// commonMain -> nonAndroidMain -> skikoMain -> (desktop/native/web).
119+
skikoMain {
120+
dependsOn(nonAndroidMain)
121+
dependencies {
122+
api(libs.skiko)
123+
}
124+
}
125+
126+
nonAndroidExcludingWebMain {
115127
dependsOn(skikoMain)
116128
}
117129

118-
skikoExcludingWebTest {
119-
dependsOn(skikoTest)
130+
nonAndroidExcludingWebTest {
131+
dependsOn(nonAndroidTest)
120132
}
121133

122134
desktopMain {
123135
dependsOn(skikoMain)
124-
dependsOn(skikoExcludingWebMain)
136+
dependsOn(nonAndroidExcludingWebMain)
125137
}
126138

127139
desktopTest {
128-
dependsOn(skikoTest)
129-
dependsOn(skikoExcludingWebTest)
140+
dependsOn(nonAndroidTest)
141+
dependsOn(nonAndroidExcludingWebTest)
130142
resources.srcDirs += "src/desktopTest/res"
131143
dependencies {
132144
implementation(libs.junit)
@@ -145,17 +157,17 @@ androidXMultiplatform {
145157
}
146158

147159
nonJvmTest {
148-
dependsOn(skikoTest)
160+
dependsOn(nonAndroidTest)
149161
}
150162

151163
nativeMain {
152164
dependsOn(nonJvmMain)
153-
dependsOn(skikoExcludingWebMain)
165+
dependsOn(nonAndroidExcludingWebMain)
154166
}
155167

156168
nativeTest {
157169
dependsOn(nonJvmTest)
158-
dependsOn(skikoExcludingWebTest)
170+
dependsOn(nonAndroidExcludingWebTest)
159171
}
160172

161173
wasmJsMain {
@@ -191,4 +203,4 @@ tasks.withType(KotlinCompile).configureEach { task ->
191203

192204
tasks.findByName("desktopTest").configure {
193205
systemProperties["GOLDEN_PATH"] = project.rootDir.absolutePath + "/golden"
194-
}
206+
}

compose/ui/ui-graphics/src/skikoMain/kotlin/androidx/compose/ui/graphics/SkiaVertexMode.skiko.kt renamed to compose/ui/ui-graphics/src/nonAndroidMain/kotlin/androidx/compose/ui/graphics/Canvas.nonAndroid.kt

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,9 @@
1616

1717
package androidx.compose.ui.graphics
1818

19-
import org.jetbrains.skia.VertexMode as SkVertexMode
19+
import androidx.compose.ui.InternalComposeUiApi
20+
import androidx.compose.ui.graphics.platform.PlatformGraphicsRegistry
2021

21-
internal fun VertexMode.toSkiaVertexMode(): SkVertexMode = when (this) {
22-
VertexMode.Triangles -> SkVertexMode.TRIANGLES
23-
VertexMode.TriangleStrip -> SkVertexMode.TRIANGLE_STRIP
24-
VertexMode.TriangleFan -> SkVertexMode.TRIANGLE_FAN
25-
else -> SkVertexMode.TRIANGLES
26-
}
22+
@OptIn(InternalComposeUiApi::class)
23+
internal actual fun ActualCanvas(image: ImageBitmap): Canvas =
24+
PlatformGraphicsRegistry.requireCurrent().createCanvas(image)
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright 2021 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package androidx.compose.ui.graphics
18+
19+
import androidx.compose.ui.InternalComposeUiApi
20+
import androidx.compose.ui.graphics.platform.PlatformColorFilter
21+
import androidx.compose.ui.graphics.platform.PlatformGraphicsRegistry
22+
23+
// Use `Any` to avoid double-wrapping and EXPECT_ACTUAL_INCOMPATIBLE_CLASS_KIND
24+
internal actual typealias NativeColorFilter = Any // == PlatformColorFilter
25+
26+
@OptIn(InternalComposeUiApi::class)
27+
internal actual fun actualTintColorFilter(color: Color, blendMode: BlendMode): NativeColorFilter =
28+
PlatformGraphicsRegistry.requireCurrent()
29+
.createTintColorFilter(color, blendMode)
30+
31+
@OptIn(InternalComposeUiApi::class)
32+
internal actual fun actualColorMatrixColorFilter(colorMatrix: ColorMatrix): NativeColorFilter =
33+
PlatformGraphicsRegistry.requireCurrent()
34+
.createColorMatrixColorFilter(colorMatrix)
35+
36+
@OptIn(InternalComposeUiApi::class)
37+
internal actual fun actualLightingColorFilter(multiply: Color, add: Color): NativeColorFilter =
38+
PlatformGraphicsRegistry.requireCurrent()
39+
.createLightingColorFilter(multiply, add)
40+
41+
@OptIn(InternalComposeUiApi::class)
42+
internal actual fun actualColorMatrixFromFilter(filter: NativeColorFilter): ColorMatrix =
43+
PlatformGraphicsRegistry.requireCurrent()
44+
.colorMatrixFromFilter(filter as PlatformColorFilter)
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright 2024 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package androidx.compose.ui.graphics
18+
19+
import androidx.compose.ui.InternalComposeUiApi
20+
import androidx.compose.ui.graphics.colorspace.ColorSpace
21+
import androidx.compose.ui.graphics.platform.PlatformGraphicsRegistry
22+
23+
@OptIn(InternalComposeUiApi::class)
24+
internal actual fun ActualImageBitmap(
25+
width: Int,
26+
height: Int,
27+
config: ImageBitmapConfig,
28+
hasAlpha: Boolean,
29+
colorSpace: ColorSpace,
30+
): ImageBitmap =
31+
PlatformGraphicsRegistry.requireCurrent().createImageBitmap(
32+
width = width,
33+
height = height,
34+
config = config,
35+
hasAlpha = hasAlpha,
36+
colorSpace = colorSpace,
37+
)
38+
39+
@OptIn(InternalComposeUiApi::class)
40+
internal actual fun createImageBitmap(bytes: ByteArray): ImageBitmap =
41+
PlatformGraphicsRegistry.requireCurrent().decodeImageBitmap(bytes)
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright 2020 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package androidx.compose.ui.graphics
18+
19+
import androidx.compose.ui.InternalComposeUiApi
20+
import androidx.compose.ui.graphics.platform.PlatformGraphicsRegistry
21+
22+
@OptIn(InternalComposeUiApi::class)
23+
actual fun Paint(): Paint =
24+
PlatformGraphicsRegistry.requireCurrent().createPaint()
25+
26+
@OptIn(InternalComposeUiApi::class)
27+
actual fun BlendMode.isSupported(): Boolean =
28+
PlatformGraphicsRegistry.requireCurrent().isBlendModeSupported(this)

compose/ui/ui-graphics/src/skikoMain/kotlin/androidx/compose/ui/graphics/ImageBitmap.skiko.kt renamed to compose/ui/ui-graphics/src/nonAndroidMain/kotlin/androidx/compose/ui/graphics/Path.nonAndroid.kt

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2024 The Android Open Source Project
2+
* Copyright 2020 The Android Open Source Project
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,7 +16,9 @@
1616

1717
package androidx.compose.ui.graphics
1818

19-
import org.jetbrains.skia.Image
19+
import androidx.compose.ui.InternalComposeUiApi
20+
import androidx.compose.ui.graphics.platform.PlatformGraphicsRegistry
2021

21-
internal actual fun createImageBitmap(bytes: ByteArray): ImageBitmap =
22-
Image.makeFromEncoded(bytes).toComposeImageBitmap()
22+
@OptIn(InternalComposeUiApi::class)
23+
actual fun Path(): Path =
24+
PlatformGraphicsRegistry.requireCurrent().createPath()
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright 2020 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package androidx.compose.ui.graphics
18+
19+
import androidx.compose.ui.InternalComposeUiApi
20+
import androidx.compose.ui.graphics.platform.PlatformGraphicsRegistry
21+
22+
@OptIn(InternalComposeUiApi::class)
23+
internal actual fun actualCornerPathEffect(radius: Float): PathEffect =
24+
PlatformGraphicsRegistry.requireCurrent().createCornerPathEffect(radius)
25+
26+
@OptIn(InternalComposeUiApi::class)
27+
internal actual fun actualDashPathEffect(
28+
intervals: FloatArray,
29+
phase: Float,
30+
): PathEffect =
31+
PlatformGraphicsRegistry.requireCurrent().createDashPathEffect(intervals, phase)
32+
33+
@OptIn(InternalComposeUiApi::class)
34+
internal actual fun actualChainPathEffect(outer: PathEffect, inner: PathEffect): PathEffect =
35+
PlatformGraphicsRegistry.requireCurrent().createChainPathEffect(outer, inner)
36+
37+
@OptIn(InternalComposeUiApi::class)
38+
internal actual fun actualStampedPathEffect(
39+
shape: Path,
40+
advance: Float,
41+
phase: Float,
42+
style: StampedPathEffectStyle,
43+
): PathEffect =
44+
PlatformGraphicsRegistry.requireCurrent().createStampedPathEffect(
45+
shape = shape,
46+
advance = advance,
47+
phase = phase,
48+
style = style,
49+
)
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright 2024 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package androidx.compose.ui.graphics
18+
19+
import androidx.compose.ui.InternalComposeUiApi
20+
import androidx.compose.ui.graphics.platform.PlatformGraphicsRegistry
21+
22+
@OptIn(InternalComposeUiApi::class)
23+
actual fun PathIterator(
24+
path: Path,
25+
conicEvaluation: PathIterator.ConicEvaluation,
26+
tolerance: Float
27+
): PathIterator =
28+
PlatformGraphicsRegistry.requireCurrent().createPathIterator(
29+
path = path,
30+
conicEvaluation = conicEvaluation,
31+
tolerance = tolerance,
32+
)
33+
34+
@InternalComposeUiApi
35+
fun createPathSegment(type: PathSegment.Type, points: FloatArray, weight: Float): PathSegment =
36+
PathSegment(type, points, weight)

compose/ui/ui-graphics/src/skikoMain/kotlin/androidx/compose/ui/graphics/SkiaTileMode.skiko.kt renamed to compose/ui/ui-graphics/src/nonAndroidMain/kotlin/androidx/compose/ui/graphics/PathMeasure.nonAndroid.kt

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,9 @@
1616

1717
package androidx.compose.ui.graphics
1818

19-
import org.jetbrains.skia.FilterTileMode
19+
import androidx.compose.ui.InternalComposeUiApi
20+
import androidx.compose.ui.graphics.platform.PlatformGraphicsRegistry
2021

21-
actual fun TileMode.isSupported(): Boolean = true
22-
23-
internal fun TileMode.toSkiaTileMode(): FilterTileMode = when (this) {
24-
TileMode.Clamp -> FilterTileMode.CLAMP
25-
TileMode.Repeated -> FilterTileMode.REPEAT
26-
TileMode.Mirror -> FilterTileMode.MIRROR
27-
TileMode.Decal -> FilterTileMode.DECAL
28-
else -> FilterTileMode.CLAMP
29-
}
22+
@OptIn(InternalComposeUiApi::class)
23+
actual fun PathMeasure(): PathMeasure =
24+
PlatformGraphicsRegistry.requireCurrent().createPathMeasure()

0 commit comments

Comments
 (0)