[go: nahoru, domu]

ui-desktop: implement androidx.ui.graphics (DesktopCanvas, DesktopPaint, DesktopPath)

We can't implement the classes in "true" MPP mainDesktop module yet. As temporary measure, we create GraphicsFactory (looks a little ugly though)

Test: ./gradlew buildOnServer :ui:ui-desktop:jvmTest
Relnote: N/A
Change-Id: I5b473c1a556c781d49a03f12167c40299324138a
diff --git a/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt b/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
index 4ac00d8..5f13437 100644
--- a/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
@@ -88,6 +88,7 @@
 const val REACTIVE_STREAMS = "org.reactivestreams:reactive-streams:1.0.0"
 const val RX_JAVA = "io.reactivex.rxjava2:rxjava:2.2.9"
 const val RX_JAVA3 = "io.reactivex.rxjava3:rxjava:3.0.0"
+const val SKIJA = "org.jetbrains.skija:skija:0.4.1"
 const val TRUTH = "com.google.truth:truth:1.0.1"
 const val XERIAL = "org.xerial:sqlite-jdbc:3.25.2"
 const val XPP3 = "xpp3:xpp3:1.1.4c"
diff --git a/ui/ui-desktop/android-emu/build.gradle b/ui/ui-desktop/android-emu/build.gradle
index 2e35d28..2996764 100644
--- a/ui/ui-desktop/android-emu/build.gradle
+++ b/ui/ui-desktop/android-emu/build.gradle
@@ -41,7 +41,7 @@
         jvmMain.dependencies {
             api(KOTLIN_STDLIB)
 
-            api "org.jetbrains.skija:skija:0.4.1"
+            api(SKIJA)
         }
 
         desktopMain {
diff --git a/ui/ui-desktop/android-emu/src/desktopMain/kotlin/android/graphics/Bitmap.kt b/ui/ui-desktop/android-emu/src/desktopMain/kotlin/android/graphics/Bitmap.kt
index bfa808a..10b5ecd 100644
--- a/ui/ui-desktop/android-emu/src/desktopMain/kotlin/android/graphics/Bitmap.kt
+++ b/ui/ui-desktop/android-emu/src/desktopMain/kotlin/android/graphics/Bitmap.kt
@@ -19,7 +19,7 @@
 import org.jetbrains.skija.Image
 
 class Bitmap(bytes: ByteArray) {
-    internal val skiaImage = Image.makeFromEncoded(bytes)
+    val skiaImage = Image.makeFromEncoded(bytes)
 
     val width = skiaImage.width
     val height = skiaImage.height
diff --git a/ui/ui-desktop/android-emu/src/desktopMain/kotlin/android/graphics/Paint.kt b/ui/ui-desktop/android-emu/src/desktopMain/kotlin/android/graphics/Paint.kt
index a98ff7d..4ea8cf2 100644
--- a/ui/ui-desktop/android-emu/src/desktopMain/kotlin/android/graphics/Paint.kt
+++ b/ui/ui-desktop/android-emu/src/desktopMain/kotlin/android/graphics/Paint.kt
@@ -62,7 +62,7 @@
         const val FILTER_BITMAP_FLAG = 2
     }
 
-    internal val skijaPaint = org.jetbrains.skija.Paint()
+    val skijaPaint = org.jetbrains.skija.Paint()
 
     constructor()
 
diff --git a/ui/ui-desktop/android-emu/src/desktopMain/kotlin/android/graphics/PathEffect.kt b/ui/ui-desktop/android-emu/src/desktopMain/kotlin/android/graphics/PathEffect.kt
index 9d0fabb..4e762e8 100644
--- a/ui/ui-desktop/android-emu/src/desktopMain/kotlin/android/graphics/PathEffect.kt
+++ b/ui/ui-desktop/android-emu/src/desktopMain/kotlin/android/graphics/PathEffect.kt
@@ -16,7 +16,7 @@
 
 package android.graphics
 
-open class PathEffect(internal val skija: org.jetbrains.skija.PathEffect)
+open class PathEffect(val skija: org.jetbrains.skija.PathEffect)
 
 @Suppress("unused")
 class DashPathEffect(intervals: FloatArray?, phase: Float) : PathEffect(
diff --git a/ui/ui-desktop/android-emu/src/desktopMain/kotlin/android/graphics/Shader.kt b/ui/ui-desktop/android-emu/src/desktopMain/kotlin/android/graphics/Shader.kt
index 14f3ae0..c4a6510 100644
--- a/ui/ui-desktop/android-emu/src/desktopMain/kotlin/android/graphics/Shader.kt
+++ b/ui/ui-desktop/android-emu/src/desktopMain/kotlin/android/graphics/Shader.kt
@@ -19,7 +19,7 @@
 import org.jetbrains.skija.GradientStyle
 
 @Suppress("unused")
-open class Shader(internal val skija: org.jetbrains.skija.Shader? = null) {
+open class Shader(val skija: org.jetbrains.skija.Shader? = null) {
     enum class TileMode(val skija: FilterTileMode) {
         CLAMP(FilterTileMode.CLAMP),
         REPEAT(FilterTileMode.REPEAT),
diff --git a/ui/ui-desktop/android-emu/src/desktopMain/kotlin/android/view/ViewGroup.kt b/ui/ui-desktop/android-emu/src/desktopMain/kotlin/android/view/ViewGroup.kt
index ee493ad..b83384c 100644
--- a/ui/ui-desktop/android-emu/src/desktopMain/kotlin/android/view/ViewGroup.kt
+++ b/ui/ui-desktop/android-emu/src/desktopMain/kotlin/android/view/ViewGroup.kt
@@ -18,9 +18,9 @@
 
 import android.content.Context
 import android.graphics.Canvas
-import android.graphics.Region
 import android.graphics.Outline
 import android.graphics.Rect
+import org.jetbrains.skija.ClipMode
 import org.jetbrains.skija.RRect
 
 abstract class ViewGroup(context: Context) : View(context), ViewParent {
@@ -59,13 +59,13 @@
     override fun requestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {}
 
     fun drawChild(canvas: Canvas, view: View, drawingTime: Long): Boolean {
-        canvas.save()
-        canvas.translate(
+        canvas.skijaCanvas.save()
+        canvas.skijaCanvas.translate(
             view.left.toFloat() + view.translationX,
             view.top.toFloat() + view.translationY
         )
         if (view.clipToBounds && view.clipBounds != null) {
-            canvas.clipRect(view.clipBounds!!)
+            canvas.skijaCanvas.clipRect(view.clipBounds!!.toSkija(), ClipMode.INTERSECT)
         }
         if (view.clipToOutline) {
             val outline = Outline()
@@ -73,32 +73,29 @@
             canvas.clipOutline(outline)
         }
         if (view.scaleX != 1f || view.scaleY != 1f) {
-            canvas.scale(view.scaleX, view.scaleY)
+            canvas.skijaCanvas.scale(view.scaleX, view.scaleY)
         }
 
         view.dispatchDraw(canvas)
-        canvas.restore()
+        canvas.skijaCanvas.restore()
         return true
     }
 
-    private fun Canvas.clipRect(rect: Rect) {
-        clipRect(
-            rect.left.toFloat(),
-            rect.top.toFloat(),
-            rect.right.toFloat(),
-            rect.bottom.toFloat(),
-            Region.Op.INTERSECT
-        )
-    }
+    private fun Rect.toSkija() = org.jetbrains.skija.Rect.makeLTRB(
+        left.toFloat(),
+        top.toFloat(),
+        right.toFloat(),
+        bottom.toFloat()
+    )
 
     private fun Canvas.clipOutline(outline: Outline) {
         val rect = outline.rect
         val radius = outline.radius
         val path = outline.path
         if (path != null) {
-            clipPath(path, Region.Op.INTERSECT)
+            skijaCanvas.clipPath(path.skijaPath, ClipMode.INTERSECT)
         } else if (radius != null && rect != null) {
-            skijaCanvas!!.clipRRect(
+            skijaCanvas.clipRRect(
                 RRect.makeLTRB(
                     rect.left.toFloat(),
                     rect.top.toFloat(),
@@ -109,7 +106,7 @@
                 )
             )
         } else if (rect != null) {
-            clipRect(rect)
+            skijaCanvas.clipRect(rect.toSkija())
         }
     }
 
diff --git a/ui/ui-desktop/build.gradle b/ui/ui-desktop/build.gradle
index 0b3d087..6c6e3a0 100644
--- a/ui/ui-desktop/build.gradle
+++ b/ui/ui-desktop/build.gradle
@@ -109,7 +109,7 @@
             api(KOTLIN_STDLIB_JDK8)
             api(KOTLIN_COROUTINES_CORE)
 
-            api "org.jetbrains.skija:skija:0.4.1"
+            api(SKIJA)
 
             api project(":compose:desktop:desktop:android-emu")
 
diff --git a/ui/ui-desktop/src/jvmMain/kotlin/androidx/ui/desktop/ComposeInit.kt b/ui/ui-desktop/src/jvmMain/kotlin/androidx/ui/desktop/ComposeInit.kt
new file mode 100644
index 0000000..6682a79
--- /dev/null
+++ b/ui/ui-desktop/src/jvmMain/kotlin/androidx/ui/desktop/ComposeInit.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("DEPRECATION_ERROR")
+
+package androidx.ui.desktop
+
+import androidx.compose.InternalComposeApi
+import androidx.ui.graphics.DesktopCanvas
+import androidx.ui.graphics.DesktopImageShader
+import androidx.ui.graphics.DesktopInternalCanvasHolder
+import androidx.ui.graphics.DesktopLinearGradientShader
+import androidx.ui.graphics.DesktopPaint
+import androidx.ui.graphics.DesktopPath
+import androidx.ui.graphics.DesktopRadialGradientShader
+import androidx.ui.graphics.GraphicsFactory
+import androidx.ui.text.platform.paragraphActualFactory
+import androidx.ui.text.platform.paragraphIntrinsicsActualFactory
+import org.jetbrains.skija.Library
+
+/**
+ * Can be called multiple times.
+ *
+ * Initialization will occur only on the first call. The next calls will do nothing.
+ *
+ * Should be called in a class that uses Jetpack Compose Api:
+ *
+ * class SomeClass {
+ *     companion object {
+ *         init {
+ *             initCompose()
+ *         }
+ *     }
+ * }
+ */
+fun initCompose() {
+    // call object initializer only once
+    ComposeInit
+}
+
+@OptIn(androidx.ui.text.platform.InternalPlatformTextApi::class, InternalComposeApi::class)
+private object ComposeInit {
+    init {
+        Library.load("/", "skija")
+        // Until https://github.com/Kotlin/kotlinx.coroutines/issues/2039 is resolved
+        // we have to set this property manually for coroutines to work.
+        System.getProperties().setProperty("kotlinx.coroutines.fast.service.loader", "false")
+
+        GraphicsFactory.nativeCanvas = ::DesktopCanvas
+        GraphicsFactory.imageCanvas = ::DesktopCanvas
+        GraphicsFactory.canvasHolder = ::DesktopInternalCanvasHolder
+        GraphicsFactory.paint = ::DesktopPaint
+        GraphicsFactory.path = { DesktopPath() }
+        GraphicsFactory.Shader.linear = ::DesktopLinearGradientShader
+        GraphicsFactory.Shader.radial = ::DesktopRadialGradientShader
+        GraphicsFactory.Shader.image = ::DesktopImageShader
+        paragraphIntrinsicsActualFactory = ::DesktopParagraphIntrinsics
+        paragraphActualFactory = ::DesktopParagraph
+    }
+}
\ No newline at end of file
diff --git a/ui/ui-desktop/src/jvmMain/kotlin/androidx/ui/desktop/DesktopParagraph.kt b/ui/ui-desktop/src/jvmMain/kotlin/androidx/ui/desktop/DesktopParagraph.kt
index d8c692a..2bb81f1 100644
--- a/ui/ui-desktop/src/jvmMain/kotlin/androidx/ui/desktop/DesktopParagraph.kt
+++ b/ui/ui-desktop/src/jvmMain/kotlin/androidx/ui/desktop/DesktopParagraph.kt
@@ -15,16 +15,16 @@
  */
 package androidx.ui.desktop
 
+import androidx.ui.geometry.Offset
 import androidx.ui.geometry.Rect
 import androidx.ui.graphics.Canvas
+import androidx.ui.graphics.DesktopPath
 import androidx.ui.graphics.Path
-import androidx.ui.graphics.AndroidPath
 import androidx.ui.text.Paragraph
 import androidx.ui.text.ParagraphConstraints
 import androidx.ui.text.ParagraphIntrinsics
 import androidx.ui.text.TextRange
 import androidx.ui.text.style.ResolvedTextDirection
-import androidx.ui.geometry.Offset
 import org.jetbrains.skija.paragraph.RectHeightMode
 import org.jetbrains.skija.paragraph.RectWidthMode
 
@@ -80,9 +80,9 @@
             RectHeightMode.MAX,
             RectWidthMode.MAX
         )
-        val path = Path() as AndroidPath
+        val path = DesktopPath()
         for (b in boxes) {
-            path.internalPath.skijaPath.addRect(b.rect)
+            path.internalPath.addRect(b.rect)
         }
         return path
     }
diff --git a/ui/ui-desktop/src/jvmMain/kotlin/androidx/ui/desktop/SkiaWindow.kt b/ui/ui-desktop/src/jvmMain/kotlin/androidx/ui/desktop/SkiaWindow.kt
index 92be182..8a795e2 100644
--- a/ui/ui-desktop/src/jvmMain/kotlin/androidx/ui/desktop/SkiaWindow.kt
+++ b/ui/ui-desktop/src/jvmMain/kotlin/androidx/ui/desktop/SkiaWindow.kt
@@ -24,14 +24,6 @@
 import com.jogamp.opengl.GLProfile
 import com.jogamp.opengl.awt.GLCanvas
 import com.jogamp.opengl.util.FPSAnimator
-import java.awt.event.KeyAdapter
-import java.awt.event.KeyEvent
-import java.awt.event.MouseAdapter
-import java.awt.event.MouseEvent
-import java.awt.event.MouseMotionAdapter
-import java.nio.IntBuffer
-import javax.swing.JDialog
-import javax.swing.JFrame
 import org.jetbrains.skija.BackendRenderTarget
 import org.jetbrains.skija.Canvas
 import org.jetbrains.skija.ColorSpace
@@ -41,6 +33,14 @@
 import org.jetbrains.skija.Surface
 import org.jetbrains.skija.SurfaceColorFormat
 import org.jetbrains.skija.SurfaceOrigin
+import java.awt.event.KeyAdapter
+import java.awt.event.KeyEvent
+import java.awt.event.MouseAdapter
+import java.awt.event.MouseEvent
+import java.awt.event.MouseMotionAdapter
+import java.nio.IntBuffer
+import javax.swing.JDialog
+import javax.swing.JFrame
 
 private class SkijaState {
     var context: Context? = null
@@ -77,18 +77,9 @@
 }
 
 class Window : JFrame, SkiaFrame {
-    @OptIn(androidx.ui.text.platform.InternalPlatformTextApi::class)
     companion object {
         init {
-            Library.load("/", "skija")
-            // Until https://github.com/Kotlin/kotlinx.coroutines/issues/2039 is resolved
-            // we have to set this property manually for coroutines to work.
-            System.getProperties().setProperty("kotlinx.coroutines.fast.service.loader", "false")
-
-            @Suppress("DEPRECATION_ERROR")
-            paragraphIntrinsicsActualFactory = ::DesktopParagraphIntrinsics
-            @Suppress("DEPRECATION_ERROR")
-            paragraphActualFactory = ::DesktopParagraph
+            initCompose()
         }
     }
 
@@ -228,9 +219,11 @@
         override fun mouseClicked(event: MouseEvent) {
             frame.renderer!!.onMouseClicked(event.x, event.y, event.getModifiersEx())
         }
+
         override fun mousePressed(event: MouseEvent) {
             frame.renderer!!.onMousePressed(event.x, event.y, event.getModifiersEx())
         }
+
         override fun mouseReleased(event: MouseEvent) {
             frame.renderer!!.onMouseReleased(event.x, event.y, event.getModifiersEx())
         }
@@ -244,9 +237,11 @@
         override fun keyPressed(event: KeyEvent) {
             frame.renderer!!.onKeyPressed(event.keyCode, event.keyChar)
         }
+
         override fun keyReleased(event: KeyEvent) {
             frame.renderer!!.onKeyReleased(event.keyCode, event.keyChar)
         }
+
         override fun keyTyped(event: KeyEvent) {
             frame.renderer!!.onKeyTyped(event.keyChar)
         }
@@ -262,15 +257,18 @@
             initSkija(glCanvas, skijaState, vsync, false)
             frame.renderer!!.onReshape(skijaState.canvas!!, width, height)
         }
+
         override fun init(drawable: GLAutoDrawable?) {
             skijaState.context = Context.makeGL()
             initSkija(glCanvas, skijaState, vsync, false)
             frame.renderer!!.onInit()
         }
+
         override fun dispose(drawable: GLAutoDrawable?) {
             frame.renderer!!.onDispose()
             AppManager.removeWindow(frame.parent)
         }
+
         override fun display(drawable: GLAutoDrawable?) {
             skijaState.apply {
                 val gl = drawable!!.gl!!
diff --git a/ui/ui-desktop/src/jvmMain/kotlin/androidx/ui/desktop/test/TestSkiaWindow.kt b/ui/ui-desktop/src/jvmMain/kotlin/androidx/ui/desktop/test/TestSkiaWindow.kt
index 3ec4992..fb1502e 100644
--- a/ui/ui-desktop/src/jvmMain/kotlin/androidx/ui/desktop/test/TestSkiaWindow.kt
+++ b/ui/ui-desktop/src/jvmMain/kotlin/androidx/ui/desktop/test/TestSkiaWindow.kt
@@ -15,19 +15,10 @@
  */
 package androidx.ui.desktop.test
 
-import androidx.animation.rootAnimationClockFactory
-import androidx.animation.ManualAnimationClock
 import android.content.Context
 import android.view.ViewGroup
-import androidx.ui.text.platform.paragraphActualFactory
-import androidx.ui.text.platform.paragraphIntrinsicsActualFactory
-import org.jetbrains.skija.Canvas
-import org.jetbrains.skija.Library
-import org.jetbrains.skija.Surface
-import androidx.ui.desktop.DesktopPlatformInput
-import androidx.ui.desktop.DesktopParagraph
-import androidx.ui.desktop.DesktopParagraphIntrinsics
-import androidx.ui.desktop.FontLoader
+import androidx.animation.ManualAnimationClock
+import androidx.animation.rootAnimationClockFactory
 import androidx.compose.Composable
 import androidx.compose.Providers
 import androidx.compose.Recomposer
@@ -37,10 +28,15 @@
 import androidx.lifecycle.ViewModelStoreOwner
 import androidx.lifecycle.ViewTreeLifecycleOwner
 import androidx.lifecycle.ViewTreeViewModelStoreOwner
-import androidx.ui.core.setContent
-import androidx.ui.core.TextInputServiceAmbient
 import androidx.ui.core.FontLoaderAmbient
+import androidx.ui.core.TextInputServiceAmbient
+import androidx.ui.core.setContent
+import androidx.ui.desktop.DesktopPlatformInput
+import androidx.ui.desktop.FontLoader
+import androidx.ui.desktop.initCompose
 import androidx.ui.input.TextInputService
+import org.jetbrains.skija.Canvas
+import org.jetbrains.skija.Surface
 
 class TestSkiaWindow(
     val width: Int,
@@ -58,15 +54,7 @@
     @OptIn(androidx.ui.text.platform.InternalPlatformTextApi::class)
     companion object {
         init {
-            Library.load("/", "skija")
-            // Until https://github.com/Kotlin/kotlinx.coroutines/issues/2039 is resolved
-            // we have to set this property manually for coroutines to work.
-            System.getProperties().setProperty("kotlinx.coroutines.fast.service.loader", "false")
-
-            @Suppress("DEPRECATION_ERROR")
-            paragraphIntrinsicsActualFactory = ::DesktopParagraphIntrinsics
-            @Suppress("DEPRECATION_ERROR")
-            paragraphActualFactory = ::DesktopParagraph
+            initCompose()
         }
     }
 
diff --git a/ui/ui-desktop/src/jvmMain/kotlin/androidx/ui/graphics/DesktopCanvas.kt b/ui/ui-desktop/src/jvmMain/kotlin/androidx/ui/graphics/DesktopCanvas.kt
new file mode 100644
index 0000000..010b8f5
--- /dev/null
+++ b/ui/ui-desktop/src/jvmMain/kotlin/androidx/ui/graphics/DesktopCanvas.kt
@@ -0,0 +1,357 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.graphics
+
+import androidx.compose.InternalComposeApi
+import androidx.ui.geometry.Offset
+import androidx.ui.geometry.Rect
+import androidx.ui.graphics.vectormath.Matrix4
+import androidx.ui.graphics.vectormath.isIdentity
+import androidx.ui.unit.IntOffset
+import androidx.ui.unit.IntSize
+import androidx.ui.util.fastForEach
+import org.jetbrains.skija.ClipMode as SkijaClipMode
+import org.jetbrains.skija.IRect as SkijaIRect
+import org.jetbrains.skija.Matrix33 as SkijaMatrix33
+import org.jetbrains.skija.RRect as SkijaRRect
+import org.jetbrains.skija.Rect as SkijaRect
+
+@Suppress("UNUSED_PARAMETER")
+fun DesktopCanvas(image: ImageAsset): Canvas {
+    println("Canvas(image: ImageAsset) isn't implemented yet")
+    return DesktopCanvas(android.graphics.Canvas())
+}
+
+@OptIn(InternalComposeApi::class)
+internal class DesktopInternalCanvasHolder : InternalCanvasHolder {
+    private val _canvas = DesktopCanvas(android.graphics.Canvas())
+    override val canvas: Canvas get() = _canvas
+
+    override var internalCanvas: NativeCanvas
+        get() = _canvas.nativeCanvas
+        set(value) {
+            _canvas.nativeCanvas = value
+        }
+}
+
+class DesktopCanvas(override var nativeCanvas: NativeCanvas) : Canvas {
+    private val skija get() = nativeCanvas.skijaCanvas
+    private val Paint.skija get() = asFrameworkPaint().skijaPaint
+
+    override fun save() {
+        skija.save()
+    }
+
+    override fun restore() {
+        skija.restore()
+    }
+
+    override fun saveLayer(bounds: Rect, paint: Paint) {
+        skija.saveLayer(
+            bounds.left,
+            bounds.top,
+            bounds.right,
+            bounds.bottom,
+            paint.skija
+        )
+    }
+
+    override fun translate(dx: Float, dy: Float) {
+        skija.translate(dx, dy)
+    }
+
+    override fun scale(sx: Float, sy: Float) {
+        skija.scale(sx, sy)
+    }
+
+    override fun rotate(degrees: Float) {
+        skija.rotate(degrees)
+    }
+
+    override fun skew(sx: Float, sy: Float) {
+        skija.skew(sx, sy)
+    }
+
+    /**
+     * @throws IllegalStateException if an arbitrary transform is provided
+     */
+    override fun concat(matrix4: Matrix4) {
+        if (!matrix4.isIdentity()) {
+            if (matrix4[2, 0] != 0f ||
+                matrix4[2, 1] != 0f ||
+                matrix4[2, 0] != 0f ||
+                matrix4[2, 1] != 0f ||
+                matrix4[2, 2] != 1f ||
+                matrix4[2, 3] != 0f ||
+                matrix4[3, 2] != 0f) {
+                throw IllegalStateException("Desktop does not support arbitrary transforms")
+            }
+            skija.concat(
+                SkijaMatrix33(
+                    matrix4[0, 0],
+                    matrix4[1, 0],
+                    matrix4[3, 0],
+                    matrix4[0, 1],
+                    matrix4[1, 1],
+                    matrix4[3, 1],
+                    matrix4[0, 3],
+                    matrix4[1, 3],
+                    matrix4[3, 3]
+                )
+            )
+        }
+    }
+
+    override fun clipRect(left: Float, top: Float, right: Float, bottom: Float, clipOp: ClipOp) {
+        skija.clipRect(SkijaRect.makeLTRB(left, top, right, bottom), clipOp.toSkija())
+    }
+
+    override fun clipPath(path: Path, clipOp: ClipOp) {
+        skija.clipPath(path.asDesktopPath(), clipOp.toSkija())
+    }
+
+    override fun drawLine(p1: Offset, p2: Offset, paint: Paint) {
+        skija.drawLine(p1.x, p1.y, p2.x, p2.y, paint.skija)
+    }
+
+    override fun drawRect(left: Float, top: Float, right: Float, bottom: Float, paint: Paint) {
+        skija.drawRect(SkijaRect.makeLTRB(left, top, right, bottom), paint.skija)
+    }
+
+    override fun drawRoundRect(
+        left: Float,
+        top: Float,
+        right: Float,
+        bottom: Float,
+        radiusX: Float,
+        radiusY: Float,
+        paint: Paint
+    ) {
+        skija.drawRRect(
+            SkijaRRect.makeLTRB(
+                left,
+                top,
+                right,
+                bottom,
+                radiusX,
+                radiusY
+            ),
+            paint.skija
+        )
+    }
+
+    override fun drawOval(left: Float, top: Float, right: Float, bottom: Float, paint: Paint) {
+        skija.drawOval(SkijaRect.makeLTRB(left, top, right, bottom), paint.skija)
+    }
+
+    override fun drawCircle(center: Offset, radius: Float, paint: Paint) {
+        skija.drawCircle(center.x, center.y, radius, paint.skija)
+    }
+
+    override fun drawArc(
+        left: Float,
+        top: Float,
+        right: Float,
+        bottom: Float,
+        startAngle: Float,
+        sweepAngle: Float,
+        useCenter: Boolean,
+        paint: Paint
+    ) {
+        skija.drawArc(
+            left,
+            top,
+            right,
+            bottom,
+            startAngle,
+            sweepAngle,
+            useCenter,
+            paint.skija
+        )
+    }
+
+    override fun drawPath(path: Path, paint: Paint) {
+        skija.drawPath(path.asDesktopPath(), paint.skija)
+    }
+
+    override fun drawImage(image: ImageAsset, topLeftOffset: Offset, paint: Paint) {
+        skija.drawImageRect(
+            image.asAndroidBitmap().skiaImage,
+            SkijaIRect.makeXYWH(
+                0,
+                0,
+                image.width,
+                image.height
+            ),
+            SkijaRect.makeXYWH(
+                topLeftOffset.x,
+                topLeftOffset.y,
+                image.width.toFloat(),
+                image.height.toFloat()
+            ),
+            paint.skija
+        )
+    }
+
+    override fun drawImageRect(
+        image: ImageAsset,
+        srcOffset: IntOffset,
+        srcSize: IntSize,
+        dstOffset: IntOffset,
+        dstSize: IntSize,
+        paint: Paint
+    ) {
+        skija.drawImageRect(
+            image.asAndroidBitmap().skiaImage,
+            SkijaIRect.makeXYWH(
+                srcOffset.x,
+                srcOffset.y,
+                srcSize.width,
+                srcSize.height
+            ),
+            SkijaRect.makeXYWH(
+                dstOffset.x.toFloat(),
+                dstOffset.y.toFloat(),
+                dstSize.width.toFloat(),
+                dstSize.height.toFloat()
+            ),
+            paint.skija
+        )
+    }
+
+    override fun drawPoints(pointMode: PointMode, points: List<Offset>, paint: Paint) {
+        when (pointMode) {
+            // Draw a line between each pair of points, each point has at most one line
+            // If the number of points is odd, then the last point is ignored.
+            PointMode.lines -> drawLines(points, paint, 2)
+
+            // Connect each adjacent point with a line
+            PointMode.polygon -> drawLines(points, paint, 1)
+
+            // Draw a point at each provided coordinate
+            PointMode.points -> drawPoints(points, paint)
+        }
+    }
+
+    override fun enableZ() = Unit
+
+    override fun disableZ() = Unit
+
+    private fun drawPoints(points: List<Offset>, paint: Paint) {
+        points.fastForEach { point ->
+            skija.drawPoint(
+                point.x,
+                point.y,
+                paint.skija
+            )
+        }
+    }
+
+    /**
+     * Draw lines connecting points based on the corresponding step.
+     *
+     * ex. 3 points with a step of 1 would draw 2 lines between the first and second points
+     * and another between the second and third
+     *
+     * ex. 4 points with a step of 2 would draw 2 lines between the first and second and another
+     * between the third and fourth. If there is an odd number of points, the last point is
+     * ignored
+     *
+     * @see drawRawLines
+     */
+    private fun drawLines(points: List<Offset>, paint: Paint, stepBy: Int) {
+        if (points.size >= 2) {
+            for (i in 0 until points.size - 1 step stepBy) {
+                val p1 = points[i]
+                val p2 = points[i + 1]
+                skija.drawLine(
+                    p1.x,
+                    p1.y,
+                    p2.x,
+                    p2.y,
+                    paint.skija
+                )
+            }
+        }
+    }
+
+    /**
+     * @throws IllegalArgumentException if a non even number of points is provided
+     */
+    override fun drawRawPoints(pointMode: PointMode, points: FloatArray, paint: Paint) {
+        if (points.size % 2 != 0) {
+            throw IllegalArgumentException("points must have an even number of values")
+        }
+        when (pointMode) {
+            PointMode.lines -> drawRawLines(points, paint, 2)
+            PointMode.polygon -> drawRawLines(points, paint, 1)
+            PointMode.points -> drawRawPoints(points, paint, 2)
+        }
+    }
+
+    private fun drawRawPoints(points: FloatArray, paint: Paint, stepBy: Int) {
+        if (points.size % 2 == 0) {
+            for (i in 0 until points.size - 1 step stepBy) {
+                val x = points[i]
+                val y = points[i + 1]
+                skija.drawPoint(x, y, paint.skija)
+            }
+        }
+    }
+
+    /**
+     * Draw lines connecting points based on the corresponding step. The points are interpreted
+     * as x, y coordinate pairs in alternating index positions
+     *
+     * ex. 3 points with a step of 1 would draw 2 lines between the first and second points
+     * and another between the second and third
+     *
+     * ex. 4 points with a step of 2 would draw 2 lines between the first and second and another
+     * between the third and fourth. If there is an odd number of points, the last point is
+     * ignored
+     *
+     * @see drawLines
+     */
+    private fun drawRawLines(points: FloatArray, paint: Paint, stepBy: Int) {
+        // Float array is treated as alternative set of x and y coordinates
+        // x1, y1, x2, y2, x3, y3, ... etc.
+        if (points.size >= 4 && points.size % 2 == 0) {
+            for (i in 0 until points.size - 3 step stepBy * 2) {
+                val x1 = points[i]
+                val y1 = points[i + 1]
+                val x2 = points[i + 2]
+                val y2 = points[i + 3]
+                skija.drawLine(
+                    x1,
+                    y1,
+                    x2,
+                    y2,
+                    paint.skija
+                )
+            }
+        }
+    }
+
+    override fun drawVertices(vertices: Vertices, blendMode: BlendMode, paint: Paint) {
+        println("Canvas.drawVertices not implemented yet")
+    }
+
+    private fun ClipOp.toSkija() = when (this) {
+        ClipOp.difference -> SkijaClipMode.DIFFERENCE
+        ClipOp.intersect -> SkijaClipMode.INTERSECT
+    }
+}
\ No newline at end of file
diff --git a/ui/ui-desktop/src/jvmMain/kotlin/androidx/ui/graphics/DesktopPaint.kt b/ui/ui-desktop/src/jvmMain/kotlin/androidx/ui/graphics/DesktopPaint.kt
new file mode 100644
index 0000000..aef5e46
--- /dev/null
+++ b/ui/ui-desktop/src/jvmMain/kotlin/androidx/ui/graphics/DesktopPaint.kt
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.graphics
+
+import kotlin.math.round
+import org.jetbrains.skija.BlendMode as SkijaBlendMode
+import org.jetbrains.skija.ColorFilter as SkijaColorFilter
+import org.jetbrains.skija.FilterQuality as SkijaFilterQuality
+import org.jetbrains.skija.PaintMode as SkijaPaintMode
+import org.jetbrains.skija.PaintStrokeCap as SkijaPaintStrokeCap
+import org.jetbrains.skija.PaintStrokeJoin as SkijaPaintStrokeJoin
+
+class DesktopPaint : Paint {
+    private var nativePaint = android.graphics.Paint()
+    private val skija get() = nativePaint.skijaPaint
+
+    override fun asFrameworkPaint(): NativePaint = nativePaint
+
+    override var alpha: Float
+        get() = alphaInt / 255f
+        set(value) {
+            alphaInt = round(value * 255.0f).toInt()
+        }
+
+    private var alphaInt: Int
+        get() = (skija.color shr 24) and 0xff
+        set(value) {
+            skija.color = (value shl 24) or (skija.color and 0x00ffffff)
+        }
+
+    override var isAntiAlias: Boolean
+        get() = skija.isAntiAlias
+        set(value) {
+            skija.isAntiAlias = value
+        }
+
+    override var color: Color
+        get() = Color(skija.color)
+        set(color) {
+            skija.color = color.toArgb()
+        }
+
+    override var blendMode: BlendMode = BlendMode.srcOver
+        set(value) {
+            skija.blendMode = value.toSkija()
+            field = value
+        }
+
+    override var style: PaintingStyle = PaintingStyle.fill
+        set(value) {
+            skija.mode = value.toSkija()
+            field = value
+        }
+
+    override var strokeWidth: Float
+        get() = skija.strokeWidth
+        set(value) {
+            skija.strokeWidth = value
+        }
+
+    override var strokeCap: StrokeCap = StrokeCap.butt
+        set(value) {
+            skija.strokeCap = value.toSkija()
+            field = value
+        }
+
+    override var strokeJoin: StrokeJoin = StrokeJoin.round
+        set(value) {
+            skija.strokeJoin = value.toSkija()
+            field = value
+        }
+
+    override var strokeMiterLimit: Float = 0f
+        set(value) {
+            skija.strokeMiter = value
+            field = value
+        }
+
+    override var filterQuality: FilterQuality = FilterQuality.none
+        set(value) {
+            skija.filterQuality = value.toSkija()
+            field = value
+        }
+
+    override var shader: Shader? = null
+        set(value) {
+            skija.shader = value?.skija
+            field = value
+        }
+
+    override var colorFilter: ColorFilter? = null
+        set(value) {
+            skija.colorFilter = if (value != null) {
+                SkijaColorFilter.makeBlend(
+                    value.color.toArgb(),
+                    value.blendMode.toSkija()
+                )
+            } else {
+                null
+            }
+            field = value
+        }
+
+    override var nativePathEffect: NativePathEffect? = null
+        set(value) {
+            skija.pathEffect = value?.skija
+            field = value
+        }
+
+    private fun BlendMode.toSkija() = when (this) {
+        BlendMode.clear -> SkijaBlendMode.CLEAR
+        BlendMode.src -> SkijaBlendMode.SRC
+        BlendMode.dst -> SkijaBlendMode.DST
+        BlendMode.srcOver -> SkijaBlendMode.SRC_OVER
+        BlendMode.dstOver -> SkijaBlendMode.DST_OVER
+        BlendMode.srcIn -> SkijaBlendMode.SRC_IN
+        BlendMode.dstIn -> SkijaBlendMode.DST_IN
+        BlendMode.srcOut -> SkijaBlendMode.SRC_OUT
+        BlendMode.dstOut -> SkijaBlendMode.DST_OUT
+        BlendMode.srcATop -> SkijaBlendMode.SRC_ATOP
+        BlendMode.dstATop -> SkijaBlendMode.DST_ATOP
+        BlendMode.xor -> SkijaBlendMode.XOR
+        BlendMode.plus -> SkijaBlendMode.PLUS
+        BlendMode.modulate -> SkijaBlendMode.MODULATE
+        BlendMode.screen -> SkijaBlendMode.SCREEN
+        BlendMode.overlay -> SkijaBlendMode.OVERLAY
+        BlendMode.darken -> SkijaBlendMode.DARKEN
+        BlendMode.lighten -> SkijaBlendMode.LIGHTEN
+        BlendMode.colorDodge -> SkijaBlendMode.COLOR_DODGE
+        BlendMode.colorBurn -> SkijaBlendMode.COLOR_BURN
+        BlendMode.hardLight -> SkijaBlendMode.HARD_LIGHT
+        BlendMode.softLight -> SkijaBlendMode.SOFT_LIGHT
+        BlendMode.difference -> SkijaBlendMode.DIFFERENCE
+        BlendMode.exclusion -> SkijaBlendMode.EXCLUSION
+        BlendMode.multiply -> SkijaBlendMode.MULTIPLY
+        BlendMode.hue -> SkijaBlendMode.HUE
+        BlendMode.saturation -> SkijaBlendMode.SATURATION
+        BlendMode.color -> SkijaBlendMode.COLOR
+        BlendMode.luminosity -> SkijaBlendMode.LUMINOSITY
+    }
+
+    private fun PaintingStyle.toSkija() = when (this) {
+        PaintingStyle.fill -> SkijaPaintMode.FILL
+        PaintingStyle.stroke -> SkijaPaintMode.STROKE
+    }
+
+    private fun StrokeCap.toSkija() = when (this) {
+        StrokeCap.butt -> SkijaPaintStrokeCap.BUTT
+        StrokeCap.round -> SkijaPaintStrokeCap.ROUND
+        StrokeCap.square -> SkijaPaintStrokeCap.SQUARE
+    }
+
+    private fun StrokeJoin.toSkija() = when (this) {
+        StrokeJoin.miter -> SkijaPaintStrokeJoin.MITER
+        StrokeJoin.round -> SkijaPaintStrokeJoin.ROUND
+        StrokeJoin.bevel -> SkijaPaintStrokeJoin.BEVEL
+    }
+
+    private fun FilterQuality.toSkija() = when (this) {
+        FilterQuality.none -> SkijaFilterQuality.NONE
+        FilterQuality.low -> SkijaFilterQuality.LOW
+        FilterQuality.medium -> SkijaFilterQuality.MEDIUM
+        FilterQuality.high -> SkijaFilterQuality.HIGH
+    }
+}
\ No newline at end of file
diff --git a/ui/ui-desktop/src/jvmMain/kotlin/androidx/ui/graphics/DesktopPath.kt b/ui/ui-desktop/src/jvmMain/kotlin/androidx/ui/graphics/DesktopPath.kt
new file mode 100644
index 0000000..a05fd78
--- /dev/null
+++ b/ui/ui-desktop/src/jvmMain/kotlin/androidx/ui/graphics/DesktopPath.kt
@@ -0,0 +1,197 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.graphics
+
+import androidx.ui.geometry.Offset
+import androidx.ui.geometry.RRect
+import androidx.ui.geometry.Rect
+import androidx.ui.graphics.vectormath.degrees
+import org.jetbrains.skija.Matrix33
+import org.jetbrains.skija.PathDirection
+import org.jetbrains.skija.PathFillMode
+
+/**
+ * @Throws UnsupportedOperationException if this Path is not backed by an org.jetbrains.skija.Path
+ */
+@Suppress("NOTHING_TO_INLINE")
+inline fun Path.asDesktopPath(): org.jetbrains.skija.Path =
+    if (this is DesktopPath) {
+        internalPath
+    } else {
+        throw UnsupportedOperationException("Unable to obtain org.jetbrains.skija.Path")
+    }
+
+class DesktopPath(
+    val internalPath: org.jetbrains.skija.Path = org.jetbrains.skija.Path()
+) : Path {
+    private val radii = FloatArray(8)
+
+    override var fillType: PathFillType
+        get() {
+            if (internalPath.fillMode == PathFillMode.EVEN_ODD) {
+                return PathFillType.evenOdd
+            } else {
+                return PathFillType.nonZero
+            }
+        }
+
+        set(value) {
+            internalPath.fillMode =
+                if (value == PathFillType.evenOdd) {
+                    PathFillMode.EVEN_ODD
+                } else {
+                    PathFillMode.WINDING
+                }
+        }
+
+    override fun moveTo(x: Float, y: Float) {
+        internalPath.moveTo(x, y)
+    }
+
+    override fun relativeMoveTo(dx: Float, dy: Float) {
+        internalPath.rMoveTo(dx, dy)
+    }
+
+    override fun lineTo(x: Float, y: Float) {
+        internalPath.lineTo(x, y)
+    }
+
+    override fun relativeLineTo(dx: Float, dy: Float) {
+        internalPath.rLineTo(dx, dy)
+    }
+
+    override fun quadraticBezierTo(x1: Float, y1: Float, x2: Float, y2: Float) {
+        internalPath.quadTo(x1, y1, x2, y2)
+    }
+
+    override fun relativeQuadraticBezierTo(dx1: Float, dy1: Float, dx2: Float, dy2: Float) {
+        internalPath.rQuadTo(dx1, dy1, dx2, dy2)
+    }
+
+    override fun cubicTo(x1: Float, y1: Float, x2: Float, y2: Float, x3: Float, y3: Float) {
+        internalPath.cubicTo(
+            x1, y1,
+            x2, y2,
+            x3, y3
+        )
+    }
+
+    override fun relativeCubicTo(
+        dx1: Float,
+        dy1: Float,
+        dx2: Float,
+        dy2: Float,
+        dx3: Float,
+        dy3: Float
+    ) {
+        internalPath.rCubicTo(
+            dx1, dy1,
+            dx2, dy2,
+            dx3, dy3
+        )
+    }
+
+    override fun arcTo(
+        rect: Rect,
+        startAngleDegrees: Float,
+        sweepAngleDegrees: Float,
+        forceMoveTo: Boolean
+    ) {
+        internalPath.arcTo(
+            rect.toSkija(),
+            startAngleDegrees,
+            sweepAngleDegrees,
+            forceMoveTo
+        )
+    }
+
+    override fun addRect(rect: Rect) {
+        internalPath.addRect(rect.toSkija(), PathDirection.COUNTER_CLOCKWISE)
+    }
+
+    override fun addOval(oval: Rect) {
+        internalPath.addOval(oval.toSkija(), PathDirection.COUNTER_CLOCKWISE)
+    }
+
+    override fun addArcRad(oval: Rect, startAngleRadians: Float, sweepAngleRadians: Float) {
+        addArc(oval, degrees(startAngleRadians), degrees(sweepAngleRadians))
+    }
+
+    override fun addArc(oval: Rect, startAngleDegrees: Float, sweepAngleDegrees: Float) {
+        internalPath.addArc(oval.toSkija(), startAngleDegrees, sweepAngleDegrees)
+    }
+
+    override fun addRRect(rrect: RRect) {
+        radii[0] = rrect.topLeftRadiusX
+        radii[1] = rrect.topLeftRadiusY
+
+        radii[2] = rrect.topRightRadiusX
+        radii[3] = rrect.topRightRadiusY
+
+        radii[4] = rrect.bottomRightRadiusX
+        radii[5] = rrect.bottomRightRadiusY
+
+        radii[6] = rrect.bottomLeftRadiusX
+        radii[7] = rrect.bottomLeftRadiusY
+
+        internalPath.addRRect(
+            org.jetbrains.skija.RRect.makeComplexLTRB(
+                rrect.left, rrect.top, rrect.right, rrect.bottom, radii
+            ),
+            PathDirection.COUNTER_CLOCKWISE
+        )
+    }
+
+    override fun addPath(path: Path, offset: Offset) {
+        internalPath.addPath(path.asDesktopPath(), offset.x, offset.y)
+    }
+
+    override fun close() {
+        internalPath.closePath()
+    }
+
+    override fun reset() {
+        internalPath.reset()
+    }
+
+    override fun shift(offset: Offset) {
+        internalPath.transform(Matrix33.makeTranslate(offset.x, offset.y))
+    }
+
+    override fun getBounds(): Rect {
+        val bounds = internalPath.bounds
+        return Rect(
+            bounds.left,
+            bounds.top,
+            bounds.right,
+            bounds.bottom
+        )
+    }
+
+    override fun op(
+        path1: Path,
+        path2: Path,
+        operation: PathOperation
+    ): Boolean {
+        println("Path.op not implemented yet")
+        return false
+    }
+
+    override val isConvex: Boolean get() = internalPath.isConvex
+
+    override val isEmpty: Boolean get() = internalPath.isEmpty
+}
\ No newline at end of file
diff --git a/ui/ui-desktop/src/jvmMain/kotlin/androidx/ui/graphics/DesktopShader.kt b/ui/ui-desktop/src/jvmMain/kotlin/androidx/ui/graphics/DesktopShader.kt
new file mode 100644
index 0000000..a5e1628
--- /dev/null
+++ b/ui/ui-desktop/src/jvmMain/kotlin/androidx/ui/graphics/DesktopShader.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.graphics
+
+import androidx.ui.geometry.Offset
+import org.jetbrains.skija.FilterTileMode
+import org.jetbrains.skija.GradientStyle
+
+internal fun DesktopLinearGradientShader(
+    from: Offset,
+    to: Offset,
+    colors: List<Color>,
+    colorStops: List<Float>?,
+    tileMode: TileMode
+): Shader {
+    validateColorStops(colors, colorStops)
+    val skijaShader = org.jetbrains.skija.Shader.makeLinearGradient(
+        from.x, from.y, to.x, to.y, colors.toIntArray(), colorStops?.toFloatArray(),
+        GradientStyle(tileMode.toSkija(), true, identityMatrix33())
+    )
+    return Shader(skijaShader)
+}
+
+internal fun DesktopRadialGradientShader(
+    center: Offset,
+    radius: Float,
+    colors: List<Color>,
+    colorStops: List<Float>?,
+    tileMode: TileMode
+): Shader {
+    validateColorStops(colors, colorStops)
+    val skijaShader = org.jetbrains.skija.Shader.makeRadialGradient(
+        center.x,
+        center.y,
+        radius,
+        colors.toIntArray(),
+        colorStops?.toFloatArray(),
+        GradientStyle(tileMode.toSkija(), true, identityMatrix33())
+    )
+    return Shader(skijaShader)
+}
+
+@Suppress("UNUSED_PARAMETER")
+internal fun DesktopImageShader(
+    image: ImageAsset,
+    tileModeX: TileMode,
+    tileModeY: TileMode
+): Shader {
+    println("ImageShader not implemented yet")
+    return Shader()
+}
+
+private fun List<Color>.toIntArray(): IntArray =
+    IntArray(size) { i -> this[i].toArgb() }
+
+private fun validateColorStops(colors: List<Color>, colorStops: List<Float>?) {
+    if (colorStops == null) {
+        if (colors.size < 2) {
+            throw IllegalArgumentException(
+                "colors must have length of at least 2 if colorStops " +
+                        "is omitted."
+            )
+        }
+    } else if (colors.size != colorStops.size) {
+        throw IllegalArgumentException(
+            "colors and colorStops arguments must have" +
+                    " equal length."
+        )
+    }
+}
+
+private fun TileMode.toSkija() = when (this) {
+    TileMode.Clamp -> FilterTileMode.CLAMP
+    TileMode.Repeated -> FilterTileMode.REPEAT
+    TileMode.Mirror -> FilterTileMode.MIRROR
+}
\ No newline at end of file
diff --git a/ui/ui-desktop/src/jvmMain/kotlin/androidx/ui/graphics/Matrices.kt b/ui/ui-desktop/src/jvmMain/kotlin/androidx/ui/graphics/Matrices.kt
new file mode 100644
index 0000000..551dc08
--- /dev/null
+++ b/ui/ui-desktop/src/jvmMain/kotlin/androidx/ui/graphics/Matrices.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.graphics
+
+import org.jetbrains.skija.Matrix33
+
+internal fun identityMatrix33() = Matrix33(
+    1f, 0f, 0f,
+    0f, 1f, 0f,
+    0f, 0f, 1f
+)
\ No newline at end of file
diff --git a/ui/ui-desktop/src/jvmMain/kotlin/androidx/ui/graphics/Rects.kt b/ui/ui-desktop/src/jvmMain/kotlin/androidx/ui/graphics/Rects.kt
new file mode 100644
index 0000000..461cc99
--- /dev/null
+++ b/ui/ui-desktop/src/jvmMain/kotlin/androidx/ui/graphics/Rects.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.ui.graphics
+
+import androidx.ui.geometry.Rect
+
+internal fun Rect.toSkija(): org.jetbrains.skija.Rect {
+    return org.jetbrains.skija.Rect.makeLTRB(
+        left,
+        top,
+        right,
+        bottom
+    )
+}
\ No newline at end of file
diff --git a/ui/ui-desktop/src/jvmMain/kotlin/androidx/ui/view/LayoutScope.kt b/ui/ui-desktop/src/jvmMain/kotlin/androidx/ui/view/LayoutScope.kt
index 56b801f..31a8c1b 100644
--- a/ui/ui-desktop/src/jvmMain/kotlin/androidx/ui/view/LayoutScope.kt
+++ b/ui/ui-desktop/src/jvmMain/kotlin/androidx/ui/view/LayoutScope.kt
@@ -101,14 +101,14 @@
 
     fun draw(canvas: Canvas, width: Int, height: Int) {
         val androidCanvas = android.graphics.Canvas(canvas)
-        androidCanvas.save()
-        androidCanvas.translate(x.toFloat(), y.toFloat())
+        androidCanvas.skijaCanvas.save()
+        androidCanvas.skijaCanvas.translate(x.toFloat(), y.toFloat())
         if (children.isNotEmpty()) {
             val view = getChildAt(0)
             view.onMeasure(width, height)
             view.dispatchDraw(androidCanvas)
         }
-        androidCanvas.restore()
+        androidCanvas.skijaCanvas.restore()
     }
 
     fun dispatchTouchEvent(event: MotionEvent) {
diff --git a/ui/ui-desktop/src/jvmTest/kotlin/androidx/ui/desktop/TestResources.kt b/ui/ui-desktop/src/jvmTest/kotlin/androidx/ui/desktop/TestResources.kt
index 83d58ef..c8c3732 100644
--- a/ui/ui-desktop/src/jvmTest/kotlin/androidx/ui/desktop/TestResources.kt
+++ b/ui/ui-desktop/src/jvmTest/kotlin/androidx/ui/desktop/TestResources.kt
@@ -21,8 +21,7 @@
 import androidx.ui.graphics.asImageAsset
 
 internal object TestResources {
-    fun testImageAsset(): ImageAsset {
-        val path = "androidx/ui/desktop/test.png"
+    fun testImageAsset(path: String = "androidx/ui/desktop/test.png"): ImageAsset {
         return Bitmap(
             javaClass.classLoader.getResource(path)!!
                 .openStream().buffered().readBytes()
diff --git a/ui/ui-desktop/src/jvmTest/kotlin/androidx/ui/graphics/DesktopGraphicsTest.kt b/ui/ui-desktop/src/jvmTest/kotlin/androidx/ui/graphics/DesktopGraphicsTest.kt
new file mode 100644
index 0000000..ad40a34
--- /dev/null
+++ b/ui/ui-desktop/src/jvmTest/kotlin/androidx/ui/graphics/DesktopGraphicsTest.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.graphics
+
+import androidx.ui.desktop.initCompose
+import androidx.ui.desktop.test.DesktopScreenshotTestRule
+import org.jetbrains.skija.Surface
+import org.junit.After
+import org.junit.Rule
+
+abstract class DesktopGraphicsTest {
+    @get:Rule
+    val screenshotRule = DesktopScreenshotTestRule("ui/ui-desktop/graphics")
+
+    private var _surface: Surface? = null
+    protected val surface get() = _surface!!
+
+    protected val redPaint = Paint().apply { color = Color.Red }
+    protected val bluePaint = Paint().apply { color = Color.Blue }
+    protected val greenPaint = Paint().apply { color = Color.Green }
+    protected val cyanPaint = Paint().apply { color = Color.Cyan }
+    protected val magentaPaint = Paint().apply { color = Color.Magenta }
+    protected val yellowPaint = Paint().apply { color = Color.Yellow }
+
+    @Suppress("SameParameterValue")
+    protected fun initCanvas(widthPx: Int, heightPx: Int): Canvas {
+        require(_surface == null)
+        _surface = Surface.makeRasterN32Premul(widthPx, heightPx)
+        return Canvas(android.graphics.Canvas(_surface!!.canvas))
+    }
+
+    @After
+    fun teardown() {
+        _surface?.close()
+    }
+
+    private companion object {
+        init {
+            initCompose()
+        }
+    }
+}
\ No newline at end of file
diff --git a/ui/ui-desktop/src/jvmTest/kotlin/androidx/ui/graphics/DesktopPaintTest.kt b/ui/ui-desktop/src/jvmTest/kotlin/androidx/ui/graphics/DesktopPaintTest.kt
new file mode 100644
index 0000000..475101d
--- /dev/null
+++ b/ui/ui-desktop/src/jvmTest/kotlin/androidx/ui/graphics/DesktopPaintTest.kt
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.graphics
+
+import androidx.ui.desktop.TestResources.testImageAsset
+import androidx.ui.geometry.Offset
+import androidx.ui.unit.IntOffset
+import androidx.ui.unit.IntSize
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+class DesktopPaintTest : DesktopGraphicsTest() {
+    private val canvas: Canvas = initCanvas(widthPx = 16, heightPx = 16)
+
+    @Test
+    fun initialParameters() {
+        val paint = Paint()
+
+        assertEquals(Color.Black, paint.color)
+        assertEquals(1f, paint.alpha)
+        assertEquals(PaintingStyle.fill, paint.style)
+        assertEquals(0f, paint.strokeWidth)
+        assertEquals(StrokeCap.butt, paint.strokeCap)
+        assertEquals(0f, paint.strokeMiterLimit)
+        assertEquals(StrokeJoin.round, paint.strokeJoin)
+        assertEquals(true, paint.isAntiAlias)
+        assertEquals(FilterQuality.none, paint.filterQuality)
+        assertEquals(BlendMode.srcOver, paint.blendMode)
+        assertEquals(null, paint.colorFilter)
+        assertEquals(null, paint.shader)
+    }
+
+    @Test
+    fun blendModePlus() {
+        canvas.drawRect(left = 0f, top = 0f, right = 16f, bottom = 16f, paint = redPaint)
+        canvas.drawRect(left = 4f, top = 4f, right = 12f, bottom = 12f, paint = Paint().apply {
+            color = Color.Blue
+            blendMode = BlendMode.plus
+        })
+
+        screenshotRule.snap(surface)
+    }
+
+    @Test
+    fun blendModeMultiply() {
+        canvas.drawRect(left = 0f, top = 0f, right = 16f, bottom = 16f, paint = redPaint)
+        canvas.drawRect(left = 4f, top = 4f, right = 12f, bottom = 12f, paint = Paint().apply {
+            color = Color.Gray
+            blendMode = BlendMode.multiply
+        })
+
+        screenshotRule.snap(surface)
+    }
+
+    @Test
+    fun colorFilter() {
+        canvas.drawRect(left = 0f, top = 0f, right = 16f, bottom = 16f, paint = redPaint)
+
+        canvas.drawImage(
+            image = testImageAsset("androidx/ui/desktop/test.png"),
+            topLeftOffset = Offset(2f, 4f),
+            paint = Paint().apply {
+                colorFilter = ColorFilter(Color.Blue, BlendMode.plus)
+            }
+        )
+
+        screenshotRule.snap(surface)
+    }
+
+    @Test
+    fun filterQuality() {
+        canvas.drawImageRect(
+            image = testImageAsset("androidx/ui/desktop/test.png"),
+            srcOffset = IntOffset(0, 2),
+            srcSize = IntSize(2, 4),
+            dstOffset = IntOffset(0, 4),
+            dstSize = IntSize(4, 12),
+            paint = redPaint
+        )
+        canvas.drawImageRect(
+            image = testImageAsset("androidx/ui/desktop/test.png"),
+            srcOffset = IntOffset(0, 2),
+            srcSize = IntSize(2, 4),
+            dstOffset = IntOffset(4, 4),
+            dstSize = IntSize(4, 12),
+            paint = redPaint.apply {
+                filterQuality = FilterQuality.low
+            }
+        )
+        canvas.drawImageRect(
+            image = testImageAsset("androidx/ui/desktop/test.png"),
+            srcOffset = IntOffset(0, 2),
+            srcSize = IntSize(2, 4),
+            dstOffset = IntOffset(8, 4),
+            dstSize = IntSize(4, 12),
+            paint = redPaint.apply {
+                filterQuality = FilterQuality.high
+            }
+        )
+
+        screenshotRule.snap(surface)
+    }
+
+    @Test
+    fun linearGradientShader() {
+        canvas.drawRect(left = 0f, top = 0f, right = 16f, bottom = 16f, paint = redPaint)
+
+        canvas.drawRect(left = 2f, top = 2f, right = 14f, bottom = 14f, paint = Paint().apply {
+            shader = LinearGradientShader(
+                from = Offset(0f, 0f),
+                to = Offset(6f, 6f),
+                colors = listOf(Color.Blue, Color.Green),
+                tileMode = TileMode.Mirror
+            )
+        })
+
+        screenshotRule.snap(surface)
+    }
+
+    @Test
+    fun linearGradientShaderWithStops() {
+        canvas.drawRect(left = 0f, top = 0f, right = 16f, bottom = 16f, paint = redPaint)
+
+        canvas.drawRect(left = 1f, top = 2f, right = 14f, bottom = 15f, paint = Paint().apply {
+            shader = LinearGradientShader(
+                from = Offset(0f, 0f),
+                to = Offset(12f, 0f),
+                colorStops = listOf(0f, 0.25f, 1f),
+                colors = listOf(Color.Blue, Color.Green, Color.Yellow),
+                tileMode = TileMode.Mirror
+            )
+        })
+
+        screenshotRule.snap(surface)
+    }
+
+    @Test
+    fun radialGradientShader() {
+        canvas.drawRect(left = 0f, top = 0f, right = 16f, bottom = 16f, paint = redPaint)
+
+        canvas.drawRect(left = 2f, top = 2f, right = 14f, bottom = 14f, paint = Paint().apply {
+            shader = RadialGradientShader(
+                center = Offset(4f, 8f),
+                radius = 8f,
+                colors = listOf(Color.Blue, Color.Green),
+                tileMode = TileMode.Clamp
+            )
+        })
+
+        screenshotRule.snap(surface)
+    }
+}
diff --git a/ui/ui-desktop/src/jvmTest/kotlin/androidx/ui/graphics/DesktopPathTest.kt b/ui/ui-desktop/src/jvmTest/kotlin/androidx/ui/graphics/DesktopPathTest.kt
new file mode 100644
index 0000000..9d50048
--- /dev/null
+++ b/ui/ui-desktop/src/jvmTest/kotlin/androidx/ui/graphics/DesktopPathTest.kt
@@ -0,0 +1,192 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.graphics
+
+import androidx.ui.geometry.RRect
+import androidx.ui.geometry.Rect
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+class DesktopPathTest : DesktopGraphicsTest() {
+    private val canvas: Canvas = initCanvas(widthPx = 16, heightPx = 16)
+
+    @Test
+    fun arc() {
+        val path = Path().apply {
+            addArc(Rect(0f, 0f, 16f, 16f), 0f, 90f)
+            arcTo(Rect(0f, 0f, 16f, 16f), 90f, 90f, true)
+        }
+
+        canvas.drawPath(path, redPaint)
+
+        screenshotRule.snap(surface)
+    }
+
+    @Test
+    fun clipPath() {
+        val path = Path().apply {
+            addOval(Rect(0f, 0f, 16f, 8f))
+        }
+
+        canvas.withSave {
+            canvas.clipPath(path, ClipOp.intersect)
+            canvas.drawRect(0f, 0f, 16f, 16f, redPaint)
+        }
+
+        canvas.withSave {
+            canvas.clipPath(path, ClipOp.difference)
+            canvas.drawRect(0f, 0f, 16f, 16f, bluePaint)
+        }
+
+        screenshotRule.snap(surface)
+    }
+
+    @Test
+    fun bezier() {
+        val path = Path().apply {
+            quadraticBezierTo(0f, 16f, 16f, 16f)
+        }
+
+        canvas.drawPath(path, redPaint)
+
+        screenshotRule.snap(surface)
+    }
+
+    @Test
+    fun cubic() {
+        val path = Path().apply {
+            cubicTo(0f, 12f, 0f, 16f, 16f, 16f)
+        }
+
+        canvas.drawPath(path, redPaint)
+
+        screenshotRule.snap(surface)
+    }
+
+    @Test
+    fun figures() {
+        val path = Path().apply {
+            addOval(Rect(0f, 0f, 8f, 4f))
+            addRect(Rect(12f, 0f, 16f, 8f))
+            addRRect(RRect(0f, 8f, 4f, 16f, 4f, 4f))
+        }
+
+        canvas.drawPath(path, redPaint)
+
+        screenshotRule.snap(surface)
+    }
+
+    @Test
+    fun fillTypeEvenOdd() {
+        val path = Path().apply {
+            fillType = PathFillType.evenOdd
+            addRect(Rect(0f, 0f, 8f, 8f))
+            addRect(Rect(4f, 4f, 12f, 12f))
+        }
+
+        canvas.drawPath(path, redPaint)
+
+        screenshotRule.snap(surface)
+    }
+
+    @Test
+    fun fillTypeNonZero() {
+        val path = Path().apply {
+            addRect(Rect(0f, 0f, 8f, 8f))
+            addRect(Rect(4f, 4f, 12f, 12f))
+        }
+
+        assertEquals(PathFillType.nonZero, path.fillType)
+        canvas.drawPath(path, redPaint)
+
+        screenshotRule.snap(surface)
+    }
+
+    @Test
+    fun linesFill() {
+        val path = Path().apply {
+            moveTo(0f, 0f)
+            lineTo(8f, 8f)
+            lineTo(0f, 8f)
+
+            moveTo(8f, 8f)
+            lineTo(8f, 16f)
+            lineTo(16f, 16f)
+            relativeLineTo(0f, -8f)
+        }
+
+        assertEquals(PaintingStyle.fill, redPaint.style)
+        canvas.drawPath(path, redPaint)
+
+        screenshotRule.snap(surface)
+    }
+
+    @Test
+    fun linesStroke() {
+        val path = Path().apply {
+            moveTo(0f, 0f)
+            lineTo(8f, 8f)
+            lineTo(0f, 8f)
+            close()
+
+            moveTo(8f, 8f)
+            lineTo(8f, 16f)
+            lineTo(16f, 16f)
+            relativeLineTo(0f, -8f)
+        }
+
+        canvas.drawPath(path, redPaint.apply {
+            style = PaintingStyle.stroke
+            strokeWidth = 2f
+        })
+
+        screenshotRule.snap(surface)
+    }
+
+    @Test
+    fun isEmpty() {
+        val path = Path()
+        assertEquals(true, path.isEmpty)
+
+        path.addRect(Rect(0f, 0f, 16f, 16f))
+        assertEquals(false, path.isEmpty)
+    }
+
+    @Test
+    fun isConvex() {
+        val path = Path()
+        assertEquals(true, path.isConvex)
+
+        path.addRect(Rect(0f, 0f, 8f, 8f))
+        assertEquals(true, path.isConvex)
+
+        path.addRect(Rect(8f, 8f, 16f, 16f))
+        assertEquals(false, path.isConvex)
+    }
+
+    @Test
+    fun getBounds() {
+        val path = Path()
+        assertEquals(Rect(0f, 0f, 0f, 0f), path.getBounds())
+
+        path.addRect(Rect(0f, 0f, 8f, 8f))
+        assertEquals(Rect(0f, 0f, 8f, 8f), path.getBounds())
+
+        path.addRect(Rect(8f, 8f, 16f, 16f))
+        assertEquals(Rect(0f, 0f, 16f, 16f), path.getBounds())
+    }
+}
diff --git a/ui/ui-desktop/src/jvmTest/kotlin/androidx/ui/graphics/canvas/DesktopCanvasPointsTest.kt b/ui/ui-desktop/src/jvmTest/kotlin/androidx/ui/graphics/canvas/DesktopCanvasPointsTest.kt
new file mode 100644
index 0000000..27613de
--- /dev/null
+++ b/ui/ui-desktop/src/jvmTest/kotlin/androidx/ui/graphics/canvas/DesktopCanvasPointsTest.kt
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.graphics.canvas
+
+import androidx.ui.geometry.Offset
+import androidx.ui.graphics.Canvas
+import androidx.ui.graphics.Color
+import androidx.ui.graphics.DesktopGraphicsTest
+import androidx.ui.graphics.Paint
+import androidx.ui.graphics.PointMode
+import androidx.ui.graphics.StrokeCap
+import org.junit.Test
+
+class DesktopCanvasPointsTest : DesktopGraphicsTest() {
+    private val canvas: Canvas = initCanvas(widthPx = 16, heightPx = 16)
+
+    @Test
+    fun drawLines() {
+        canvas.drawPoints(
+            pointMode = PointMode.lines,
+            points = listOf(
+                Offset(0f, 8f),
+                Offset(8f, 0f),
+                Offset(8f, 8f),
+                Offset(0f, 8f)
+            ),
+            paint = Paint().apply {
+                color = Color.Red
+                strokeWidth = 2f
+            }
+        )
+
+        canvas.translate(6f, 6f)
+        canvas.drawPoints(
+            pointMode = PointMode.lines,
+            points = listOf(
+                Offset(0f, 8f),
+                Offset(8f, 0f),
+                Offset(8f, 8f),
+                Offset(0f, 8f)
+            ),
+            paint = Paint().apply {
+                color = Color.Green
+                strokeWidth = 2f
+                strokeCap = StrokeCap.round
+            }
+        )
+
+        screenshotRule.snap(surface)
+    }
+
+    @Test
+    fun drawPoints() {
+        canvas.drawPoints(
+            pointMode = PointMode.points,
+            points = listOf(
+                Offset(0f, 2f),
+                Offset(2f, 0f)
+            ),
+            paint = Paint().apply {
+                color = Color.Red
+                strokeWidth = 2f
+                strokeCap = StrokeCap.butt
+            }
+        )
+        canvas.drawRawPoints(
+            pointMode = PointMode.points,
+            points = floatArrayOf(
+                4f, 4f,
+                8f, 8f
+            ),
+            paint = Paint().apply {
+                color = Color.Blue
+                strokeWidth = 4f
+                strokeCap = StrokeCap.round
+            }
+        )
+        canvas.drawPoints(
+            pointMode = PointMode.points,
+            points = listOf(
+                Offset(4f, 0f)
+            ),
+            paint = Paint().apply {
+                color = Color.Green
+                strokeWidth = 2f
+                strokeCap = StrokeCap.square
+            }
+        )
+
+        screenshotRule.snap(surface)
+    }
+
+    @Test
+    fun drawPolygons() {
+        canvas.drawPoints(
+            pointMode = PointMode.polygon,
+            points = listOf(
+                Offset(0f, 8f),
+                Offset(8f, 0f),
+                Offset(8f, 8f),
+                Offset(0f, 8f)
+            ),
+            paint = Paint().apply {
+                color = Color.Red
+                strokeWidth = 2f
+            }
+        )
+
+        canvas.translate(6f, 6f)
+        canvas.drawPoints(
+            pointMode = PointMode.polygon,
+            points = listOf(
+                Offset(0f, 8f),
+                Offset(8f, 0f),
+                Offset(8f, 8f),
+                Offset(0f, 8f)
+            ),
+            paint = Paint().apply {
+                color = Color.Green
+                strokeWidth = 2f
+                strokeCap = StrokeCap.round
+            }
+        )
+
+        screenshotRule.snap(surface)
+    }
+}
diff --git a/ui/ui-desktop/src/jvmTest/kotlin/androidx/ui/graphics/canvas/DesktopCanvasTest.kt b/ui/ui-desktop/src/jvmTest/kotlin/androidx/ui/graphics/canvas/DesktopCanvasTest.kt
new file mode 100644
index 0000000..9b9a20d
--- /dev/null
+++ b/ui/ui-desktop/src/jvmTest/kotlin/androidx/ui/graphics/canvas/DesktopCanvasTest.kt
@@ -0,0 +1,277 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.graphics.canvas
+
+import androidx.ui.desktop.TestResources.testImageAsset
+import androidx.ui.geometry.Offset
+import androidx.ui.geometry.Rect
+import androidx.ui.graphics.BlendMode
+import androidx.ui.graphics.Canvas
+import androidx.ui.graphics.ClipOp
+import androidx.ui.graphics.Color
+import androidx.ui.graphics.DesktopGraphicsTest
+import androidx.ui.graphics.Paint
+import androidx.ui.graphics.StrokeCap
+import androidx.ui.graphics.vectormath.Matrix4
+import androidx.ui.graphics.withSave
+import androidx.ui.graphics.withSaveLayer
+import androidx.ui.unit.IntOffset
+import androidx.ui.unit.IntSize
+import org.junit.Test
+
+class DesktopCanvasTest : DesktopGraphicsTest() {
+    private val canvas: Canvas = initCanvas(widthPx = 16, heightPx = 16)
+
+    @Test
+    fun drawArc() {
+        canvas.drawArc(
+            left = 0f,
+            top = 0f,
+            right = 16f,
+            bottom = 16f,
+            startAngle = 0f,
+            sweepAngle = 90f,
+            useCenter = true,
+            paint = redPaint
+        )
+        canvas.drawArc(
+            left = 0f,
+            top = 0f,
+            right = 16f,
+            bottom = 16f,
+            startAngle = 90f,
+            sweepAngle = 90f,
+            useCenter = true,
+            paint = bluePaint
+        )
+        canvas.drawArc(
+            left = 0f,
+            top = 0f,
+            right = 16f,
+            bottom = 16f,
+            startAngle = 180f,
+            sweepAngle = 90f,
+            useCenter = false,
+            paint = greenPaint
+        )
+        canvas.drawArc(
+            left = 0f,
+            top = 0f,
+            right = 16f,
+            bottom = 16f,
+            startAngle = 270f,
+            sweepAngle = 90f,
+            useCenter = false,
+            paint = cyanPaint
+        )
+
+        screenshotRule.snap(surface)
+    }
+
+    @Test
+    fun drawCircle() {
+        canvas.drawCircle(Offset(8f, 8f), radius = 4f, paint = redPaint)
+
+        screenshotRule.snap(surface)
+    }
+
+    @Test
+    fun drawImage() {
+        canvas.drawImage(
+            image = testImageAsset("androidx/ui/desktop/test.png"),
+            topLeftOffset = Offset(2f, 4f),
+            paint = redPaint
+        )
+        canvas.drawImage(
+            image = testImageAsset("androidx/ui/desktop/test.png"),
+            topLeftOffset = Offset(-2f, 0f),
+            paint = redPaint
+        )
+
+        screenshotRule.snap(surface)
+    }
+
+    @Test
+    fun drawImageRect() {
+        canvas.drawImageRect(
+            image = testImageAsset("androidx/ui/desktop/test.png"),
+            srcOffset = IntOffset(0, 2),
+            srcSize = IntSize(2, 4),
+            dstOffset = IntOffset(0, 4),
+            dstSize = IntSize(4, 12),
+            paint = redPaint
+        )
+
+        screenshotRule.snap(surface)
+    }
+
+    @Test
+    fun drawLine() {
+        canvas.drawLine(Offset(-4f, -4f), Offset(4f, 4f), Paint().apply {
+            color = Color.Red
+            strokeWidth = 1f
+            strokeCap = StrokeCap.butt
+        })
+        canvas.drawLine(Offset(8f, 4f), Offset(8f, 12f), Paint().apply {
+            color = Color.Blue
+            strokeWidth = 4f
+            strokeCap = StrokeCap.butt
+        })
+        canvas.drawLine(Offset(12f, 4f), Offset(12f, 12f), Paint().apply {
+            color = Color.Green
+            strokeWidth = 4f
+            strokeCap = StrokeCap.round
+        })
+        canvas.drawLine(Offset(4f, 4f), Offset(4f, 12f), Paint().apply {
+            color = Color.Black.copy(alpha = 0.5f)
+            strokeWidth = 4f
+            strokeCap = StrokeCap.square
+        })
+
+        // should draw antialiased two-pixel line
+        canvas.drawLine(Offset(4f, 4f), Offset(4f, 12f), Paint().apply {
+            color = Color.Yellow
+            strokeWidth = 1f
+            strokeCap = StrokeCap.butt
+        })
+
+        // should draw aliased one-pixel line
+        canvas.drawLine(Offset(4f, 4f), Offset(4f, 12f), Paint().apply {
+            color = Color.Yellow
+            strokeWidth = 1f
+            strokeCap = StrokeCap.butt
+            isAntiAlias = false
+        })
+
+        // shouldn't draw any line
+        canvas.drawLine(Offset(4f, 4f), Offset(4f, 12f), Paint().apply {
+            color = Color.Yellow
+            strokeWidth = 0f
+            strokeCap = StrokeCap.butt
+            isAntiAlias = false
+        })
+
+        screenshotRule.snap(surface)
+    }
+
+    @Test
+    fun drawOval() {
+        canvas.drawOval(left = 0f, top = 4f, right = 16f, bottom = 12f, paint = redPaint)
+
+        screenshotRule.snap(surface)
+    }
+
+    @Test
+    fun drawRect() {
+        canvas.drawRect(left = 2f, top = 0f, right = 6f, bottom = 8f, paint = redPaint)
+        canvas.drawRect(left = -4f, top = -4f, right = 4f, bottom = 4f, paint = bluePaint)
+        canvas.drawRect(left = 1f, top = 1f, right = 1f, bottom = 1f, paint = bluePaint)
+
+        screenshotRule.snap(surface)
+    }
+
+    @Test
+    fun drawRoundRect() {
+        canvas.drawRoundRect(
+            left = 0f,
+            top = 4f,
+            right = 16f,
+            bottom = 12f,
+            radiusX = 6f,
+            radiusY = 4f,
+            paint = redPaint
+        )
+
+        screenshotRule.snap(surface)
+    }
+
+    @Test
+    fun saveLayer() {
+        canvas.drawRect(0f, 0f, 16f, 16f, redPaint)
+
+        canvas.withSaveLayer(
+            Rect(left = 4f, top = 8f, right = 12f, bottom = 16f), redPaint.apply {
+                blendMode = BlendMode.plus
+            }
+        ) {
+            canvas.drawLine(Offset(4f, 0f), Offset(4f, 16f), bluePaint)
+        }
+
+        screenshotRule.snap(surface)
+    }
+
+    @Test
+    fun transform() {
+        canvas.withSave {
+            canvas.translate(4f, 2f)
+            canvas.drawRect(left = 0f, top = 0f, right = 4f, bottom = 4f, paint = redPaint)
+
+            canvas.rotate(45f)
+            canvas.drawLine(Offset(0f, 0f), Offset(4f, 0f), paint = bluePaint)
+        }
+
+        canvas.withSave {
+            canvas.rotate(90f)
+            canvas.translate(8f, 0f)
+            canvas.drawRect(left = 0f, top = -4f, right = 4f, bottom = 0f, paint = greenPaint)
+        }
+
+        canvas.withSave {
+            canvas.translate(8f, 8f)
+            canvas.skew(1f, 0f)
+            canvas.drawRect(left = 0f, top = 0f, right = 4f, bottom = 4f, paint = yellowPaint)
+        }
+
+        canvas.withSave {
+            canvas.translate(8f, 8f)
+            canvas.skew(0f, 1f)
+            canvas.drawRect(left = 0f, top = 0f, right = 4f, bottom = 4f, paint = magentaPaint)
+        }
+
+        canvas.withSave {
+            canvas.concat(
+                Matrix4.identity().apply {
+                    translate(12f, 2f)
+                }
+            )
+            canvas.drawRect(left = 0f, top = 0f, right = 4f, bottom = 4f, paint = cyanPaint)
+        }
+
+        screenshotRule.snap(surface)
+    }
+
+    @Test
+    fun clipRect() {
+        canvas.withSave {
+            canvas.clipRect(
+                left = 4f,
+                top = 4f,
+                right = 12f,
+                bottom = 12f,
+                clipOp = ClipOp.intersect
+            )
+            canvas.drawRect(left = -4f, top = -4f, right = 20f, bottom = 20f, paint = redPaint)
+        }
+
+        canvas.drawRect(left = 4f, top = 0f, right = 8f, bottom = 4f, paint = bluePaint)
+
+        canvas.clipRect(left = 2f, top = 2f, right = 14f, bottom = 14f, clipOp = ClipOp.difference)
+        canvas.drawRect(left = -4f, top = -4f, right = 20f, bottom = 20f, paint = greenPaint)
+
+        screenshotRule.snap(surface)
+    }
+}
diff --git a/ui/ui-desktop/src/jvmTest/res/androidx/ui/desktop/test.png b/ui/ui-desktop/src/jvmTest/res/androidx/ui/desktop/test.png
index 374b121..96963d3 100644
--- a/ui/ui-desktop/src/jvmTest/res/androidx/ui/desktop/test.png
+++ b/ui/ui-desktop/src/jvmTest/res/androidx/ui/desktop/test.png
Binary files differ
diff --git a/ui/ui-graphics/api/0.1.0-dev16.txt b/ui/ui-graphics/api/0.1.0-dev16.txt
index ba44771..e014534 100644
--- a/ui/ui-graphics/api/0.1.0-dev16.txt
+++ b/ui/ui-graphics/api/0.1.0-dev16.txt
@@ -24,7 +24,52 @@
     method public static androidx.ui.graphics.ImageAsset imageFromResource(android.content.res.Resources res, int resId);
   }
 
+  public final class AndroidPaint implements androidx.ui.graphics.Paint {
+    ctor public AndroidPaint();
+    method public android.graphics.Paint asFrameworkPaint();
+    method public float getAlpha();
+    method public androidx.ui.graphics.BlendMode getBlendMode();
+    method public long getColor();
+    method public androidx.ui.graphics.ColorFilter? getColorFilter();
+    method public androidx.ui.graphics.FilterQuality getFilterQuality();
+    method public android.graphics.PathEffect? getNativePathEffect();
+    method public android.graphics.Shader? getShader();
+    method public androidx.ui.graphics.StrokeCap getStrokeCap();
+    method public androidx.ui.graphics.StrokeJoin getStrokeJoin();
+    method public float getStrokeMiterLimit();
+    method public float getStrokeWidth();
+    method public androidx.ui.graphics.PaintingStyle getStyle();
+    method public boolean isAntiAlias();
+    method public void setAlpha(float value);
+    method public void setAntiAlias(boolean value);
+    method public void setBlendMode(androidx.ui.graphics.BlendMode value);
+    method public void setColor-QEYXlZo(long color);
+    method public void setColorFilter(androidx.ui.graphics.ColorFilter? value);
+    method public void setFilterQuality(androidx.ui.graphics.FilterQuality value);
+    method public void setNativePathEffect(android.graphics.PathEffect? value);
+    method public void setShader(android.graphics.Shader? value);
+    method public void setStrokeCap(androidx.ui.graphics.StrokeCap value);
+    method public void setStrokeJoin(androidx.ui.graphics.StrokeJoin value);
+    method public void setStrokeMiterLimit(float value);
+    method public void setStrokeWidth(float value);
+    method public void setStyle(androidx.ui.graphics.PaintingStyle value);
+    property public float alpha;
+    property public androidx.ui.graphics.BlendMode blendMode;
+    property public long color;
+    property public androidx.ui.graphics.ColorFilter? colorFilter;
+    property public androidx.ui.graphics.FilterQuality filterQuality;
+    property public boolean isAntiAlias;
+    property public android.graphics.PathEffect? nativePathEffect;
+    property public android.graphics.Shader? shader;
+    property public androidx.ui.graphics.StrokeCap strokeCap;
+    property public androidx.ui.graphics.StrokeJoin strokeJoin;
+    property public float strokeMiterLimit;
+    property public float strokeWidth;
+    property public androidx.ui.graphics.PaintingStyle style;
+  }
+
   public final class AndroidPaintKt {
+    method public static androidx.ui.graphics.Paint Paint();
   }
 
   public final class AndroidPath implements androidx.ui.graphics.Path {
@@ -273,6 +318,38 @@
   public final class Float16Kt {
   }
 
+  @Deprecated @androidx.compose.InternalComposeApi public final class GraphicsFactory {
+    method @Deprecated public kotlin.jvm.functions.Function0<androidx.ui.graphics.InternalCanvasHolder> getCanvasHolder();
+    method @Deprecated public kotlin.reflect.KFunction<androidx.ui.graphics.Canvas> getImageCanvas();
+    method @Deprecated public kotlin.jvm.functions.Function1<android.graphics.Canvas,androidx.ui.graphics.Canvas> getNativeCanvas();
+    method @Deprecated public kotlin.jvm.functions.Function0<androidx.ui.graphics.Paint> getPaint();
+    method @Deprecated public kotlin.jvm.functions.Function0<androidx.ui.graphics.Path> getPath();
+    method @Deprecated public void setCanvasHolder(kotlin.jvm.functions.Function0<? extends androidx.ui.graphics.InternalCanvasHolder> p);
+    method @Deprecated public void setImageCanvas(kotlin.reflect.KFunction<? extends androidx.ui.graphics.Canvas> p);
+    method @Deprecated public void setNativeCanvas(kotlin.jvm.functions.Function1<? super android.graphics.Canvas,? extends androidx.ui.graphics.Canvas> p);
+    method @Deprecated public void setPaint(kotlin.jvm.functions.Function0<? extends androidx.ui.graphics.Paint> p);
+    method @Deprecated public void setPath(kotlin.jvm.functions.Function0<? extends androidx.ui.graphics.Path> p);
+    property public final kotlin.jvm.functions.Function0<androidx.ui.graphics.InternalCanvasHolder> canvasHolder;
+    property public final kotlin.reflect.KFunction<androidx.ui.graphics.Canvas> imageCanvas;
+    property public final kotlin.jvm.functions.Function1<android.graphics.Canvas,androidx.ui.graphics.Canvas> nativeCanvas;
+    property public final kotlin.jvm.functions.Function0<androidx.ui.graphics.Paint> paint;
+    property public final kotlin.jvm.functions.Function0<androidx.ui.graphics.Path> path;
+    field @Deprecated public static final androidx.ui.graphics.GraphicsFactory INSTANCE;
+  }
+
+  @Deprecated public static final class GraphicsFactory.Shader {
+    method @Deprecated public kotlin.reflect.KFunction<android.graphics.Shader> getImage();
+    method @Deprecated public kotlin.reflect.KFunction<android.graphics.Shader> getLinear();
+    method @Deprecated public kotlin.reflect.KFunction<android.graphics.Shader> getRadial();
+    method @Deprecated public void setImage(kotlin.reflect.KFunction<? extends android.graphics.Shader> p);
+    method @Deprecated public void setLinear(kotlin.reflect.KFunction<? extends android.graphics.Shader> p);
+    method @Deprecated public void setRadial(kotlin.reflect.KFunction<? extends android.graphics.Shader> p);
+    property public final kotlin.reflect.KFunction<android.graphics.Shader> image;
+    property public final kotlin.reflect.KFunction<android.graphics.Shader> linear;
+    property public final kotlin.reflect.KFunction<android.graphics.Shader> radial;
+    field @Deprecated public static final androidx.ui.graphics.GraphicsFactory.Shader INSTANCE;
+  }
+
   public interface ImageAsset {
     method public androidx.ui.graphics.colorspace.ColorSpace getColorSpace();
     method public androidx.ui.graphics.ImageAssetConfig getConfig();
@@ -303,6 +380,14 @@
     method public static androidx.ui.graphics.PixelMap toPixelMap(androidx.ui.graphics.ImageAsset, int startX = 0, int startY = 0, int width = this.width, int height = this.height, int[] buffer = null(width * height), int bufferOffset = 0, int stride = width);
   }
 
+  @androidx.compose.InternalComposeApi public interface InternalCanvasHolder {
+    method public androidx.ui.graphics.Canvas getCanvas();
+    method public android.graphics.Canvas getInternalCanvas();
+    method public void setInternalCanvas(android.graphics.Canvas p);
+    property public abstract androidx.ui.graphics.Canvas canvas;
+    property public abstract android.graphics.Canvas internalCanvas;
+  }
+
   public final class LinearGradient extends androidx.ui.graphics.ShaderBrush {
     method public androidx.ui.graphics.LinearGradient copy(java.util.List<androidx.ui.graphics.Color> colors, java.util.List<java.lang.Float>? stops, float startX, float startY, float endX, float endY, androidx.ui.graphics.TileMode tileMode);
   }
@@ -338,14 +423,14 @@
     method public static void drawOutline-2ddATRk(androidx.ui.graphics.drawscope.DrawScope, androidx.ui.graphics.Outline outline, long color, @FloatRange(from=0.0, to=1.0) float alpha = 1.0f, androidx.ui.graphics.drawscope.DrawStyle style = Fill, androidx.ui.graphics.ColorFilter? colorFilter = null, androidx.ui.graphics.BlendMode blendMode = DrawScope.DefaultBlendMode);
   }
 
-  public final class Paint {
-    ctor public Paint();
+  public interface Paint {
     method public android.graphics.Paint asFrameworkPaint();
     method public float getAlpha();
     method public androidx.ui.graphics.BlendMode getBlendMode();
     method public long getColor();
     method public androidx.ui.graphics.ColorFilter? getColorFilter();
     method public androidx.ui.graphics.FilterQuality getFilterQuality();
+    method public android.graphics.PathEffect? getNativePathEffect();
     method public android.graphics.Shader? getShader();
     method public androidx.ui.graphics.StrokeCap getStrokeCap();
     method public androidx.ui.graphics.StrokeJoin getStrokeJoin();
@@ -353,30 +438,32 @@
     method public float getStrokeWidth();
     method public androidx.ui.graphics.PaintingStyle getStyle();
     method public boolean isAntiAlias();
-    method public void setAlpha(float value);
-    method public void setAntiAlias(boolean value);
-    method public void setBlendMode(androidx.ui.graphics.BlendMode value);
-    method public void setColor-QEYXlZo(long color);
-    method public void setColorFilter(androidx.ui.graphics.ColorFilter? value);
-    method public void setFilterQuality(androidx.ui.graphics.FilterQuality value);
-    method public void setShader(android.graphics.Shader? value);
-    method public void setStrokeCap(androidx.ui.graphics.StrokeCap value);
-    method public void setStrokeJoin(androidx.ui.graphics.StrokeJoin value);
-    method public void setStrokeMiterLimit(float value);
-    method public void setStrokeWidth(float value);
-    method public void setStyle(androidx.ui.graphics.PaintingStyle value);
-    property public final float alpha;
-    property public final androidx.ui.graphics.BlendMode blendMode;
-    property public final long color;
-    property public final androidx.ui.graphics.ColorFilter? colorFilter;
-    property public final androidx.ui.graphics.FilterQuality filterQuality;
-    property public final boolean isAntiAlias;
-    property public final android.graphics.Shader? shader;
-    property public final androidx.ui.graphics.StrokeCap strokeCap;
-    property public final androidx.ui.graphics.StrokeJoin strokeJoin;
-    property public final float strokeMiterLimit;
-    property public final float strokeWidth;
-    property public final androidx.ui.graphics.PaintingStyle style;
+    method public void setAlpha(float p);
+    method public void setAntiAlias(boolean p);
+    method public void setBlendMode(androidx.ui.graphics.BlendMode p);
+    method public void setColor-QEYXlZo(long p);
+    method public void setColorFilter(androidx.ui.graphics.ColorFilter? p);
+    method public void setFilterQuality(androidx.ui.graphics.FilterQuality p);
+    method public void setNativePathEffect(android.graphics.PathEffect? p);
+    method public void setShader(android.graphics.Shader? p);
+    method public void setStrokeCap(androidx.ui.graphics.StrokeCap p);
+    method public void setStrokeJoin(androidx.ui.graphics.StrokeJoin p);
+    method public void setStrokeMiterLimit(float p);
+    method public void setStrokeWidth(float p);
+    method public void setStyle(androidx.ui.graphics.PaintingStyle p);
+    property public abstract float alpha;
+    property public abstract androidx.ui.graphics.BlendMode blendMode;
+    property public abstract long color;
+    property public abstract androidx.ui.graphics.ColorFilter? colorFilter;
+    property public abstract androidx.ui.graphics.FilterQuality filterQuality;
+    property public abstract boolean isAntiAlias;
+    property public abstract android.graphics.PathEffect? nativePathEffect;
+    property public abstract android.graphics.Shader? shader;
+    property public abstract androidx.ui.graphics.StrokeCap strokeCap;
+    property public abstract androidx.ui.graphics.StrokeJoin strokeJoin;
+    property public abstract float strokeMiterLimit;
+    property public abstract float strokeWidth;
+    property public abstract androidx.ui.graphics.PaintingStyle style;
   }
 
   public final class PaintKt {
diff --git a/ui/ui-graphics/api/current.txt b/ui/ui-graphics/api/current.txt
index ba44771..e014534 100644
--- a/ui/ui-graphics/api/current.txt
+++ b/ui/ui-graphics/api/current.txt
@@ -24,7 +24,52 @@
     method public static androidx.ui.graphics.ImageAsset imageFromResource(android.content.res.Resources res, int resId);
   }
 
+  public final class AndroidPaint implements androidx.ui.graphics.Paint {
+    ctor public AndroidPaint();
+    method public android.graphics.Paint asFrameworkPaint();
+    method public float getAlpha();
+    method public androidx.ui.graphics.BlendMode getBlendMode();
+    method public long getColor();
+    method public androidx.ui.graphics.ColorFilter? getColorFilter();
+    method public androidx.ui.graphics.FilterQuality getFilterQuality();
+    method public android.graphics.PathEffect? getNativePathEffect();
+    method public android.graphics.Shader? getShader();
+    method public androidx.ui.graphics.StrokeCap getStrokeCap();
+    method public androidx.ui.graphics.StrokeJoin getStrokeJoin();
+    method public float getStrokeMiterLimit();
+    method public float getStrokeWidth();
+    method public androidx.ui.graphics.PaintingStyle getStyle();
+    method public boolean isAntiAlias();
+    method public void setAlpha(float value);
+    method public void setAntiAlias(boolean value);
+    method public void setBlendMode(androidx.ui.graphics.BlendMode value);
+    method public void setColor-QEYXlZo(long color);
+    method public void setColorFilter(androidx.ui.graphics.ColorFilter? value);
+    method public void setFilterQuality(androidx.ui.graphics.FilterQuality value);
+    method public void setNativePathEffect(android.graphics.PathEffect? value);
+    method public void setShader(android.graphics.Shader? value);
+    method public void setStrokeCap(androidx.ui.graphics.StrokeCap value);
+    method public void setStrokeJoin(androidx.ui.graphics.StrokeJoin value);
+    method public void setStrokeMiterLimit(float value);
+    method public void setStrokeWidth(float value);
+    method public void setStyle(androidx.ui.graphics.PaintingStyle value);
+    property public float alpha;
+    property public androidx.ui.graphics.BlendMode blendMode;
+    property public long color;
+    property public androidx.ui.graphics.ColorFilter? colorFilter;
+    property public androidx.ui.graphics.FilterQuality filterQuality;
+    property public boolean isAntiAlias;
+    property public android.graphics.PathEffect? nativePathEffect;
+    property public android.graphics.Shader? shader;
+    property public androidx.ui.graphics.StrokeCap strokeCap;
+    property public androidx.ui.graphics.StrokeJoin strokeJoin;
+    property public float strokeMiterLimit;
+    property public float strokeWidth;
+    property public androidx.ui.graphics.PaintingStyle style;
+  }
+
   public final class AndroidPaintKt {
+    method public static androidx.ui.graphics.Paint Paint();
   }
 
   public final class AndroidPath implements androidx.ui.graphics.Path {
@@ -273,6 +318,38 @@
   public final class Float16Kt {
   }
 
+  @Deprecated @androidx.compose.InternalComposeApi public final class GraphicsFactory {
+    method @Deprecated public kotlin.jvm.functions.Function0<androidx.ui.graphics.InternalCanvasHolder> getCanvasHolder();
+    method @Deprecated public kotlin.reflect.KFunction<androidx.ui.graphics.Canvas> getImageCanvas();
+    method @Deprecated public kotlin.jvm.functions.Function1<android.graphics.Canvas,androidx.ui.graphics.Canvas> getNativeCanvas();
+    method @Deprecated public kotlin.jvm.functions.Function0<androidx.ui.graphics.Paint> getPaint();
+    method @Deprecated public kotlin.jvm.functions.Function0<androidx.ui.graphics.Path> getPath();
+    method @Deprecated public void setCanvasHolder(kotlin.jvm.functions.Function0<? extends androidx.ui.graphics.InternalCanvasHolder> p);
+    method @Deprecated public void setImageCanvas(kotlin.reflect.KFunction<? extends androidx.ui.graphics.Canvas> p);
+    method @Deprecated public void setNativeCanvas(kotlin.jvm.functions.Function1<? super android.graphics.Canvas,? extends androidx.ui.graphics.Canvas> p);
+    method @Deprecated public void setPaint(kotlin.jvm.functions.Function0<? extends androidx.ui.graphics.Paint> p);
+    method @Deprecated public void setPath(kotlin.jvm.functions.Function0<? extends androidx.ui.graphics.Path> p);
+    property public final kotlin.jvm.functions.Function0<androidx.ui.graphics.InternalCanvasHolder> canvasHolder;
+    property public final kotlin.reflect.KFunction<androidx.ui.graphics.Canvas> imageCanvas;
+    property public final kotlin.jvm.functions.Function1<android.graphics.Canvas,androidx.ui.graphics.Canvas> nativeCanvas;
+    property public final kotlin.jvm.functions.Function0<androidx.ui.graphics.Paint> paint;
+    property public final kotlin.jvm.functions.Function0<androidx.ui.graphics.Path> path;
+    field @Deprecated public static final androidx.ui.graphics.GraphicsFactory INSTANCE;
+  }
+
+  @Deprecated public static final class GraphicsFactory.Shader {
+    method @Deprecated public kotlin.reflect.KFunction<android.graphics.Shader> getImage();
+    method @Deprecated public kotlin.reflect.KFunction<android.graphics.Shader> getLinear();
+    method @Deprecated public kotlin.reflect.KFunction<android.graphics.Shader> getRadial();
+    method @Deprecated public void setImage(kotlin.reflect.KFunction<? extends android.graphics.Shader> p);
+    method @Deprecated public void setLinear(kotlin.reflect.KFunction<? extends android.graphics.Shader> p);
+    method @Deprecated public void setRadial(kotlin.reflect.KFunction<? extends android.graphics.Shader> p);
+    property public final kotlin.reflect.KFunction<android.graphics.Shader> image;
+    property public final kotlin.reflect.KFunction<android.graphics.Shader> linear;
+    property public final kotlin.reflect.KFunction<android.graphics.Shader> radial;
+    field @Deprecated public static final androidx.ui.graphics.GraphicsFactory.Shader INSTANCE;
+  }
+
   public interface ImageAsset {
     method public androidx.ui.graphics.colorspace.ColorSpace getColorSpace();
     method public androidx.ui.graphics.ImageAssetConfig getConfig();
@@ -303,6 +380,14 @@
     method public static androidx.ui.graphics.PixelMap toPixelMap(androidx.ui.graphics.ImageAsset, int startX = 0, int startY = 0, int width = this.width, int height = this.height, int[] buffer = null(width * height), int bufferOffset = 0, int stride = width);
   }
 
+  @androidx.compose.InternalComposeApi public interface InternalCanvasHolder {
+    method public androidx.ui.graphics.Canvas getCanvas();
+    method public android.graphics.Canvas getInternalCanvas();
+    method public void setInternalCanvas(android.graphics.Canvas p);
+    property public abstract androidx.ui.graphics.Canvas canvas;
+    property public abstract android.graphics.Canvas internalCanvas;
+  }
+
   public final class LinearGradient extends androidx.ui.graphics.ShaderBrush {
     method public androidx.ui.graphics.LinearGradient copy(java.util.List<androidx.ui.graphics.Color> colors, java.util.List<java.lang.Float>? stops, float startX, float startY, float endX, float endY, androidx.ui.graphics.TileMode tileMode);
   }
@@ -338,14 +423,14 @@
     method public static void drawOutline-2ddATRk(androidx.ui.graphics.drawscope.DrawScope, androidx.ui.graphics.Outline outline, long color, @FloatRange(from=0.0, to=1.0) float alpha = 1.0f, androidx.ui.graphics.drawscope.DrawStyle style = Fill, androidx.ui.graphics.ColorFilter? colorFilter = null, androidx.ui.graphics.BlendMode blendMode = DrawScope.DefaultBlendMode);
   }
 
-  public final class Paint {
-    ctor public Paint();
+  public interface Paint {
     method public android.graphics.Paint asFrameworkPaint();
     method public float getAlpha();
     method public androidx.ui.graphics.BlendMode getBlendMode();
     method public long getColor();
     method public androidx.ui.graphics.ColorFilter? getColorFilter();
     method public androidx.ui.graphics.FilterQuality getFilterQuality();
+    method public android.graphics.PathEffect? getNativePathEffect();
     method public android.graphics.Shader? getShader();
     method public androidx.ui.graphics.StrokeCap getStrokeCap();
     method public androidx.ui.graphics.StrokeJoin getStrokeJoin();
@@ -353,30 +438,32 @@
     method public float getStrokeWidth();
     method public androidx.ui.graphics.PaintingStyle getStyle();
     method public boolean isAntiAlias();
-    method public void setAlpha(float value);
-    method public void setAntiAlias(boolean value);
-    method public void setBlendMode(androidx.ui.graphics.BlendMode value);
-    method public void setColor-QEYXlZo(long color);
-    method public void setColorFilter(androidx.ui.graphics.ColorFilter? value);
-    method public void setFilterQuality(androidx.ui.graphics.FilterQuality value);
-    method public void setShader(android.graphics.Shader? value);
-    method public void setStrokeCap(androidx.ui.graphics.StrokeCap value);
-    method public void setStrokeJoin(androidx.ui.graphics.StrokeJoin value);
-    method public void setStrokeMiterLimit(float value);
-    method public void setStrokeWidth(float value);
-    method public void setStyle(androidx.ui.graphics.PaintingStyle value);
-    property public final float alpha;
-    property public final androidx.ui.graphics.BlendMode blendMode;
-    property public final long color;
-    property public final androidx.ui.graphics.ColorFilter? colorFilter;
-    property public final androidx.ui.graphics.FilterQuality filterQuality;
-    property public final boolean isAntiAlias;
-    property public final android.graphics.Shader? shader;
-    property public final androidx.ui.graphics.StrokeCap strokeCap;
-    property public final androidx.ui.graphics.StrokeJoin strokeJoin;
-    property public final float strokeMiterLimit;
-    property public final float strokeWidth;
-    property public final androidx.ui.graphics.PaintingStyle style;
+    method public void setAlpha(float p);
+    method public void setAntiAlias(boolean p);
+    method public void setBlendMode(androidx.ui.graphics.BlendMode p);
+    method public void setColor-QEYXlZo(long p);
+    method public void setColorFilter(androidx.ui.graphics.ColorFilter? p);
+    method public void setFilterQuality(androidx.ui.graphics.FilterQuality p);
+    method public void setNativePathEffect(android.graphics.PathEffect? p);
+    method public void setShader(android.graphics.Shader? p);
+    method public void setStrokeCap(androidx.ui.graphics.StrokeCap p);
+    method public void setStrokeJoin(androidx.ui.graphics.StrokeJoin p);
+    method public void setStrokeMiterLimit(float p);
+    method public void setStrokeWidth(float p);
+    method public void setStyle(androidx.ui.graphics.PaintingStyle p);
+    property public abstract float alpha;
+    property public abstract androidx.ui.graphics.BlendMode blendMode;
+    property public abstract long color;
+    property public abstract androidx.ui.graphics.ColorFilter? colorFilter;
+    property public abstract androidx.ui.graphics.FilterQuality filterQuality;
+    property public abstract boolean isAntiAlias;
+    property public abstract android.graphics.PathEffect? nativePathEffect;
+    property public abstract android.graphics.Shader? shader;
+    property public abstract androidx.ui.graphics.StrokeCap strokeCap;
+    property public abstract androidx.ui.graphics.StrokeJoin strokeJoin;
+    property public abstract float strokeMiterLimit;
+    property public abstract float strokeWidth;
+    property public abstract androidx.ui.graphics.PaintingStyle style;
   }
 
   public final class PaintKt {
diff --git a/ui/ui-graphics/api/public_plus_experimental_0.1.0-dev16.txt b/ui/ui-graphics/api/public_plus_experimental_0.1.0-dev16.txt
index ba44771..e014534 100644
--- a/ui/ui-graphics/api/public_plus_experimental_0.1.0-dev16.txt
+++ b/ui/ui-graphics/api/public_plus_experimental_0.1.0-dev16.txt
@@ -24,7 +24,52 @@
     method public static androidx.ui.graphics.ImageAsset imageFromResource(android.content.res.Resources res, int resId);
   }
 
+  public final class AndroidPaint implements androidx.ui.graphics.Paint {
+    ctor public AndroidPaint();
+    method public android.graphics.Paint asFrameworkPaint();
+    method public float getAlpha();
+    method public androidx.ui.graphics.BlendMode getBlendMode();
+    method public long getColor();
+    method public androidx.ui.graphics.ColorFilter? getColorFilter();
+    method public androidx.ui.graphics.FilterQuality getFilterQuality();
+    method public android.graphics.PathEffect? getNativePathEffect();
+    method public android.graphics.Shader? getShader();
+    method public androidx.ui.graphics.StrokeCap getStrokeCap();
+    method public androidx.ui.graphics.StrokeJoin getStrokeJoin();
+    method public float getStrokeMiterLimit();
+    method public float getStrokeWidth();
+    method public androidx.ui.graphics.PaintingStyle getStyle();
+    method public boolean isAntiAlias();
+    method public void setAlpha(float value);
+    method public void setAntiAlias(boolean value);
+    method public void setBlendMode(androidx.ui.graphics.BlendMode value);
+    method public void setColor-QEYXlZo(long color);
+    method public void setColorFilter(androidx.ui.graphics.ColorFilter? value);
+    method public void setFilterQuality(androidx.ui.graphics.FilterQuality value);
+    method public void setNativePathEffect(android.graphics.PathEffect? value);
+    method public void setShader(android.graphics.Shader? value);
+    method public void setStrokeCap(androidx.ui.graphics.StrokeCap value);
+    method public void setStrokeJoin(androidx.ui.graphics.StrokeJoin value);
+    method public void setStrokeMiterLimit(float value);
+    method public void setStrokeWidth(float value);
+    method public void setStyle(androidx.ui.graphics.PaintingStyle value);
+    property public float alpha;
+    property public androidx.ui.graphics.BlendMode blendMode;
+    property public long color;
+    property public androidx.ui.graphics.ColorFilter? colorFilter;
+    property public androidx.ui.graphics.FilterQuality filterQuality;
+    property public boolean isAntiAlias;
+    property public android.graphics.PathEffect? nativePathEffect;
+    property public android.graphics.Shader? shader;
+    property public androidx.ui.graphics.StrokeCap strokeCap;
+    property public androidx.ui.graphics.StrokeJoin strokeJoin;
+    property public float strokeMiterLimit;
+    property public float strokeWidth;
+    property public androidx.ui.graphics.PaintingStyle style;
+  }
+
   public final class AndroidPaintKt {
+    method public static androidx.ui.graphics.Paint Paint();
   }
 
   public final class AndroidPath implements androidx.ui.graphics.Path {
@@ -273,6 +318,38 @@
   public final class Float16Kt {
   }
 
+  @Deprecated @androidx.compose.InternalComposeApi public final class GraphicsFactory {
+    method @Deprecated public kotlin.jvm.functions.Function0<androidx.ui.graphics.InternalCanvasHolder> getCanvasHolder();
+    method @Deprecated public kotlin.reflect.KFunction<androidx.ui.graphics.Canvas> getImageCanvas();
+    method @Deprecated public kotlin.jvm.functions.Function1<android.graphics.Canvas,androidx.ui.graphics.Canvas> getNativeCanvas();
+    method @Deprecated public kotlin.jvm.functions.Function0<androidx.ui.graphics.Paint> getPaint();
+    method @Deprecated public kotlin.jvm.functions.Function0<androidx.ui.graphics.Path> getPath();
+    method @Deprecated public void setCanvasHolder(kotlin.jvm.functions.Function0<? extends androidx.ui.graphics.InternalCanvasHolder> p);
+    method @Deprecated public void setImageCanvas(kotlin.reflect.KFunction<? extends androidx.ui.graphics.Canvas> p);
+    method @Deprecated public void setNativeCanvas(kotlin.jvm.functions.Function1<? super android.graphics.Canvas,? extends androidx.ui.graphics.Canvas> p);
+    method @Deprecated public void setPaint(kotlin.jvm.functions.Function0<? extends androidx.ui.graphics.Paint> p);
+    method @Deprecated public void setPath(kotlin.jvm.functions.Function0<? extends androidx.ui.graphics.Path> p);
+    property public final kotlin.jvm.functions.Function0<androidx.ui.graphics.InternalCanvasHolder> canvasHolder;
+    property public final kotlin.reflect.KFunction<androidx.ui.graphics.Canvas> imageCanvas;
+    property public final kotlin.jvm.functions.Function1<android.graphics.Canvas,androidx.ui.graphics.Canvas> nativeCanvas;
+    property public final kotlin.jvm.functions.Function0<androidx.ui.graphics.Paint> paint;
+    property public final kotlin.jvm.functions.Function0<androidx.ui.graphics.Path> path;
+    field @Deprecated public static final androidx.ui.graphics.GraphicsFactory INSTANCE;
+  }
+
+  @Deprecated public static final class GraphicsFactory.Shader {
+    method @Deprecated public kotlin.reflect.KFunction<android.graphics.Shader> getImage();
+    method @Deprecated public kotlin.reflect.KFunction<android.graphics.Shader> getLinear();
+    method @Deprecated public kotlin.reflect.KFunction<android.graphics.Shader> getRadial();
+    method @Deprecated public void setImage(kotlin.reflect.KFunction<? extends android.graphics.Shader> p);
+    method @Deprecated public void setLinear(kotlin.reflect.KFunction<? extends android.graphics.Shader> p);
+    method @Deprecated public void setRadial(kotlin.reflect.KFunction<? extends android.graphics.Shader> p);
+    property public final kotlin.reflect.KFunction<android.graphics.Shader> image;
+    property public final kotlin.reflect.KFunction<android.graphics.Shader> linear;
+    property public final kotlin.reflect.KFunction<android.graphics.Shader> radial;
+    field @Deprecated public static final androidx.ui.graphics.GraphicsFactory.Shader INSTANCE;
+  }
+
   public interface ImageAsset {
     method public androidx.ui.graphics.colorspace.ColorSpace getColorSpace();
     method public androidx.ui.graphics.ImageAssetConfig getConfig();
@@ -303,6 +380,14 @@
     method public static androidx.ui.graphics.PixelMap toPixelMap(androidx.ui.graphics.ImageAsset, int startX = 0, int startY = 0, int width = this.width, int height = this.height, int[] buffer = null(width * height), int bufferOffset = 0, int stride = width);
   }
 
+  @androidx.compose.InternalComposeApi public interface InternalCanvasHolder {
+    method public androidx.ui.graphics.Canvas getCanvas();
+    method public android.graphics.Canvas getInternalCanvas();
+    method public void setInternalCanvas(android.graphics.Canvas p);
+    property public abstract androidx.ui.graphics.Canvas canvas;
+    property public abstract android.graphics.Canvas internalCanvas;
+  }
+
   public final class LinearGradient extends androidx.ui.graphics.ShaderBrush {
     method public androidx.ui.graphics.LinearGradient copy(java.util.List<androidx.ui.graphics.Color> colors, java.util.List<java.lang.Float>? stops, float startX, float startY, float endX, float endY, androidx.ui.graphics.TileMode tileMode);
   }
@@ -338,14 +423,14 @@
     method public static void drawOutline-2ddATRk(androidx.ui.graphics.drawscope.DrawScope, androidx.ui.graphics.Outline outline, long color, @FloatRange(from=0.0, to=1.0) float alpha = 1.0f, androidx.ui.graphics.drawscope.DrawStyle style = Fill, androidx.ui.graphics.ColorFilter? colorFilter = null, androidx.ui.graphics.BlendMode blendMode = DrawScope.DefaultBlendMode);
   }
 
-  public final class Paint {
-    ctor public Paint();
+  public interface Paint {
     method public android.graphics.Paint asFrameworkPaint();
     method public float getAlpha();
     method public androidx.ui.graphics.BlendMode getBlendMode();
     method public long getColor();
     method public androidx.ui.graphics.ColorFilter? getColorFilter();
     method public androidx.ui.graphics.FilterQuality getFilterQuality();
+    method public android.graphics.PathEffect? getNativePathEffect();
     method public android.graphics.Shader? getShader();
     method public androidx.ui.graphics.StrokeCap getStrokeCap();
     method public androidx.ui.graphics.StrokeJoin getStrokeJoin();
@@ -353,30 +438,32 @@
     method public float getStrokeWidth();
     method public androidx.ui.graphics.PaintingStyle getStyle();
     method public boolean isAntiAlias();
-    method public void setAlpha(float value);
-    method public void setAntiAlias(boolean value);
-    method public void setBlendMode(androidx.ui.graphics.BlendMode value);
-    method public void setColor-QEYXlZo(long color);
-    method public void setColorFilter(androidx.ui.graphics.ColorFilter? value);
-    method public void setFilterQuality(androidx.ui.graphics.FilterQuality value);
-    method public void setShader(android.graphics.Shader? value);
-    method public void setStrokeCap(androidx.ui.graphics.StrokeCap value);
-    method public void setStrokeJoin(androidx.ui.graphics.StrokeJoin value);
-    method public void setStrokeMiterLimit(float value);
-    method public void setStrokeWidth(float value);
-    method public void setStyle(androidx.ui.graphics.PaintingStyle value);
-    property public final float alpha;
-    property public final androidx.ui.graphics.BlendMode blendMode;
-    property public final long color;
-    property public final androidx.ui.graphics.ColorFilter? colorFilter;
-    property public final androidx.ui.graphics.FilterQuality filterQuality;
-    property public final boolean isAntiAlias;
-    property public final android.graphics.Shader? shader;
-    property public final androidx.ui.graphics.StrokeCap strokeCap;
-    property public final androidx.ui.graphics.StrokeJoin strokeJoin;
-    property public final float strokeMiterLimit;
-    property public final float strokeWidth;
-    property public final androidx.ui.graphics.PaintingStyle style;
+    method public void setAlpha(float p);
+    method public void setAntiAlias(boolean p);
+    method public void setBlendMode(androidx.ui.graphics.BlendMode p);
+    method public void setColor-QEYXlZo(long p);
+    method public void setColorFilter(androidx.ui.graphics.ColorFilter? p);
+    method public void setFilterQuality(androidx.ui.graphics.FilterQuality p);
+    method public void setNativePathEffect(android.graphics.PathEffect? p);
+    method public void setShader(android.graphics.Shader? p);
+    method public void setStrokeCap(androidx.ui.graphics.StrokeCap p);
+    method public void setStrokeJoin(androidx.ui.graphics.StrokeJoin p);
+    method public void setStrokeMiterLimit(float p);
+    method public void setStrokeWidth(float p);
+    method public void setStyle(androidx.ui.graphics.PaintingStyle p);
+    property public abstract float alpha;
+    property public abstract androidx.ui.graphics.BlendMode blendMode;
+    property public abstract long color;
+    property public abstract androidx.ui.graphics.ColorFilter? colorFilter;
+    property public abstract androidx.ui.graphics.FilterQuality filterQuality;
+    property public abstract boolean isAntiAlias;
+    property public abstract android.graphics.PathEffect? nativePathEffect;
+    property public abstract android.graphics.Shader? shader;
+    property public abstract androidx.ui.graphics.StrokeCap strokeCap;
+    property public abstract androidx.ui.graphics.StrokeJoin strokeJoin;
+    property public abstract float strokeMiterLimit;
+    property public abstract float strokeWidth;
+    property public abstract androidx.ui.graphics.PaintingStyle style;
   }
 
   public final class PaintKt {
diff --git a/ui/ui-graphics/api/public_plus_experimental_current.txt b/ui/ui-graphics/api/public_plus_experimental_current.txt
index ba44771..e014534 100644
--- a/ui/ui-graphics/api/public_plus_experimental_current.txt
+++ b/ui/ui-graphics/api/public_plus_experimental_current.txt
@@ -24,7 +24,52 @@
     method public static androidx.ui.graphics.ImageAsset imageFromResource(android.content.res.Resources res, int resId);
   }
 
+  public final class AndroidPaint implements androidx.ui.graphics.Paint {
+    ctor public AndroidPaint();
+    method public android.graphics.Paint asFrameworkPaint();
+    method public float getAlpha();
+    method public androidx.ui.graphics.BlendMode getBlendMode();
+    method public long getColor();
+    method public androidx.ui.graphics.ColorFilter? getColorFilter();
+    method public androidx.ui.graphics.FilterQuality getFilterQuality();
+    method public android.graphics.PathEffect? getNativePathEffect();
+    method public android.graphics.Shader? getShader();
+    method public androidx.ui.graphics.StrokeCap getStrokeCap();
+    method public androidx.ui.graphics.StrokeJoin getStrokeJoin();
+    method public float getStrokeMiterLimit();
+    method public float getStrokeWidth();
+    method public androidx.ui.graphics.PaintingStyle getStyle();
+    method public boolean isAntiAlias();
+    method public void setAlpha(float value);
+    method public void setAntiAlias(boolean value);
+    method public void setBlendMode(androidx.ui.graphics.BlendMode value);
+    method public void setColor-QEYXlZo(long color);
+    method public void setColorFilter(androidx.ui.graphics.ColorFilter? value);
+    method public void setFilterQuality(androidx.ui.graphics.FilterQuality value);
+    method public void setNativePathEffect(android.graphics.PathEffect? value);
+    method public void setShader(android.graphics.Shader? value);
+    method public void setStrokeCap(androidx.ui.graphics.StrokeCap value);
+    method public void setStrokeJoin(androidx.ui.graphics.StrokeJoin value);
+    method public void setStrokeMiterLimit(float value);
+    method public void setStrokeWidth(float value);
+    method public void setStyle(androidx.ui.graphics.PaintingStyle value);
+    property public float alpha;
+    property public androidx.ui.graphics.BlendMode blendMode;
+    property public long color;
+    property public androidx.ui.graphics.ColorFilter? colorFilter;
+    property public androidx.ui.graphics.FilterQuality filterQuality;
+    property public boolean isAntiAlias;
+    property public android.graphics.PathEffect? nativePathEffect;
+    property public android.graphics.Shader? shader;
+    property public androidx.ui.graphics.StrokeCap strokeCap;
+    property public androidx.ui.graphics.StrokeJoin strokeJoin;
+    property public float strokeMiterLimit;
+    property public float strokeWidth;
+    property public androidx.ui.graphics.PaintingStyle style;
+  }
+
   public final class AndroidPaintKt {
+    method public static androidx.ui.graphics.Paint Paint();
   }
 
   public final class AndroidPath implements androidx.ui.graphics.Path {
@@ -273,6 +318,38 @@
   public final class Float16Kt {
   }
 
+  @Deprecated @androidx.compose.InternalComposeApi public final class GraphicsFactory {
+    method @Deprecated public kotlin.jvm.functions.Function0<androidx.ui.graphics.InternalCanvasHolder> getCanvasHolder();
+    method @Deprecated public kotlin.reflect.KFunction<androidx.ui.graphics.Canvas> getImageCanvas();
+    method @Deprecated public kotlin.jvm.functions.Function1<android.graphics.Canvas,androidx.ui.graphics.Canvas> getNativeCanvas();
+    method @Deprecated public kotlin.jvm.functions.Function0<androidx.ui.graphics.Paint> getPaint();
+    method @Deprecated public kotlin.jvm.functions.Function0<androidx.ui.graphics.Path> getPath();
+    method @Deprecated public void setCanvasHolder(kotlin.jvm.functions.Function0<? extends androidx.ui.graphics.InternalCanvasHolder> p);
+    method @Deprecated public void setImageCanvas(kotlin.reflect.KFunction<? extends androidx.ui.graphics.Canvas> p);
+    method @Deprecated public void setNativeCanvas(kotlin.jvm.functions.Function1<? super android.graphics.Canvas,? extends androidx.ui.graphics.Canvas> p);
+    method @Deprecated public void setPaint(kotlin.jvm.functions.Function0<? extends androidx.ui.graphics.Paint> p);
+    method @Deprecated public void setPath(kotlin.jvm.functions.Function0<? extends androidx.ui.graphics.Path> p);
+    property public final kotlin.jvm.functions.Function0<androidx.ui.graphics.InternalCanvasHolder> canvasHolder;
+    property public final kotlin.reflect.KFunction<androidx.ui.graphics.Canvas> imageCanvas;
+    property public final kotlin.jvm.functions.Function1<android.graphics.Canvas,androidx.ui.graphics.Canvas> nativeCanvas;
+    property public final kotlin.jvm.functions.Function0<androidx.ui.graphics.Paint> paint;
+    property public final kotlin.jvm.functions.Function0<androidx.ui.graphics.Path> path;
+    field @Deprecated public static final androidx.ui.graphics.GraphicsFactory INSTANCE;
+  }
+
+  @Deprecated public static final class GraphicsFactory.Shader {
+    method @Deprecated public kotlin.reflect.KFunction<android.graphics.Shader> getImage();
+    method @Deprecated public kotlin.reflect.KFunction<android.graphics.Shader> getLinear();
+    method @Deprecated public kotlin.reflect.KFunction<android.graphics.Shader> getRadial();
+    method @Deprecated public void setImage(kotlin.reflect.KFunction<? extends android.graphics.Shader> p);
+    method @Deprecated public void setLinear(kotlin.reflect.KFunction<? extends android.graphics.Shader> p);
+    method @Deprecated public void setRadial(kotlin.reflect.KFunction<? extends android.graphics.Shader> p);
+    property public final kotlin.reflect.KFunction<android.graphics.Shader> image;
+    property public final kotlin.reflect.KFunction<android.graphics.Shader> linear;
+    property public final kotlin.reflect.KFunction<android.graphics.Shader> radial;
+    field @Deprecated public static final androidx.ui.graphics.GraphicsFactory.Shader INSTANCE;
+  }
+
   public interface ImageAsset {
     method public androidx.ui.graphics.colorspace.ColorSpace getColorSpace();
     method public androidx.ui.graphics.ImageAssetConfig getConfig();
@@ -303,6 +380,14 @@
     method public static androidx.ui.graphics.PixelMap toPixelMap(androidx.ui.graphics.ImageAsset, int startX = 0, int startY = 0, int width = this.width, int height = this.height, int[] buffer = null(width * height), int bufferOffset = 0, int stride = width);
   }
 
+  @androidx.compose.InternalComposeApi public interface InternalCanvasHolder {
+    method public androidx.ui.graphics.Canvas getCanvas();
+    method public android.graphics.Canvas getInternalCanvas();
+    method public void setInternalCanvas(android.graphics.Canvas p);
+    property public abstract androidx.ui.graphics.Canvas canvas;
+    property public abstract android.graphics.Canvas internalCanvas;
+  }
+
   public final class LinearGradient extends androidx.ui.graphics.ShaderBrush {
     method public androidx.ui.graphics.LinearGradient copy(java.util.List<androidx.ui.graphics.Color> colors, java.util.List<java.lang.Float>? stops, float startX, float startY, float endX, float endY, androidx.ui.graphics.TileMode tileMode);
   }
@@ -338,14 +423,14 @@
     method public static void drawOutline-2ddATRk(androidx.ui.graphics.drawscope.DrawScope, androidx.ui.graphics.Outline outline, long color, @FloatRange(from=0.0, to=1.0) float alpha = 1.0f, androidx.ui.graphics.drawscope.DrawStyle style = Fill, androidx.ui.graphics.ColorFilter? colorFilter = null, androidx.ui.graphics.BlendMode blendMode = DrawScope.DefaultBlendMode);
   }
 
-  public final class Paint {
-    ctor public Paint();
+  public interface Paint {
     method public android.graphics.Paint asFrameworkPaint();
     method public float getAlpha();
     method public androidx.ui.graphics.BlendMode getBlendMode();
     method public long getColor();
     method public androidx.ui.graphics.ColorFilter? getColorFilter();
     method public androidx.ui.graphics.FilterQuality getFilterQuality();
+    method public android.graphics.PathEffect? getNativePathEffect();
     method public android.graphics.Shader? getShader();
     method public androidx.ui.graphics.StrokeCap getStrokeCap();
     method public androidx.ui.graphics.StrokeJoin getStrokeJoin();
@@ -353,30 +438,32 @@
     method public float getStrokeWidth();
     method public androidx.ui.graphics.PaintingStyle getStyle();
     method public boolean isAntiAlias();
-    method public void setAlpha(float value);
-    method public void setAntiAlias(boolean value);
-    method public void setBlendMode(androidx.ui.graphics.BlendMode value);
-    method public void setColor-QEYXlZo(long color);
-    method public void setColorFilter(androidx.ui.graphics.ColorFilter? value);
-    method public void setFilterQuality(androidx.ui.graphics.FilterQuality value);
-    method public void setShader(android.graphics.Shader? value);
-    method public void setStrokeCap(androidx.ui.graphics.StrokeCap value);
-    method public void setStrokeJoin(androidx.ui.graphics.StrokeJoin value);
-    method public void setStrokeMiterLimit(float value);
-    method public void setStrokeWidth(float value);
-    method public void setStyle(androidx.ui.graphics.PaintingStyle value);
-    property public final float alpha;
-    property public final androidx.ui.graphics.BlendMode blendMode;
-    property public final long color;
-    property public final androidx.ui.graphics.ColorFilter? colorFilter;
-    property public final androidx.ui.graphics.FilterQuality filterQuality;
-    property public final boolean isAntiAlias;
-    property public final android.graphics.Shader? shader;
-    property public final androidx.ui.graphics.StrokeCap strokeCap;
-    property public final androidx.ui.graphics.StrokeJoin strokeJoin;
-    property public final float strokeMiterLimit;
-    property public final float strokeWidth;
-    property public final androidx.ui.graphics.PaintingStyle style;
+    method public void setAlpha(float p);
+    method public void setAntiAlias(boolean p);
+    method public void setBlendMode(androidx.ui.graphics.BlendMode p);
+    method public void setColor-QEYXlZo(long p);
+    method public void setColorFilter(androidx.ui.graphics.ColorFilter? p);
+    method public void setFilterQuality(androidx.ui.graphics.FilterQuality p);
+    method public void setNativePathEffect(android.graphics.PathEffect? p);
+    method public void setShader(android.graphics.Shader? p);
+    method public void setStrokeCap(androidx.ui.graphics.StrokeCap p);
+    method public void setStrokeJoin(androidx.ui.graphics.StrokeJoin p);
+    method public void setStrokeMiterLimit(float p);
+    method public void setStrokeWidth(float p);
+    method public void setStyle(androidx.ui.graphics.PaintingStyle p);
+    property public abstract float alpha;
+    property public abstract androidx.ui.graphics.BlendMode blendMode;
+    property public abstract long color;
+    property public abstract androidx.ui.graphics.ColorFilter? colorFilter;
+    property public abstract androidx.ui.graphics.FilterQuality filterQuality;
+    property public abstract boolean isAntiAlias;
+    property public abstract android.graphics.PathEffect? nativePathEffect;
+    property public abstract android.graphics.Shader? shader;
+    property public abstract androidx.ui.graphics.StrokeCap strokeCap;
+    property public abstract androidx.ui.graphics.StrokeJoin strokeJoin;
+    property public abstract float strokeMiterLimit;
+    property public abstract float strokeWidth;
+    property public abstract androidx.ui.graphics.PaintingStyle style;
   }
 
   public final class PaintKt {
diff --git a/ui/ui-graphics/api/restricted_0.1.0-dev16.txt b/ui/ui-graphics/api/restricted_0.1.0-dev16.txt
index 342a4800..3942120 100644
--- a/ui/ui-graphics/api/restricted_0.1.0-dev16.txt
+++ b/ui/ui-graphics/api/restricted_0.1.0-dev16.txt
@@ -56,7 +56,52 @@
     method public static androidx.ui.graphics.ImageAsset imageFromResource(android.content.res.Resources res, int resId);
   }
 
+  public final class AndroidPaint implements androidx.ui.graphics.Paint {
+    ctor public AndroidPaint();
+    method public android.graphics.Paint asFrameworkPaint();
+    method public float getAlpha();
+    method public androidx.ui.graphics.BlendMode getBlendMode();
+    method public long getColor();
+    method public androidx.ui.graphics.ColorFilter? getColorFilter();
+    method public androidx.ui.graphics.FilterQuality getFilterQuality();
+    method public android.graphics.PathEffect? getNativePathEffect();
+    method public android.graphics.Shader? getShader();
+    method public androidx.ui.graphics.StrokeCap getStrokeCap();
+    method public androidx.ui.graphics.StrokeJoin getStrokeJoin();
+    method public float getStrokeMiterLimit();
+    method public float getStrokeWidth();
+    method public androidx.ui.graphics.PaintingStyle getStyle();
+    method public boolean isAntiAlias();
+    method public void setAlpha(float value);
+    method public void setAntiAlias(boolean value);
+    method public void setBlendMode(androidx.ui.graphics.BlendMode value);
+    method public void setColor-QEYXlZo(long color);
+    method public void setColorFilter(androidx.ui.graphics.ColorFilter? value);
+    method public void setFilterQuality(androidx.ui.graphics.FilterQuality value);
+    method public void setNativePathEffect(android.graphics.PathEffect? value);
+    method public void setShader(android.graphics.Shader? value);
+    method public void setStrokeCap(androidx.ui.graphics.StrokeCap value);
+    method public void setStrokeJoin(androidx.ui.graphics.StrokeJoin value);
+    method public void setStrokeMiterLimit(float value);
+    method public void setStrokeWidth(float value);
+    method public void setStyle(androidx.ui.graphics.PaintingStyle value);
+    property public float alpha;
+    property public androidx.ui.graphics.BlendMode blendMode;
+    property public long color;
+    property public androidx.ui.graphics.ColorFilter? colorFilter;
+    property public androidx.ui.graphics.FilterQuality filterQuality;
+    property public boolean isAntiAlias;
+    property public android.graphics.PathEffect? nativePathEffect;
+    property public android.graphics.Shader? shader;
+    property public androidx.ui.graphics.StrokeCap strokeCap;
+    property public androidx.ui.graphics.StrokeJoin strokeJoin;
+    property public float strokeMiterLimit;
+    property public float strokeWidth;
+    property public androidx.ui.graphics.PaintingStyle style;
+  }
+
   public final class AndroidPaintKt {
+    method public static androidx.ui.graphics.Paint Paint();
   }
 
   public final class AndroidPath implements androidx.ui.graphics.Path {
@@ -196,7 +241,7 @@
   public final class CanvasHolder {
     ctor public CanvasHolder();
     method public inline void drawInto(android.graphics.Canvas targetCanvas, kotlin.jvm.functions.Function1<? super androidx.ui.graphics.Canvas,kotlin.Unit> block);
-    field @kotlin.PublishedApi internal final androidx.ui.graphics.AndroidCanvas androidCanvas;
+    field @kotlin.PublishedApi internal final androidx.ui.graphics.InternalCanvasHolder internalHolder;
   }
 
   public final class CanvasKt {
@@ -306,6 +351,38 @@
   public final class Float16Kt {
   }
 
+  @Deprecated @androidx.compose.InternalComposeApi public final class GraphicsFactory {
+    method @Deprecated public kotlin.jvm.functions.Function0<androidx.ui.graphics.InternalCanvasHolder> getCanvasHolder();
+    method @Deprecated public kotlin.reflect.KFunction<androidx.ui.graphics.Canvas> getImageCanvas();
+    method @Deprecated public kotlin.jvm.functions.Function1<android.graphics.Canvas,androidx.ui.graphics.Canvas> getNativeCanvas();
+    method @Deprecated public kotlin.jvm.functions.Function0<androidx.ui.graphics.Paint> getPaint();
+    method @Deprecated public kotlin.jvm.functions.Function0<androidx.ui.graphics.Path> getPath();
+    method @Deprecated public void setCanvasHolder(kotlin.jvm.functions.Function0<? extends androidx.ui.graphics.InternalCanvasHolder> p);
+    method @Deprecated public void setImageCanvas(kotlin.reflect.KFunction<? extends androidx.ui.graphics.Canvas> p);
+    method @Deprecated public void setNativeCanvas(kotlin.jvm.functions.Function1<? super android.graphics.Canvas,? extends androidx.ui.graphics.Canvas> p);
+    method @Deprecated public void setPaint(kotlin.jvm.functions.Function0<? extends androidx.ui.graphics.Paint> p);
+    method @Deprecated public void setPath(kotlin.jvm.functions.Function0<? extends androidx.ui.graphics.Path> p);
+    property public final kotlin.jvm.functions.Function0<androidx.ui.graphics.InternalCanvasHolder> canvasHolder;
+    property public final kotlin.reflect.KFunction<androidx.ui.graphics.Canvas> imageCanvas;
+    property public final kotlin.jvm.functions.Function1<android.graphics.Canvas,androidx.ui.graphics.Canvas> nativeCanvas;
+    property public final kotlin.jvm.functions.Function0<androidx.ui.graphics.Paint> paint;
+    property public final kotlin.jvm.functions.Function0<androidx.ui.graphics.Path> path;
+    field @Deprecated public static final androidx.ui.graphics.GraphicsFactory INSTANCE;
+  }
+
+  @Deprecated public static final class GraphicsFactory.Shader {
+    method @Deprecated public kotlin.reflect.KFunction<android.graphics.Shader> getImage();
+    method @Deprecated public kotlin.reflect.KFunction<android.graphics.Shader> getLinear();
+    method @Deprecated public kotlin.reflect.KFunction<android.graphics.Shader> getRadial();
+    method @Deprecated public void setImage(kotlin.reflect.KFunction<? extends android.graphics.Shader> p);
+    method @Deprecated public void setLinear(kotlin.reflect.KFunction<? extends android.graphics.Shader> p);
+    method @Deprecated public void setRadial(kotlin.reflect.KFunction<? extends android.graphics.Shader> p);
+    property public final kotlin.reflect.KFunction<android.graphics.Shader> image;
+    property public final kotlin.reflect.KFunction<android.graphics.Shader> linear;
+    property public final kotlin.reflect.KFunction<android.graphics.Shader> radial;
+    field @Deprecated public static final androidx.ui.graphics.GraphicsFactory.Shader INSTANCE;
+  }
+
   public interface ImageAsset {
     method public androidx.ui.graphics.colorspace.ColorSpace getColorSpace();
     method public androidx.ui.graphics.ImageAssetConfig getConfig();
@@ -336,6 +413,14 @@
     method public static androidx.ui.graphics.PixelMap toPixelMap(androidx.ui.graphics.ImageAsset, int startX = 0, int startY = 0, int width = this.width, int height = this.height, int[] buffer = null(width * height), int bufferOffset = 0, int stride = width);
   }
 
+  @androidx.compose.InternalComposeApi public interface InternalCanvasHolder {
+    method public androidx.ui.graphics.Canvas getCanvas();
+    method public android.graphics.Canvas getInternalCanvas();
+    method public void setInternalCanvas(android.graphics.Canvas p);
+    property public abstract androidx.ui.graphics.Canvas canvas;
+    property public abstract android.graphics.Canvas internalCanvas;
+  }
+
   public final class LinearGradient extends androidx.ui.graphics.ShaderBrush {
     method public androidx.ui.graphics.LinearGradient copy(java.util.List<androidx.ui.graphics.Color> colors, java.util.List<java.lang.Float>? stops, float startX, float startY, float endX, float endY, androidx.ui.graphics.TileMode tileMode);
   }
@@ -371,14 +456,14 @@
     method public static void drawOutline-2ddATRk(androidx.ui.graphics.drawscope.DrawScope, androidx.ui.graphics.Outline outline, long color, @FloatRange(from=0.0, to=1.0) float alpha = 1.0f, androidx.ui.graphics.drawscope.DrawStyle style = Fill, androidx.ui.graphics.ColorFilter? colorFilter = null, androidx.ui.graphics.BlendMode blendMode = DrawScope.DefaultBlendMode);
   }
 
-  public final class Paint {
-    ctor public Paint();
+  public interface Paint {
     method public android.graphics.Paint asFrameworkPaint();
     method public float getAlpha();
     method public androidx.ui.graphics.BlendMode getBlendMode();
     method public long getColor();
     method public androidx.ui.graphics.ColorFilter? getColorFilter();
     method public androidx.ui.graphics.FilterQuality getFilterQuality();
+    method public android.graphics.PathEffect? getNativePathEffect();
     method public android.graphics.Shader? getShader();
     method public androidx.ui.graphics.StrokeCap getStrokeCap();
     method public androidx.ui.graphics.StrokeJoin getStrokeJoin();
@@ -386,30 +471,32 @@
     method public float getStrokeWidth();
     method public androidx.ui.graphics.PaintingStyle getStyle();
     method public boolean isAntiAlias();
-    method public void setAlpha(float value);
-    method public void setAntiAlias(boolean value);
-    method public void setBlendMode(androidx.ui.graphics.BlendMode value);
-    method public void setColor-QEYXlZo(long color);
-    method public void setColorFilter(androidx.ui.graphics.ColorFilter? value);
-    method public void setFilterQuality(androidx.ui.graphics.FilterQuality value);
-    method public void setShader(android.graphics.Shader? value);
-    method public void setStrokeCap(androidx.ui.graphics.StrokeCap value);
-    method public void setStrokeJoin(androidx.ui.graphics.StrokeJoin value);
-    method public void setStrokeMiterLimit(float value);
-    method public void setStrokeWidth(float value);
-    method public void setStyle(androidx.ui.graphics.PaintingStyle value);
-    property public final float alpha;
-    property public final androidx.ui.graphics.BlendMode blendMode;
-    property public final long color;
-    property public final androidx.ui.graphics.ColorFilter? colorFilter;
-    property public final androidx.ui.graphics.FilterQuality filterQuality;
-    property public final boolean isAntiAlias;
-    property public final android.graphics.Shader? shader;
-    property public final androidx.ui.graphics.StrokeCap strokeCap;
-    property public final androidx.ui.graphics.StrokeJoin strokeJoin;
-    property public final float strokeMiterLimit;
-    property public final float strokeWidth;
-    property public final androidx.ui.graphics.PaintingStyle style;
+    method public void setAlpha(float p);
+    method public void setAntiAlias(boolean p);
+    method public void setBlendMode(androidx.ui.graphics.BlendMode p);
+    method public void setColor-QEYXlZo(long p);
+    method public void setColorFilter(androidx.ui.graphics.ColorFilter? p);
+    method public void setFilterQuality(androidx.ui.graphics.FilterQuality p);
+    method public void setNativePathEffect(android.graphics.PathEffect? p);
+    method public void setShader(android.graphics.Shader? p);
+    method public void setStrokeCap(androidx.ui.graphics.StrokeCap p);
+    method public void setStrokeJoin(androidx.ui.graphics.StrokeJoin p);
+    method public void setStrokeMiterLimit(float p);
+    method public void setStrokeWidth(float p);
+    method public void setStyle(androidx.ui.graphics.PaintingStyle p);
+    property public abstract float alpha;
+    property public abstract androidx.ui.graphics.BlendMode blendMode;
+    property public abstract long color;
+    property public abstract androidx.ui.graphics.ColorFilter? colorFilter;
+    property public abstract androidx.ui.graphics.FilterQuality filterQuality;
+    property public abstract boolean isAntiAlias;
+    property public abstract android.graphics.PathEffect? nativePathEffect;
+    property public abstract android.graphics.Shader? shader;
+    property public abstract androidx.ui.graphics.StrokeCap strokeCap;
+    property public abstract androidx.ui.graphics.StrokeJoin strokeJoin;
+    property public abstract float strokeMiterLimit;
+    property public abstract float strokeWidth;
+    property public abstract androidx.ui.graphics.PaintingStyle style;
   }
 
   public final class PaintKt {
diff --git a/ui/ui-graphics/api/restricted_current.txt b/ui/ui-graphics/api/restricted_current.txt
index 342a4800..3942120 100644
--- a/ui/ui-graphics/api/restricted_current.txt
+++ b/ui/ui-graphics/api/restricted_current.txt
@@ -56,7 +56,52 @@
     method public static androidx.ui.graphics.ImageAsset imageFromResource(android.content.res.Resources res, int resId);
   }
 
+  public final class AndroidPaint implements androidx.ui.graphics.Paint {
+    ctor public AndroidPaint();
+    method public android.graphics.Paint asFrameworkPaint();
+    method public float getAlpha();
+    method public androidx.ui.graphics.BlendMode getBlendMode();
+    method public long getColor();
+    method public androidx.ui.graphics.ColorFilter? getColorFilter();
+    method public androidx.ui.graphics.FilterQuality getFilterQuality();
+    method public android.graphics.PathEffect? getNativePathEffect();
+    method public android.graphics.Shader? getShader();
+    method public androidx.ui.graphics.StrokeCap getStrokeCap();
+    method public androidx.ui.graphics.StrokeJoin getStrokeJoin();
+    method public float getStrokeMiterLimit();
+    method public float getStrokeWidth();
+    method public androidx.ui.graphics.PaintingStyle getStyle();
+    method public boolean isAntiAlias();
+    method public void setAlpha(float value);
+    method public void setAntiAlias(boolean value);
+    method public void setBlendMode(androidx.ui.graphics.BlendMode value);
+    method public void setColor-QEYXlZo(long color);
+    method public void setColorFilter(androidx.ui.graphics.ColorFilter? value);
+    method public void setFilterQuality(androidx.ui.graphics.FilterQuality value);
+    method public void setNativePathEffect(android.graphics.PathEffect? value);
+    method public void setShader(android.graphics.Shader? value);
+    method public void setStrokeCap(androidx.ui.graphics.StrokeCap value);
+    method public void setStrokeJoin(androidx.ui.graphics.StrokeJoin value);
+    method public void setStrokeMiterLimit(float value);
+    method public void setStrokeWidth(float value);
+    method public void setStyle(androidx.ui.graphics.PaintingStyle value);
+    property public float alpha;
+    property public androidx.ui.graphics.BlendMode blendMode;
+    property public long color;
+    property public androidx.ui.graphics.ColorFilter? colorFilter;
+    property public androidx.ui.graphics.FilterQuality filterQuality;
+    property public boolean isAntiAlias;
+    property public android.graphics.PathEffect? nativePathEffect;
+    property public android.graphics.Shader? shader;
+    property public androidx.ui.graphics.StrokeCap strokeCap;
+    property public androidx.ui.graphics.StrokeJoin strokeJoin;
+    property public float strokeMiterLimit;
+    property public float strokeWidth;
+    property public androidx.ui.graphics.PaintingStyle style;
+  }
+
   public final class AndroidPaintKt {
+    method public static androidx.ui.graphics.Paint Paint();
   }
 
   public final class AndroidPath implements androidx.ui.graphics.Path {
@@ -196,7 +241,7 @@
   public final class CanvasHolder {
     ctor public CanvasHolder();
     method public inline void drawInto(android.graphics.Canvas targetCanvas, kotlin.jvm.functions.Function1<? super androidx.ui.graphics.Canvas,kotlin.Unit> block);
-    field @kotlin.PublishedApi internal final androidx.ui.graphics.AndroidCanvas androidCanvas;
+    field @kotlin.PublishedApi internal final androidx.ui.graphics.InternalCanvasHolder internalHolder;
   }
 
   public final class CanvasKt {
@@ -306,6 +351,38 @@
   public final class Float16Kt {
   }
 
+  @Deprecated @androidx.compose.InternalComposeApi public final class GraphicsFactory {
+    method @Deprecated public kotlin.jvm.functions.Function0<androidx.ui.graphics.InternalCanvasHolder> getCanvasHolder();
+    method @Deprecated public kotlin.reflect.KFunction<androidx.ui.graphics.Canvas> getImageCanvas();
+    method @Deprecated public kotlin.jvm.functions.Function1<android.graphics.Canvas,androidx.ui.graphics.Canvas> getNativeCanvas();
+    method @Deprecated public kotlin.jvm.functions.Function0<androidx.ui.graphics.Paint> getPaint();
+    method @Deprecated public kotlin.jvm.functions.Function0<androidx.ui.graphics.Path> getPath();
+    method @Deprecated public void setCanvasHolder(kotlin.jvm.functions.Function0<? extends androidx.ui.graphics.InternalCanvasHolder> p);
+    method @Deprecated public void setImageCanvas(kotlin.reflect.KFunction<? extends androidx.ui.graphics.Canvas> p);
+    method @Deprecated public void setNativeCanvas(kotlin.jvm.functions.Function1<? super android.graphics.Canvas,? extends androidx.ui.graphics.Canvas> p);
+    method @Deprecated public void setPaint(kotlin.jvm.functions.Function0<? extends androidx.ui.graphics.Paint> p);
+    method @Deprecated public void setPath(kotlin.jvm.functions.Function0<? extends androidx.ui.graphics.Path> p);
+    property public final kotlin.jvm.functions.Function0<androidx.ui.graphics.InternalCanvasHolder> canvasHolder;
+    property public final kotlin.reflect.KFunction<androidx.ui.graphics.Canvas> imageCanvas;
+    property public final kotlin.jvm.functions.Function1<android.graphics.Canvas,androidx.ui.graphics.Canvas> nativeCanvas;
+    property public final kotlin.jvm.functions.Function0<androidx.ui.graphics.Paint> paint;
+    property public final kotlin.jvm.functions.Function0<androidx.ui.graphics.Path> path;
+    field @Deprecated public static final androidx.ui.graphics.GraphicsFactory INSTANCE;
+  }
+
+  @Deprecated public static final class GraphicsFactory.Shader {
+    method @Deprecated public kotlin.reflect.KFunction<android.graphics.Shader> getImage();
+    method @Deprecated public kotlin.reflect.KFunction<android.graphics.Shader> getLinear();
+    method @Deprecated public kotlin.reflect.KFunction<android.graphics.Shader> getRadial();
+    method @Deprecated public void setImage(kotlin.reflect.KFunction<? extends android.graphics.Shader> p);
+    method @Deprecated public void setLinear(kotlin.reflect.KFunction<? extends android.graphics.Shader> p);
+    method @Deprecated public void setRadial(kotlin.reflect.KFunction<? extends android.graphics.Shader> p);
+    property public final kotlin.reflect.KFunction<android.graphics.Shader> image;
+    property public final kotlin.reflect.KFunction<android.graphics.Shader> linear;
+    property public final kotlin.reflect.KFunction<android.graphics.Shader> radial;
+    field @Deprecated public static final androidx.ui.graphics.GraphicsFactory.Shader INSTANCE;
+  }
+
   public interface ImageAsset {
     method public androidx.ui.graphics.colorspace.ColorSpace getColorSpace();
     method public androidx.ui.graphics.ImageAssetConfig getConfig();
@@ -336,6 +413,14 @@
     method public static androidx.ui.graphics.PixelMap toPixelMap(androidx.ui.graphics.ImageAsset, int startX = 0, int startY = 0, int width = this.width, int height = this.height, int[] buffer = null(width * height), int bufferOffset = 0, int stride = width);
   }
 
+  @androidx.compose.InternalComposeApi public interface InternalCanvasHolder {
+    method public androidx.ui.graphics.Canvas getCanvas();
+    method public android.graphics.Canvas getInternalCanvas();
+    method public void setInternalCanvas(android.graphics.Canvas p);
+    property public abstract androidx.ui.graphics.Canvas canvas;
+    property public abstract android.graphics.Canvas internalCanvas;
+  }
+
   public final class LinearGradient extends androidx.ui.graphics.ShaderBrush {
     method public androidx.ui.graphics.LinearGradient copy(java.util.List<androidx.ui.graphics.Color> colors, java.util.List<java.lang.Float>? stops, float startX, float startY, float endX, float endY, androidx.ui.graphics.TileMode tileMode);
   }
@@ -371,14 +456,14 @@
     method public static void drawOutline-2ddATRk(androidx.ui.graphics.drawscope.DrawScope, androidx.ui.graphics.Outline outline, long color, @FloatRange(from=0.0, to=1.0) float alpha = 1.0f, androidx.ui.graphics.drawscope.DrawStyle style = Fill, androidx.ui.graphics.ColorFilter? colorFilter = null, androidx.ui.graphics.BlendMode blendMode = DrawScope.DefaultBlendMode);
   }
 
-  public final class Paint {
-    ctor public Paint();
+  public interface Paint {
     method public android.graphics.Paint asFrameworkPaint();
     method public float getAlpha();
     method public androidx.ui.graphics.BlendMode getBlendMode();
     method public long getColor();
     method public androidx.ui.graphics.ColorFilter? getColorFilter();
     method public androidx.ui.graphics.FilterQuality getFilterQuality();
+    method public android.graphics.PathEffect? getNativePathEffect();
     method public android.graphics.Shader? getShader();
     method public androidx.ui.graphics.StrokeCap getStrokeCap();
     method public androidx.ui.graphics.StrokeJoin getStrokeJoin();
@@ -386,30 +471,32 @@
     method public float getStrokeWidth();
     method public androidx.ui.graphics.PaintingStyle getStyle();
     method public boolean isAntiAlias();
-    method public void setAlpha(float value);
-    method public void setAntiAlias(boolean value);
-    method public void setBlendMode(androidx.ui.graphics.BlendMode value);
-    method public void setColor-QEYXlZo(long color);
-    method public void setColorFilter(androidx.ui.graphics.ColorFilter? value);
-    method public void setFilterQuality(androidx.ui.graphics.FilterQuality value);
-    method public void setShader(android.graphics.Shader? value);
-    method public void setStrokeCap(androidx.ui.graphics.StrokeCap value);
-    method public void setStrokeJoin(androidx.ui.graphics.StrokeJoin value);
-    method public void setStrokeMiterLimit(float value);
-    method public void setStrokeWidth(float value);
-    method public void setStyle(androidx.ui.graphics.PaintingStyle value);
-    property public final float alpha;
-    property public final androidx.ui.graphics.BlendMode blendMode;
-    property public final long color;
-    property public final androidx.ui.graphics.ColorFilter? colorFilter;
-    property public final androidx.ui.graphics.FilterQuality filterQuality;
-    property public final boolean isAntiAlias;
-    property public final android.graphics.Shader? shader;
-    property public final androidx.ui.graphics.StrokeCap strokeCap;
-    property public final androidx.ui.graphics.StrokeJoin strokeJoin;
-    property public final float strokeMiterLimit;
-    property public final float strokeWidth;
-    property public final androidx.ui.graphics.PaintingStyle style;
+    method public void setAlpha(float p);
+    method public void setAntiAlias(boolean p);
+    method public void setBlendMode(androidx.ui.graphics.BlendMode p);
+    method public void setColor-QEYXlZo(long p);
+    method public void setColorFilter(androidx.ui.graphics.ColorFilter? p);
+    method public void setFilterQuality(androidx.ui.graphics.FilterQuality p);
+    method public void setNativePathEffect(android.graphics.PathEffect? p);
+    method public void setShader(android.graphics.Shader? p);
+    method public void setStrokeCap(androidx.ui.graphics.StrokeCap p);
+    method public void setStrokeJoin(androidx.ui.graphics.StrokeJoin p);
+    method public void setStrokeMiterLimit(float p);
+    method public void setStrokeWidth(float p);
+    method public void setStyle(androidx.ui.graphics.PaintingStyle p);
+    property public abstract float alpha;
+    property public abstract androidx.ui.graphics.BlendMode blendMode;
+    property public abstract long color;
+    property public abstract androidx.ui.graphics.ColorFilter? colorFilter;
+    property public abstract androidx.ui.graphics.FilterQuality filterQuality;
+    property public abstract boolean isAntiAlias;
+    property public abstract android.graphics.PathEffect? nativePathEffect;
+    property public abstract android.graphics.Shader? shader;
+    property public abstract androidx.ui.graphics.StrokeCap strokeCap;
+    property public abstract androidx.ui.graphics.StrokeJoin strokeJoin;
+    property public abstract float strokeMiterLimit;
+    property public abstract float strokeWidth;
+    property public abstract androidx.ui.graphics.PaintingStyle style;
   }
 
   public final class PaintKt {
diff --git a/ui/ui-graphics/src/androidMain/kotlin/androidx/ui/graphics/AndroidCanvas.kt b/ui/ui-graphics/src/androidMain/kotlin/androidx/ui/graphics/AndroidCanvas.kt
index cea1469..e0c1c67 100644
--- a/ui/ui-graphics/src/androidMain/kotlin/androidx/ui/graphics/AndroidCanvas.kt
+++ b/ui/ui-graphics/src/androidMain/kotlin/androidx/ui/graphics/AndroidCanvas.kt
@@ -17,6 +17,7 @@
 package androidx.ui.graphics
 
 import android.graphics.Matrix
+import androidx.compose.InternalComposeApi
 import androidx.ui.geometry.Offset
 import androidx.ui.geometry.Rect
 import androidx.ui.graphics.vectormath.Matrix4
@@ -31,29 +32,62 @@
  * Create a new Canvas instance that targets its drawing commands
  * to the provided [ImageAsset]
  */
+@Suppress("DEPRECATION_ERROR")
+@OptIn(InternalComposeApi::class)
 internal actual fun ActualCanvas(image: ImageAsset): Canvas =
+    GraphicsFactory.imageCanvas(image)
+
+@Suppress("DEPRECATION_ERROR")
+@OptIn(InternalComposeApi::class)
+fun Canvas(c: android.graphics.Canvas): Canvas =
+    GraphicsFactory.nativeCanvas(c)
+
+internal fun AndroidImageCanvas(image: ImageAsset): Canvas =
     AndroidCanvas().apply {
         internalCanvas = android.graphics.Canvas(image.asAndroidBitmap())
     }
 
-fun Canvas(c: android.graphics.Canvas): Canvas =
+internal fun AndroidNativeCanvas(c: android.graphics.Canvas): Canvas =
     AndroidCanvas().apply { internalCanvas = c }
 
 /**
  * Holder class that is used to issue scoped calls to a [Canvas] from the framework
  * equivalent canvas without having to allocate an object on each draw call
  */
+@Suppress("DEPRECATION_ERROR")
+@OptIn(InternalComposeApi::class)
 class CanvasHolder {
-    @PublishedApi internal val androidCanvas = AndroidCanvas()
+    // TODO(demin): revert to the previous CanvasHolder implementation after we have real desktop
+    // implementation of :compose:ui:ui module
+    @PublishedApi internal val internalHolder = GraphicsFactory.canvasHolder()
 
     inline fun drawInto(targetCanvas: android.graphics.Canvas, block: Canvas.() -> Unit) {
-        val previousCanvas = androidCanvas.internalCanvas
-        androidCanvas.internalCanvas = targetCanvas
-        androidCanvas.block()
-        androidCanvas.internalCanvas = previousCanvas
+        val previousCanvas = internalHolder.internalCanvas
+        internalHolder.internalCanvas = targetCanvas
+        internalHolder.canvas.block()
+        internalHolder.internalCanvas = previousCanvas
     }
 }
 
+@InternalComposeApi
+@OptIn(InternalComposeApi::class)
+interface InternalCanvasHolder {
+    val canvas: Canvas
+    var internalCanvas: NativeCanvas
+}
+
+@OptIn(InternalComposeApi::class)
+internal class AndroidInternalCanvasHolder : InternalCanvasHolder {
+    private val _canvas = AndroidCanvas()
+    override val canvas: Canvas get() = _canvas
+
+    override var internalCanvas: NativeCanvas
+        get() = _canvas.internalCanvas
+        set(value) {
+            _canvas.internalCanvas = value
+        }
+}
+
 // Stub canvas instance used to keep the internal canvas parameter non-null during its
 // scoped usage and prevent unnecessary byte code null checks from being generated
 private val EmptyCanvas = android.graphics.Canvas()
diff --git a/ui/ui-graphics/src/androidMain/kotlin/androidx/ui/graphics/AndroidPaint.kt b/ui/ui-graphics/src/androidMain/kotlin/androidx/ui/graphics/AndroidPaint.kt
index e5137ff..ec83429 100644
--- a/ui/ui-graphics/src/androidMain/kotlin/androidx/ui/graphics/AndroidPaint.kt
+++ b/ui/ui-graphics/src/androidMain/kotlin/androidx/ui/graphics/AndroidPaint.kt
@@ -16,15 +16,114 @@
 
 package androidx.ui.graphics
 
+import androidx.compose.InternalComposeApi
+
 actual typealias NativePaint = android.graphics.Paint
-internal actual fun makeNativePaint() =
+
+@Suppress("DEPRECATION_ERROR")
+@OptIn(InternalComposeApi::class)
+actual fun Paint(): Paint = GraphicsFactory.paint()
+
+class AndroidPaint : Paint {
+
+    private var internalPaint = makeNativePaint()
+    private var _blendMode = BlendMode.srcOver
+    private var internalShader: Shader? = null
+    private var internalColorFilter: ColorFilter? = null
+
+    override fun asFrameworkPaint(): NativePaint = internalPaint
+
+    override var alpha: Float
+        get() = internalPaint.getNativeAlpha()
+        set(value) {
+            internalPaint.setNativeAlpha(value)
+        }
+
+    override var isAntiAlias: Boolean
+        get() = internalPaint.getNativeAntiAlias()
+        set(value) {
+            internalPaint.setNativeAntiAlias(value)
+        }
+
+    override var color: Color
+        get() = internalPaint.getNativeColor()
+        set(color) {
+            internalPaint.setNativeColor(color)
+        }
+
+    override var blendMode: BlendMode
+        get() = _blendMode
+        set(value) {
+            _blendMode = value
+            internalPaint.setNativeBlendMode(value)
+        }
+
+    override var style: PaintingStyle
+        get() = internalPaint.getNativeStyle()
+        set(value) {
+            internalPaint.setNativeStyle(value)
+        }
+
+    override var strokeWidth: Float
+        get() = internalPaint.getNativeStrokeWidth()
+        set(value) {
+            internalPaint.setNativeStrokeWidth(value)
+        }
+
+    override var strokeCap: StrokeCap
+        get() = internalPaint.getNativeStrokeCap()
+        set(value) {
+            internalPaint.setNativeStrokeCap(value)
+        }
+
+    override var strokeJoin: StrokeJoin
+        get() = internalPaint.getNativeStrokeJoin()
+        set(value) {
+            internalPaint.setNativeStrokeJoin(value)
+        }
+
+    override var strokeMiterLimit: Float
+        get() = internalPaint.getNativeStrokeMiterLimit()
+        set(value) {
+            internalPaint.setNativeStrokeMiterLimit(value)
+        }
+
+    // TODO(ianh): verify that the image drawing methods actually respect this
+    override var filterQuality: FilterQuality
+        get() = internalPaint.getNativeFilterQuality()
+        set(value) {
+            internalPaint.setNativeFilterQuality(value)
+        }
+
+    override var shader: Shader?
+        get() = internalShader
+        set(value) {
+            internalShader = value
+            internalPaint.setNativeShader(internalShader)
+        }
+
+    override var colorFilter: ColorFilter?
+        get() = internalColorFilter
+        set(value) {
+            internalColorFilter = value
+            internalPaint.setNativeColorFilter(value)
+        }
+
+    override var nativePathEffect: NativePathEffect? = null
+        set(value) {
+            internalPaint.setNativePathEffect(value)
+            field = value
+        }
+}
+
+internal fun makeNativePaint() =
     android.graphics.Paint(android.graphics.Paint.ANTI_ALIAS_FLAG)
 
-internal actual fun NativePaint.setNativeBlendMode(value: BlendMode) {
+internal fun NativePaint.setNativeBlendMode(value: BlendMode) {
     this.xfermode = android.graphics.PorterDuffXfermode(value.toPorterDuffMode())
 }
 
-internal actual fun NativePaint.setNativeColorFilter(value: ColorFilter?) {
+internal fun NativePaint.setNativeColorFilter(value: ColorFilter?) {
     if (value != null) {
         this.colorFilter = android.graphics.PorterDuffColorFilter(
                 value.color.toArgb(),
@@ -35,25 +134,25 @@
     }
 }
 
-internal actual fun NativePaint.getNativeAlpha() = this.alpha / 255f
+internal fun NativePaint.getNativeAlpha() = this.alpha / 255f
 
-internal actual fun NativePaint.setNativeAlpha(value: Float) {
+internal fun NativePaint.setNativeAlpha(value: Float) {
     this.alpha = kotlin.math.round(value * 255.0f).toInt()
 }
 
-internal actual fun NativePaint.getNativeAntiAlias(): Boolean = this.isAntiAlias
+internal fun NativePaint.getNativeAntiAlias(): Boolean = this.isAntiAlias
 
-internal actual fun NativePaint.setNativeAntiAlias(value: Boolean) {
+internal fun NativePaint.setNativeAntiAlias(value: Boolean) {
     this.isAntiAlias = value
 }
 
-internal actual fun NativePaint.getNativeColor(): Color = Color(this.color)
+internal fun NativePaint.getNativeColor(): Color = Color(this.color)
 
-internal actual fun NativePaint.setNativeColor(value: Color) {
+internal fun NativePaint.setNativeColor(value: Color) {
     this.color = value.toArgb()
 }
 
-internal actual fun NativePaint.setNativeStyle(value: PaintingStyle) {
+internal fun NativePaint.setNativeStyle(value: PaintingStyle) {
     // TODO(njawad): Platform also supports Paint.Style.FILL_AND_STROKE)
     this.style = when (value) {
         PaintingStyle.stroke -> android.graphics.Paint.Style.STROKE
@@ -61,26 +160,26 @@
     }
 }
 
-internal actual fun NativePaint.getNativeStyle() = when (this.style) {
+internal fun NativePaint.getNativeStyle() = when (this.style) {
     android.graphics.Paint.Style.STROKE -> PaintingStyle.stroke
     else -> PaintingStyle.fill
 }
 
-internal actual fun NativePaint.getNativeStrokeWidth(): Float =
+internal fun NativePaint.getNativeStrokeWidth(): Float =
     this.strokeWidth
 
-internal actual fun NativePaint.setNativeStrokeWidth(value: Float) {
+internal fun NativePaint.setNativeStrokeWidth(value: Float) {
     this.strokeWidth = value
 }
 
-internal actual fun NativePaint.getNativeStrokeCap(): StrokeCap = when (this.strokeCap) {
+internal fun NativePaint.getNativeStrokeCap(): StrokeCap = when (this.strokeCap) {
     android.graphics.Paint.Cap.BUTT -> StrokeCap.butt
     android.graphics.Paint.Cap.ROUND -> StrokeCap.round
     android.graphics.Paint.Cap.SQUARE -> StrokeCap.square
     else -> StrokeCap.butt
 }
 
-internal actual fun NativePaint.setNativeStrokeCap(value: StrokeCap) {
+internal fun NativePaint.setNativeStrokeCap(value: StrokeCap) {
     this.strokeCap = when (value) {
         StrokeCap.square -> android.graphics.Paint.Cap.SQUARE
         StrokeCap.round -> android.graphics.Paint.Cap.ROUND
@@ -88,7 +187,7 @@
     }
 }
 
-internal actual fun NativePaint.getNativeStrokeJoin(): StrokeJoin =
+internal fun NativePaint.getNativeStrokeJoin(): StrokeJoin =
     when (this.strokeJoin) {
         android.graphics.Paint.Join.MITER -> StrokeJoin.miter
         android.graphics.Paint.Join.BEVEL -> StrokeJoin.bevel
@@ -96,7 +195,7 @@
         else -> StrokeJoin.miter
     }
 
-internal actual fun NativePaint.setNativeStrokeJoin(value: StrokeJoin) {
+internal fun NativePaint.setNativeStrokeJoin(value: StrokeJoin) {
     this.strokeJoin = when (value) {
         StrokeJoin.miter -> android.graphics.Paint.Join.MITER
         StrokeJoin.bevel -> android.graphics.Paint.Join.BEVEL
@@ -104,14 +203,14 @@
     }
 }
 
-internal actual fun NativePaint.getNativeStrokeMiterLimit(): Float =
+internal fun NativePaint.getNativeStrokeMiterLimit(): Float =
     this.strokeMiter
 
-internal actual fun NativePaint.setNativeStrokeMiterLimit(value: Float) {
+internal fun NativePaint.setNativeStrokeMiterLimit(value: Float) {
     this.strokeMiter = value
 }
 
-internal actual fun NativePaint.getNativeFilterQuality(): FilterQuality =
+internal fun NativePaint.getNativeFilterQuality(): FilterQuality =
     if (!this.isFilterBitmap) {
         FilterQuality.none
     } else {
@@ -123,14 +222,14 @@
         FilterQuality.low
     }
 
-internal actual fun NativePaint.setNativeFilterQuality(value: FilterQuality) {
+internal fun NativePaint.setNativeFilterQuality(value: FilterQuality) {
     this.isFilterBitmap = value != FilterQuality.none
 }
 
-internal actual fun NativePaint.setNativeShader(value: Shader?) {
+internal fun NativePaint.setNativeShader(value: Shader?) {
     this.shader = value
 }
 
-internal actual fun NativePaint.setNativePathEffect(value: NativePathEffect?) {
+internal fun NativePaint.setNativePathEffect(value: NativePathEffect?) {
     this.pathEffect = value
 }
\ No newline at end of file
diff --git a/ui/ui-graphics/src/androidMain/kotlin/androidx/ui/graphics/AndroidPath.kt b/ui/ui-graphics/src/androidMain/kotlin/androidx/ui/graphics/AndroidPath.kt
index c6419d9..7fc3f27 100644
--- a/ui/ui-graphics/src/androidMain/kotlin/androidx/ui/graphics/AndroidPath.kt
+++ b/ui/ui-graphics/src/androidMain/kotlin/androidx/ui/graphics/AndroidPath.kt
@@ -16,13 +16,16 @@
 
 package androidx.ui.graphics
 
+import androidx.compose.InternalComposeApi
 import androidx.ui.core.toAndroidRect
 import androidx.ui.geometry.Offset
 import androidx.ui.geometry.RRect
 import androidx.ui.geometry.Rect
 import androidx.ui.graphics.vectormath.degrees
 
-actual fun Path(): Path = AndroidPath()
+@Suppress("DEPRECATION_ERROR")
+@OptIn(InternalComposeApi::class)
+actual fun Path(): Path = GraphicsFactory.path()
 
 actual typealias NativePathEffect = android.graphics.PathEffect
 
diff --git a/ui/ui-graphics/src/androidMain/kotlin/androidx/ui/graphics/AndroidShader.kt b/ui/ui-graphics/src/androidMain/kotlin/androidx/ui/graphics/AndroidShader.kt
index 21e6bce..a77f4cd 100644
--- a/ui/ui-graphics/src/androidMain/kotlin/androidx/ui/graphics/AndroidShader.kt
+++ b/ui/ui-graphics/src/androidMain/kotlin/androidx/ui/graphics/AndroidShader.kt
@@ -16,19 +16,54 @@
 
 package androidx.ui.graphics
 
-import androidx.ui.geometry.Offset
 import android.graphics.BitmapShader
 import android.graphics.LinearGradient
 import android.graphics.RadialGradient
+import androidx.compose.InternalComposeApi
+import androidx.ui.geometry.Offset
 
 actual typealias Shader = android.graphics.Shader
 
+@Suppress("DEPRECATION_ERROR")
+@OptIn(InternalComposeApi::class)
 internal actual fun ActualLinearGradientShader(
     from: Offset,
     to: Offset,
     colors: List<Color>,
     colorStops: List<Float>?,
     tileMode: TileMode
+): Shader = GraphicsFactory.Shader.linear(
+    from, to, colors, colorStops, tileMode
+)
+
+@Suppress("DEPRECATION_ERROR")
+@OptIn(InternalComposeApi::class)
+internal actual fun ActualRadialGradientShader(
+    center: Offset,
+    radius: Float,
+    colors: List<Color>,
+    colorStops: List<Float>?,
+    tileMode: TileMode
+): Shader = GraphicsFactory.Shader.radial(
+    center, radius, colors, colorStops, tileMode
+)
+
+@Suppress("DEPRECATION_ERROR")
+@OptIn(InternalComposeApi::class)
+internal actual fun ActualImageShader(
+    image: ImageAsset,
+    tileModeX: TileMode,
+    tileModeY: TileMode
+): Shader = GraphicsFactory.Shader.image(
+    image, tileModeX, tileModeY
+)
+
+internal fun AndroidLinearGradientShader(
+    from: Offset,
+    to: Offset,
+    colors: List<Color>,
+    colorStops: List<Float>?,
+    tileMode: TileMode
 ): Shader {
     validateColorStops(colors, colorStops)
     return LinearGradient(
@@ -42,7 +77,7 @@
         )
 }
 
-internal actual fun ActualRadialGradientShader(
+internal fun AndroidRadialGradientShader(
     center: Offset,
     radius: Float,
     colors: List<Color>,
@@ -60,7 +95,7 @@
         )
 }
 
-internal actual fun ActualImageShader(
+internal fun AndroidImageShader(
     image: ImageAsset,
     tileModeX: TileMode,
     tileModeY: TileMode
diff --git a/ui/ui-graphics/src/androidMain/kotlin/androidx/ui/graphics/GraphicsFactory.kt b/ui/ui-graphics/src/androidMain/kotlin/androidx/ui/graphics/GraphicsFactory.kt
new file mode 100644
index 0000000..e4bdbf7
--- /dev/null
+++ b/ui/ui-graphics/src/androidMain/kotlin/androidx/ui/graphics/GraphicsFactory.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.graphics
+
+import androidx.compose.InternalComposeApi
+
+// TODO(b/160916667): temporary workaround for ui-desktop. remove when full support of MPP will
+//  be in-place
+@Deprecated(
+    "Temporary workaround. Supposed to be used only in ui-desktop before MPP",
+    level = DeprecationLevel.ERROR
+)
+@InternalComposeApi
+object GraphicsFactory {
+    var nativeCanvas: (NativeCanvas) -> Canvas = ::AndroidNativeCanvas
+    var imageCanvas = ::AndroidImageCanvas
+    var canvasHolder: () -> InternalCanvasHolder = { AndroidInternalCanvasHolder() }
+    var paint: () -> Paint = { AndroidPaint() }
+    var path: () -> Path = { AndroidPath() }
+
+    object Shader {
+        var linear = ::AndroidLinearGradientShader
+        var radial = ::AndroidRadialGradientShader
+        var image = ::AndroidImageShader
+    }
+}
diff --git a/ui/ui-graphics/src/commonMain/kotlin/androidx/ui/graphics/Paint.kt b/ui/ui-graphics/src/commonMain/kotlin/androidx/ui/graphics/Paint.kt
index 27a35ad2..23fac38 100644
--- a/ui/ui-graphics/src/commonMain/kotlin/androidx/ui/graphics/Paint.kt
+++ b/ui/ui-graphics/src/commonMain/kotlin/androidx/ui/graphics/Paint.kt
@@ -23,59 +23,18 @@
 
 expect class NativePaint
 
-internal expect fun makeNativePaint(): NativePaint
+expect fun Paint(): Paint
 
-internal expect fun NativePaint.setNativeBlendMode(value: BlendMode)
-internal expect fun NativePaint.setNativeColorFilter(value: ColorFilter?)
-internal expect fun NativePaint.getNativeAlpha(): Float
-internal expect fun NativePaint.setNativeAlpha(value: Float)
-internal expect fun NativePaint.getNativeAntiAlias(): Boolean
-internal expect fun NativePaint.setNativeAntiAlias(value: Boolean)
-internal expect fun NativePaint.getNativeColor(): Color
-internal expect fun NativePaint.setNativeColor(value: Color)
-internal expect fun NativePaint.getNativeStyle(): PaintingStyle
-internal expect fun NativePaint.setNativeStyle(value: PaintingStyle)
-internal expect fun NativePaint.getNativeStrokeWidth(): Float
-internal expect fun NativePaint.setNativeStrokeWidth(value: Float)
-internal expect fun NativePaint.getNativeStrokeCap(): StrokeCap
-internal expect fun NativePaint.setNativeStrokeCap(value: StrokeCap)
-internal expect fun NativePaint.getNativeStrokeJoin(): StrokeJoin
-internal expect fun NativePaint.setNativeStrokeJoin(value: StrokeJoin)
-internal expect fun NativePaint.getNativeStrokeMiterLimit(): Float
-internal expect fun NativePaint.setNativeStrokeMiterLimit(value: Float)
-internal expect fun NativePaint.getNativeFilterQuality(): FilterQuality
-internal expect fun NativePaint.setNativeFilterQuality(value: FilterQuality)
-internal expect fun NativePaint.setNativeShader(value: Shader?)
-internal expect fun NativePaint.setNativePathEffect(value: NativePathEffect?)
-
-class Paint {
-
-    private var internalPaint = makeNativePaint()
-    private var _blendMode = BlendMode.srcOver
-    private var internalShader: Shader? = null
-    private var internalColorFilter: ColorFilter? = null
-
-    fun asFrameworkPaint(): NativePaint = internalPaint
+interface Paint {
+    fun asFrameworkPaint(): NativePaint
 
     var alpha: Float
-        get() = internalPaint.getNativeAlpha()
-        set(value) {
-            internalPaint.setNativeAlpha(value)
-        }
 
     // Whether to apply anti-aliasing to lines and images drawn on the
     // canvas.
     //
     // Defaults to true.
     var isAntiAlias: Boolean
-            get() = internalPaint.getNativeAntiAlias()
-            set(value) {
-                // We encode true as zero and false as one because the default value, which
-                // we always encode as zero, is true.
-                // final int encoded = value ? 0 : 1;
-                // _data.setInt32(_kIsAntiAliasOffset, encoded, _kFakeHostEndian);
-                internalPaint.setNativeAntiAlias(value)
-            }
 
     // The color to use when stroking or filling a shape.
     //
@@ -90,10 +49,6 @@
     // This color is not used when compositing. To colorize a layer, use
     // [colorFilter].
     var color: Color
-        get() = internalPaint.getNativeColor()
-        set(color) {
-            internalPaint.setNativeColor(color)
-        }
 
     // A blend mode to apply when a shape is drawn or a layer is composited.
     //
@@ -113,20 +68,11 @@
     //    the layer when [restore] is called.
     //  * [BlendMode], which discusses the user of [saveLayer] with [blendMode].
     var blendMode: BlendMode
-        get() = _blendMode
-        set(value) {
-            _blendMode = value
-            internalPaint.setNativeBlendMode(value)
-        }
 
     // Whether to paint inside shapes, the edges of shapes, or both.
     //
     // Defaults to [PaintingStyle.fill].
     var style: PaintingStyle
-        get() = internalPaint.getNativeStyle()
-        set(value) {
-            internalPaint.setNativeStyle(value)
-        }
 
     // How wide to make edges drawn when [style] is set to
     // [PaintingStyle.stroke]. The width is given in logical pixels measured in
@@ -134,20 +80,12 @@
     //
     // Defaults to 0.0, which correspond to a hairline width.
     var strokeWidth: Float
-        get() = internalPaint.getNativeStrokeWidth()
-        set(value) {
-            internalPaint.setNativeStrokeWidth(value)
-        }
 
     // The kind of finish to place on the end of lines drawn when
     // [style] is set to [PaintingStyle.stroke].
     //
     // Defaults to [StrokeCap.butt], i.e. no caps.
     var strokeCap: StrokeCap
-        get() = internalPaint.getNativeStrokeCap()
-        set(value) {
-            internalPaint.setNativeStrokeCap(value)
-        }
 
     // The kind of finish to place on the joins between segments.
     //
@@ -157,10 +95,6 @@
     // Defaults to [StrokeJoin.miter], i.e. sharp corners. See also
     // [strokeMiterLimit] to control when miters are replaced by bevels.
     var strokeJoin: StrokeJoin
-        get() = internalPaint.getNativeStrokeJoin()
-        set(value) {
-            internalPaint.setNativeStrokeJoin(value)
-        }
 
     // The limit for miters to be drawn on segments when the join is set to
     // [StrokeJoin.miter] and the [style] is set to [PaintingStyle.stroke]. If
@@ -173,21 +107,12 @@
     // Defaults to 4.0.  Using zero as a limit will cause a [StrokeJoin.bevel]
     // join to be used all the time.
     var strokeMiterLimit: Float
-        get() = internalPaint.getNativeStrokeMiterLimit()
-        set(value) {
-            internalPaint.setNativeStrokeMiterLimit(value)
-        }
 
     // Controls the performance vs quality trade-off to use when applying
     // when drawing images, as with [Canvas.drawImageRect]
     //
     // Defaults to [FilterQuality.none].
-    // TODO(ianh): verify that the image drawing methods actually respect this
     var filterQuality: FilterQuality
-        get() = internalPaint.getNativeFilterQuality()
-        set(value) {
-            internalPaint.setNativeFilterQuality(value)
-        }
 
     // The shader to use when stroking or filling a shape.
     //
@@ -200,11 +125,6 @@
     //  * [colorFilter], which overrides [shader].
     //  * [color], which is used if [shader] and [colorFilter] are null.
     var shader: Shader?
-        get() = internalShader
-        set(value) {
-            internalShader = value
-            internalPaint.setNativeShader(internalShader)
-        }
 
     // A color filter to apply when a shape is drawn or when a layer is
     // composited.
@@ -213,9 +133,6 @@
     //
     // When a shape is being drawn, [colorFilter] overrides [color] and [shader].
     var colorFilter: ColorFilter?
-        get() = internalColorFilter
-        set(value) {
-            internalColorFilter = value
-            internalPaint.setNativeColorFilter(value)
-        }
+
+    var nativePathEffect: NativePathEffect?
 }
diff --git a/ui/ui-graphics/src/commonMain/kotlin/androidx/ui/graphics/drawscope/DrawScope.kt b/ui/ui-graphics/src/commonMain/kotlin/androidx/ui/graphics/drawscope/DrawScope.kt
index da162d8..421ed7d 100644
--- a/ui/ui-graphics/src/commonMain/kotlin/androidx/ui/graphics/drawscope/DrawScope.kt
+++ b/ui/ui-graphics/src/commonMain/kotlin/androidx/ui/graphics/drawscope/DrawScope.kt
@@ -16,7 +16,6 @@
 
 package androidx.ui.graphics.drawscope
 
-import androidx.ui.util.annotation.FloatRange
 import androidx.ui.core.LayoutDirection
 import androidx.ui.geometry.Offset
 import androidx.ui.geometry.Radius
@@ -35,12 +34,12 @@
 import androidx.ui.graphics.PointMode
 import androidx.ui.graphics.StrokeCap
 import androidx.ui.graphics.StrokeJoin
-import androidx.ui.graphics.setNativePathEffect
 import androidx.ui.graphics.vectormath.Matrix4
 import androidx.ui.graphics.vectormath.degrees
 import androidx.ui.unit.Density
 import androidx.ui.unit.IntOffset
 import androidx.ui.unit.IntSize
+import androidx.ui.util.annotation.FloatRange
 
 /**
  * Simultaneously translate the [DrawScope] coordinate space by [left] and [top] as well as modify
@@ -1012,7 +1011,7 @@
                     strokeJoin = join
 
                     // TODO b/154550525 add PathEffect to Paint if necessary
-                    asFrameworkPaint().setNativePathEffect(pathEffect)
+                    nativePathEffect = pathEffect
                 }
             }
         }
@@ -1080,7 +1079,7 @@
             if (this.strokeMiterLimit != miter) this.strokeMiterLimit = miter
             if (this.strokeCap != cap) this.strokeCap = cap
             if (this.strokeJoin != join) this.strokeJoin = join
-            this.asFrameworkPaint().setNativePathEffect(pathEffect)
+            this.nativePathEffect = pathEffect
         }
 
     private fun configureStrokePaint(
@@ -1105,7 +1104,7 @@
             if (this.strokeMiterLimit != miter) this.strokeMiterLimit = miter
             if (this.strokeCap != cap) this.strokeCap = cap
             if (this.strokeJoin != join) this.strokeJoin = join
-            this.asFrameworkPaint().setNativePathEffect(pathEffect)
+            this.nativePathEffect = pathEffect
         }
 
     /**