{"id":2830,"date":"2022-10-20T12:31:03","date_gmt":"2022-10-20T19:31:03","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/surface-duo\/?p=2830"},"modified":"2022-10-20T12:31:03","modified_gmt":"2022-10-20T19:31:03","slug":"persisting-sharing-ink","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/surface-duo\/persisting-sharing-ink\/","title":{"rendered":"Persisting and sharing ink"},"content":{"rendered":"<p>\n  Hello Android developers!\n<\/p>\n<\/p>\n<p>\n  We have some updates to the <a href=\"https:\/\/github.com\/microsoft\/surface-duo-sdk\/tree\/main\/inksdk\">InkSDK<\/a> that we would like to share with you, as well as some general guidance on persisting ink and open ink standards.\n<\/p>\n<h2>Overview<\/h2>\n<\/p>\n<p>\n  While Android provides different canvases for rendering ink, there is little guidance on how to store that ink or what format it should be in when communicating with other apps. Let\u2019s dive into some topics surrounding this concept.\n<\/p>\n<h2>Persisting Ink<\/h2>\n<\/p>\n<p>\n  Rendering ink on a canvas is important, but how that data is pulled off the canvas and saved is equally as important.\n<\/p>\n<p>\n  In the previous InkSDK versions, developers have access to a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Bitmap\">bitmap<\/a> representation of the canvas, which is useful for sharing drawings with other apps. But what if you want to save the drawing and be able to edit it later? A bitmap does not provide this functionality.\n<\/p>\n<p>\n  To save and load notes without data or functionality loss, there are a couple of approaches to enable this. These approaches convert a canvas\u2019s ink to some other data type that can be saved locally.\n<\/p>\n<ol type=\"1\">\n<li>\n  Serializing the data into a string using Kotlin or Java serializers (often formatted as <a href=\"https:\/\/en.wikipedia.org\/wiki\/JSON\">JSON<\/a>)\n<\/li>\n<li>\n  Creating a custom parser that converts the data into a differently structured format<\/p>\n<ol type=\"a\">\n<li>\n    Converting to an open standard type like <a href=\"https:\/\/en.wikipedia.org\/wiki\/InkML\">InkML<\/a> is more complex, but may have some advantages that will be discussed below\n  <\/li>\n<\/ol>\n<\/li>\n<\/ol>\n<p>\n  Regardless of the developer\u2019s approach to saving the ink (e.g. saving in a file, saving in a <a href=\"https:\/\/developer.android.com\/training\/data-storage\/room\/\">Room database<\/a>, etc.), oftentimes only saving simple data types is supported. Anything with references to a <a href=\"https:\/\/developer.android.com\/reference\/android\/content\/Context\">Context<\/a>, <a href=\"https:\/\/developer.android.com\/reference\/android\/app\/Activity\">Activity<\/a>, or <a href=\"https:\/\/developer.android.com\/reference\/android\/app\/Application\">Application<\/a> are difficult to save without causing memory leaks.\n<\/p>\n<p>\n  The Brush class introduced in the InkSDK contains all the data needed to persist drawings without any data loss.\n<\/p>\n<p>\n  Some of the topics covered here, as well as some more info on persisting ink can be found in a <a href=\"https:\/\/github.com\/microsoft\/surface-duo-sdk\/discussions\/118\">discussion in our SDK repo<\/a>.\n<\/p>\n<h2>Open Ink Standards<\/h2>\n<\/p>\n<p>\n  Assuming that ink data is available within the application, can we now send it to other apps to be rendered? Not necessarily. To send data between any two apps, they need to both understand the data format. The same holds true with ink.\n<\/p>\n<p>\n  Just as there are <a href=\"https:\/\/en.wikipedia.org\/wiki\/Open_standard\">open standards<\/a> for transferring commonly encountered data like .png for images, there are open standards for ink.\n<\/p>\n<p>\n  To send ink between apps, both apps need to agree on a specific format. Since copy\/pasting ink between apps is still relatively uncommon in Android, many inking apps today opt not to accept ink formats, choosing to only accept bitmaps or screenshots of the ink instead. This approach has some key disadvantages, like the ones discussed on persisting ink using bitmaps.\n<\/p>\n<p>\n  While a bitmap may preserve what the ink looked like at a given point in time, a lot of data is lost. Data cannot be easily added to a drawing, so editing it becomes impossible. In a basic sense, exporting a drawing to another app as a bitmap declares the drawing as finished, with no room for additional edits.\n<\/p>\n<p>\n  <img decoding=\"async\" width=\"694\" height=\"438\" src=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/10\/word-image-2830-1.png\" class=\"wp-image-2831\" alt=\"Diagram illustrating exporting data as a bitmap\" srcset=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/10\/word-image-2830-1.png 694w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/10\/word-image-2830-1-300x189.png 300w\" sizes=\"(max-width: 694px) 100vw, 694px\" \/>\n<\/p>\n<p>\n  A different approach is to use an open ink standard like <a href=\"https:\/\/www.w3.org\/TR\/InkML\/\">InkML (Ink Markup Language)<\/a> for sending ink data. InkML is growing in popularity and has the advantage of being accepted by a growing number of web pages and other apps. It has room for additions and deletions, unlike bitmap representations.\n<\/p>\n<p>\n  By no means is InkML or any other open standard a complete replacement for sending bitmap representations of drawings. Implementing it in your app, however, can help enhance a user\u2019s overall inking experience.\n<\/p>\n<p>\n  <img decoding=\"async\" width=\"918\" height=\"392\" src=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/10\/word-image-2830-2.png\" class=\"wp-image-2832\" alt=\"Diagram illustrating exporting data with an open ink standard\" srcset=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/10\/word-image-2830-2.png 918w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/10\/word-image-2830-2-300x128.png 300w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/10\/word-image-2830-2-768x328.png 768w\" sizes=\"(max-width: 918px) 100vw, 918px\" \/>\n<\/p>\n<h2>Updates to the InkSDK<\/h2>\n<\/p>\n<p>\n  Now that we have some context, let\u2019s take a look at some changes that have been made to the InkSDK.\n<\/p>\n<p>\n  Developers can access ink data from the canvas now, allowing them to save and load data as they see fit. Ink data is formatted as a <a href=\"https:\/\/github.com\/microsoft\/surface-duo-sdk\/blob\/059a23684baff99de1293d1f54119b611a5765a3\/inksdk\/ink\/src\/main\/java\/com\/microsoft\/device\/ink\/InkView.kt#L112\">Brush data class<\/a>, which packages the necessary information that a single brush stroke may contain.\n<\/p>\n<pre>data class Brush(, \r\n    val color: Int,\r\n    val strokeWidth: Float,\r\n    val strokeWidthMax: Float,\r\n    val paintHandler: DynamicPaintHandler?,\r\n    val stroke: InputManager.ExtendedStroke\r\n)<\/pre>\n<p>\n  All of the fields in the Brush class are common things we\u2019ve seen in the inking canvas (<a href=\"https:\/\/github.com\/microsoft\/surface-duo-sdk\/blob\/main\/inksdk\/ink\/src\/main\/java\/com\/microsoft\/device\/ink\/InkView.kt\">InkView.kt<\/a>) in the past, but let\u2019s quickly cover their uses.\n<\/p>\n<ul>\n<li>\n    <code>color<\/code> \u2013 base color of the brush stroke\n  <\/li>\n<li>\n    <code>strokeWidth<\/code> \u2013 typical width of the stroke if no additional manipulations are made (like thinning or thickening the line based on pen pressure)\n  <\/li>\n<li>\n    <code>strokeWidthMax<\/code> \u2013 the maximum width constraint the stroke can be after additional manipulations (like thinning or thickening the line based on pen pressure)\n  <\/li>\n<li>\n    <code>paintHandler<\/code> \u2013 an optional class that describes custom changes to the brush stroke (e.g. highlighter, rainbow, patterned, etc)\n  <\/li>\n<li>\n    <code>stroke<\/code> \u2013 the (x,y) coordinates that define the brush stroke\u2019s path across the canvas\n  <\/li>\n<\/ul>\n<p>\n  Packaging all this information together, we can accurately recreate a line drawn on a canvas. Given an array of Brush objects, we can recreate an entire drawing.\n<\/p>\n<p>\n  Buttons have also been added to the <a href=\"https:\/\/github.com\/microsoft\/surface-duo-sdk\/tree\/main\/inksdk\/inksample\">InkSDK sample<\/a> app to demonstrate how to save\/load Brush data from\/to a canvas. \n<\/p>\n<p>\n  <img decoding=\"async\" width=\"783\" height=\"1099\" src=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/10\/word-image-2830-3.png\" class=\"wp-image-2833\" alt=\"Screenshot of an ink canvas with some color options and save\/load buttons\" srcset=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/10\/word-image-2830-3.png 783w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/10\/word-image-2830-3-214x300.png 214w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/10\/word-image-2830-3-730x1024.png 730w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/10\/word-image-2830-3-768x1078.png 768w\" sizes=\"(max-width: 783px) 100vw, 783px\" \/>\n<\/p>\n<p>\n  You can import these InkSDK changes into your project:\n<\/p>\n<pre>implementation \"com.microsoft.device:ink:1.0.0-alpha5\"<\/pre>\n<h2>How does these changes relate to persistence and open standards?<\/h2>\n<\/p>\n<p>\n  While the new InkSDK changes do not directly reference InkML in any way, we added the getter and setter methods for Brush data in the canvas with these concepts in mind. \n<\/p>\n<p>\n  Unlike the bitmaps, the Brush data available from the InkSDK can be converted to InkML or any other open standard (<strong>and back<\/strong>) without any data loss.\n<\/p>\n<p>\n  Circling back to persistence, InkML can be formatted as a string, which can easily be saved in any persistence implementation.\n<\/p>\n<p>\n  Overall, giving developers access to the Brush data contained in the canvas opens a lot of opportunities for developers to create a rich inking experience in their apps.\n<\/p>\n<h2>Contributing and joining the conversation<\/h2>\n<\/p>\n<p>\n  As mentioned above, this InkSDK update came directly from a <a href=\"https:\/\/github.com\/microsoft\/surface-duo-sdk\/discussions\/118\">discussion in our SDK GitHub repo<\/a>. Our team is always happy to work with you on feedback, suggestions, and potential contributions to our resources!\n<\/p>\n<p>\n  If you have any questions or would like to tell us about your dual-screen applications, use the\u00a0<a href=\"http:\/\/aka.ms\/SurfaceDuoSDK-Feedback\">feedback forum,<\/a> message us on Twitter\u00a0<a href=\"https:\/\/twitter.com\/surfaceduodev\">@surfaceduodev,<\/a> or start a discussion in one of our repos.\n<\/p>\n<p>\n  Finally, please join us every Friday on\u00a0<a href=\"https:\/\/twitch.tv\/surfaceduodev\">Twitch<\/a>\u00a0at 11am Pacific time to chat about Surface Duo developer topics!\u00a0<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hello Android developers! We have some updates to the InkSDK that we would like to share with you, as well as some general guidance on persisting ink and open ink standards. Overview While Android provides different canvases for rendering ink, there is little guidance on how to store that ink or what format it should [&hellip;]<\/p>\n","protected":false},"author":90683,"featured_media":2838,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[473],"class_list":["post-2830","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-surface-duo-sdk","tag-kotlin"],"acf":[],"blog_post_summary":"<p>Hello Android developers! We have some updates to the InkSDK that we would like to share with you, as well as some general guidance on persisting ink and open ink standards. Overview While Android provides different canvases for rendering ink, there is little guidance on how to store that ink or what format it should [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/posts\/2830","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/users\/90683"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/comments?post=2830"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/posts\/2830\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/media\/2838"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/media?parent=2830"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/categories?post=2830"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/tags?post=2830"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}