{"id":13688501,"url":"https://github.com/Harley-xk/MaLiang","last_synced_at":"2025-05-01T19:31:06.574Z","repository":{"id":38360242,"uuid":"83375298","full_name":"Harley-xk/MaLiang","owner":"Harley-xk","description":"iOS painting and drawing library based on Metal. 神笔马良有一支神笔（基于 Metal 的涂鸦绘图库）","archived":false,"fork":false,"pushed_at":"2020-12-16T05:07:16.000Z","size":4818,"stargazers_count":1478,"open_issues_count":52,"forks_count":208,"subscribers_count":37,"default_branch":"master","last_synced_at":"2025-04-08T11:14:42.219Z","etag":null,"topics":["doodle","drawing","handwriting","ios","metal","painting","swift"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Harley-xk.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-02-28T01:29:35.000Z","updated_at":"2025-04-01T14:44:53.000Z","dependencies_parsed_at":"2022-07-18T10:39:23.613Z","dependency_job_id":null,"html_url":"https://github.com/Harley-xk/MaLiang","commit_stats":null,"previous_names":[],"tags_count":40,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Harley-xk%2FMaLiang","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Harley-xk%2FMaLiang/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Harley-xk%2FMaLiang/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Harley-xk%2FMaLiang/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Harley-xk","download_url":"https://codeload.github.com/Harley-xk/MaLiang/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251932646,"owners_count":21667186,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["doodle","drawing","handwriting","ios","metal","painting","swift"],"created_at":"2024-08-02T15:01:15.479Z","updated_at":"2025-05-01T19:31:04.274Z","avatar_url":"https://github.com/Harley-xk.png","language":"Swift","funding_links":[],"categories":["Swift"],"sub_categories":[],"readme":"# ![Banner](Images/banner.png)\n\n[![CI Status](http://img.shields.io/travis/Harley-xk/MaLiang.svg)](https://travis-ci.org/Harley-xk/MaLiang)\n[![Platform](https://img.shields.io/cocoapods/p/MaLiang.svg?style=flat)](http://cocoapods.org/pods/MaLiang)\n[![Version](https://img.shields.io/cocoapods/v/MaLiang.svg)](http://cocoapods.org/pods/MaLiang)\n[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)\n[![Language](https://img.shields.io/badge/language-Swift%205-orange.svg)](https://swift.org)\n[![codebeat badge](https://codebeat.co/badges/438159fd-b5f9-43d4-a1d5-b07ba5e6cf03)](https://codebeat.co/projects/github-com-harley-xk-maliang-metal)\n[![License](https://img.shields.io/cocoapods/l/MaLiang.svg?style=flat)](http://cocoapods.org/pods/MaLiang)\n[![twitter](https://img.shields.io/badge/twitter-Harley--xk-blue.svg)](https://twitter.com/Harley86589)\n[![weibo](https://img.shields.io/badge/weibo-%E7%BE%A4%E6%98%9F%E9%99%A8%E8%90%BD-orange.svg)](https://weibo.com/u/1161848005)\n\n![icon](Images/icon-32.png) **MaLiang** is a painting framework based on [**Metal**](https://developer.apple.com/metal/). It supports drawing and handwriting with customized textures.\nThe name of \"MaLiang\" comes from a boy who had a magical brush in Chinese ancient fairy story.\n\n[Simplified Chinese](https://me.harley-xk.studio/posts/201805072231)\n\n☕️ If I have saved your time, [buy me a cup of coffee](DONATE.md)\n\n📱 App based on MaLiang is now avaliable on the [App Store](https://apps.apple.com/cn/app/id1462615532)\n\n## Features\n\n- [x] Lines with **Bezier Curve**\n- [x] Texture **Rotation**\n- [x] **Glowing** Lines\n- [x] **Chartlet** element (for image and any other content from an UIView snapshot)\n- [x] **Force** based Adjustment for stroke size\n- [x] ** Pencil** supported\n- [x] **3D Touch** supported\n- [x] **Undo** \u0026 **Redo**\n- [x] **Zoom** \u0026 **Scale**\n- [x] **Export** to image\n- [x] **Save** vector contents to disk\n- [x] support macOS **Catalyst**\n\n## Requirements\n\niOS 9.0, Swift 5 \u003c/br\u003e\n\nThe core painting module is based on Metal\u003c/br\u003e\n\nYou can simply make it compatible with lower version of iOS and swift by changing only serval lines of code.\n\n## Installation\n\n### CocoaPods\n\nMaLiang is available through [CocoaPods](http://cocoapods.org). To install\nit, simply add the following line to your `Podfile`:\n\n```ruby\npod 'MaLiang'\n```\n\nTo use the old OpenGL ES verion:\n\n```ruby\npod 'MaLiang', '~\u003e 1.1'\n```\n\n### Carthage\n\nTo integrate MaLiang into your Xcode project using [Carthage](https://github.com/Carthage/Carthage), specify it in your `Cartfile`:\n\n```ruby\ngithub \"Harley-xk/MaLiang\"\n```\n\nRun `carthage update` to build the framework and drag the built `MaLiang.framework` into your Xcode project.\n\n**Make sure to add `MaLiang.framework` to your target's `Embedded Binaries`**\n\n## Usage\n\nMaLiang is simple to use.\n\n1. import MaLiang\n2. enjoy painting!\n\n### Canvas\n\n```swift\nopen class Canvas: MetalView\n```\n\nA `Canvas` is the basic component of `MaLiang`. You will paint all things on it.\n`Canvas` extends from `MetalView`, whitch extends from `MTKView`. `MetalView` handles all the logic with MetalKit and hides them from you.\n\n`Canvas` can be simply created with xib or code.\n\n- with xib or storyboard, simply drag and drop an `UIView` object into your view controller and change it's class to `Canvas` and module to `MaLiang`\n- with code, just create with `init(frame:)` as any `UIView` you do before.\n\nNow, all things necessary is done!\n\n#### Snapshot\n\nYou can take snapshot on canvas now. Just call `snapshot` function on `Canvas` and you will get an optional `UIImage` object.\n\n### Brush\n\nWith all things done, you can do more with `Brush`!\n\n`Brush` is the key feature to `MaLiang`. It holds textures and colors, whitch makes it possiable to paint amazing things.\n\nRegister a `Brush` with image data or file to Canvas and paint with it:\n\n```swift\nlet path = Bundle.main.path(forResource: \"pencil\", ofType: \"png\")!\nlet pencil = try? canvas.registerBrush(with: URL(fileURLWithPath: path))\npencil?.use()\n```\n\n`Brush` have serval properties for you to custmize:\n\n```swift\n// opacity of texture, affects the darkness of stroke\n// set opacity to 1 may cause heavy aliasing\nopen var opacity: CGFloat = 0.3\n\n// width of stroke line in points\nopen var pointSize: CGFloat = 4\n\n// this property defines the minimum distance (measureed in points) of nearest two textures\n// defaults to 1, this means erery texture calculated will be rendered, dictance calculation will be skiped\nopen var pointStep: CGFloat = 1\n\n// sensitive of pointsize changed from force, from 0 - 1\nopen var forceSensitive: CGFloat = 0\n\n/// color of stroke\nopen var color: UIColor = .black\n\n// indicate if the stroke size in visual will be scaled along with the Canvas\n// defaults to false, the stroke size in visual will stay with the original value\nopen var scaleWithCanvas = false\n```\n\nWith all these properties, you can create you own brush as your imagination.\n\n#### Force \u0026 3D Touch\n\nMaLiang supports automatically adjustment of stroke size with painting force. 3D Touch is supported by default, and simulated force will be setup on devices those are not supporting this.\n\n`forceSensitive` is the property that force affects the storke size. It should be set between `0` to `1`. the smaller the value is, the less sensitive will be. if sets to `0`, then force will not affects the stroke size.\n\n### Chartlet\n\nChartlet elements are supported from 2.1.0. A chartlet must be registered to canvas with its' texture data. You can simply get image data from its' `pngData()` method.\n\n```swift\nlet data = UIImage(named: \"chartlet\").pngData()\nlet texture = try canvas.makeTexture(with: data)\n```\n\nYou can apply **rotation** to chartlet by passing a counter clockwise angle in radius when adding it to the canvas:\n\n```swift\ncanvas.renderChartlet(at: location, size: chartletSize, textureID: texture.id, rotation: angle)\n```\n\n#### Text\n\nText element can be rendered to canvas by the Chartlet feature. MaLiang leaves the work of text layout and styles to your self.\n\n- Firtst, put your text content to a label, a text view or any other customized views\n- Second, make it properly styled and layouted\n- Then Third, take a snapshot from that view.\n- Finally, now you shoud have an image to your text content, render this image to canvas using the Chartlet apis.\n\nRefer to the samples for more details.\n\n### CanvasData\n\n`CanvasData` is now configured by default. It holds all the data on the `Canvas`, and makes the **undo** and **redo** actions to be possiable. \u003c/br\u003e\nAnd you can implement your own **saving logic** with the data holds by `CanvasData`.\n\n### Saving\n\n🎉 You can save your paintings to disk now.\n\n```swift\n// 1. create an instance of `DataExporter` with your canvas:\nlet exporter = DataExporter(canvas: canvas)\n// 2. save to empty folders on disk:\nexporter.save(to: localPath, progress: progressHandler, result: resultHandler)\n\n// also you can use another synchronous method to do this work Synchronously\nexporter.saveSynchronously(to: locakPath, progress: progressHandler)\n```\n\nThen, contents of canvas and some document infomations will be saved to files in the directory you provided.\n\n**`MaLiang` does not zip the folders, you can implement your own archive Logics refer to the sample project**\n\n### Reading\n\nUse `DataImporter` to read data saved by `MaLiang` to your canvas:\n\n```Swift\nDataImporter.importData(from: localPath, to: canvas, progress: progressHandler, result: resultHandler)\n```\n\nAlso, the localPath passed into DataImporter must be a folder where your contents files place. If you are using your own archive logic, unzip the contents first by your own.\n\n## License\n\nMaLiang is available under the MIT license. See the LICENSE file for more info.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FHarley-xk%2FMaLiang","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FHarley-xk%2FMaLiang","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FHarley-xk%2FMaLiang/lists"}