├── .gitignore ├── test └── data │ ├── arcs.ivg │ ├── arcs.png │ ├── blank.ivg │ ├── blank.png │ ├── arcs.iconvg │ ├── blank.iconvg │ ├── cowbell.ivg │ ├── cowbell.png │ ├── favicon.ivg │ ├── favicon.png │ ├── gradient.ivg │ ├── gradient.png │ ├── cowbell.iconvg │ ├── elliptical.ivg │ ├── elliptical.png │ ├── favicon.iconvg │ ├── video-005.jpeg │ ├── elliptical.iconvg │ ├── favicon.pink.png │ ├── gradient.iconvg │ ├── lod-polygon.ivg │ ├── lod-polygon.png │ ├── action-info.iconvg │ ├── lod-polygon.64.png │ ├── lod-polygon.iconvg │ ├── action-info.hires.ivg │ ├── action-info.hires.png │ ├── action-info.lores.ivg │ ├── action-info.lores.png │ ├── video-005.primitive.ivg │ ├── video-005.primitive.png │ ├── blank.iconvg.disassembly │ ├── blank.ivg.disassembly │ ├── video-005.primitive.iconvg │ ├── action-info.svg │ ├── action-info.iconvg.disassembly │ ├── lod-polygon.ivg.disassembly │ ├── elliptical.iconvg.disassembly │ ├── action-info.hires.ivg.disassembly │ ├── action-info.lores.ivg.disassembly │ ├── lod-polygon.iconvg.disassembly │ ├── video-005.primitive.svg │ ├── README.txt │ ├── elliptical.ivg.disassembly │ ├── gradient.iconvg.disassembly │ ├── cowbell.svg │ ├── arcs.ivg.disassembly │ ├── arcs.iconvg.disassembly │ ├── favicon.svg │ └── gradient.ivg.disassembly ├── go.mod ├── src ├── dart │ ├── test │ │ ├── goldens │ │ │ ├── arcs.1024x128.png │ │ │ ├── arcs.128x1024.png │ │ │ ├── arcs.512x512.png │ │ │ ├── arcs.default.png │ │ │ ├── favicon.pink.png │ │ │ ├── favicon.teal.png │ │ │ ├── cowbell.default.png │ │ │ ├── favicon.default.png │ │ │ ├── gradient.default.png │ │ │ ├── lod-polygon.10.png │ │ │ ├── lod-polygon.200.png │ │ │ ├── lod-polygon.75.png │ │ │ ├── lod-polygon.85.png │ │ │ ├── elliptical.default.png │ │ │ ├── elliptical.rotated.aa.png │ │ │ ├── lod-polygon.75.green.png │ │ │ ├── lod-polygon.85.green.png │ │ │ ├── lod-polygon.default.png │ │ │ ├── action-info.hires.teal.png │ │ │ ├── action-info.lores.teal.png │ │ │ ├── action-info.hires.default.png │ │ │ ├── action-info.hires.orange.png │ │ │ ├── action-info.lores.default.png │ │ │ ├── action-info.lores.orange.png │ │ │ ├── elliptical.rotated.default.png │ │ │ ├── elliptical.rotated.noclip.png │ │ │ ├── video-005-primitive.default.png │ │ │ ├── video-005-primitive.rotated.aa.png │ │ │ ├── elliptical.rotated.aaWithSaveLayer.png │ │ │ ├── video-005-primitive.rotated.default.png │ │ │ ├── video-005-primitive.rotated.noclip.png │ │ │ └── video-005-primitive.rotated.aaWithSaveLayer.png │ │ └── iconvg_test.dart │ ├── .metadata │ ├── pubspec.yaml │ ├── analysis_options.yaml │ ├── README.md │ ├── .gitignore │ ├── lib │ │ └── widgets.dart │ └── LICENSE ├── c │ ├── README.md │ ├── aaa_package.h │ ├── rectangle.c │ ├── matrix.c │ ├── error.c │ ├── broken.c │ ├── aaa_private.h │ ├── color.c │ ├── paint.c │ └── debug.c └── go │ └── lowlevel │ ├── disassemble.go │ ├── buffer.go │ ├── color.go │ └── lowlevel.go ├── iconvg-root-directory.txt ├── .github ├── PULL_REQUEST_TEMPLATE.md └── ISSUE_TEMPLATE.md ├── AUTHORS ├── doc ├── contributing.md └── code-of-conduct.md ├── CONTRIBUTORS ├── cmd └── iconvg-disassemble │ └── iconvg-disassemble.go ├── script ├── print-one-byte-colors.go └── gen-release-c.go ├── README.md ├── go.sum └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | *.exe 2 | *.out 3 | *~ 4 | /gen 5 | -------------------------------------------------------------------------------- /test/data/arcs.ivg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/test/data/arcs.ivg -------------------------------------------------------------------------------- /test/data/arcs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/test/data/arcs.png -------------------------------------------------------------------------------- /test/data/blank.ivg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/test/data/blank.ivg -------------------------------------------------------------------------------- /test/data/blank.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/test/data/blank.png -------------------------------------------------------------------------------- /test/data/arcs.iconvg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/test/data/arcs.iconvg -------------------------------------------------------------------------------- /test/data/blank.iconvg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/test/data/blank.iconvg -------------------------------------------------------------------------------- /test/data/cowbell.ivg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/test/data/cowbell.ivg -------------------------------------------------------------------------------- /test/data/cowbell.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/test/data/cowbell.png -------------------------------------------------------------------------------- /test/data/favicon.ivg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/test/data/favicon.ivg -------------------------------------------------------------------------------- /test/data/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/test/data/favicon.png -------------------------------------------------------------------------------- /test/data/gradient.ivg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/test/data/gradient.ivg -------------------------------------------------------------------------------- /test/data/gradient.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/test/data/gradient.png -------------------------------------------------------------------------------- /test/data/cowbell.iconvg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/test/data/cowbell.iconvg -------------------------------------------------------------------------------- /test/data/elliptical.ivg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/test/data/elliptical.ivg -------------------------------------------------------------------------------- /test/data/elliptical.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/test/data/elliptical.png -------------------------------------------------------------------------------- /test/data/favicon.iconvg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/test/data/favicon.iconvg -------------------------------------------------------------------------------- /test/data/video-005.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/test/data/video-005.jpeg -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/google/iconvg 2 | 3 | go 1.17 4 | 5 | require golang.org/x/image v0.18.0 6 | -------------------------------------------------------------------------------- /test/data/elliptical.iconvg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/test/data/elliptical.iconvg -------------------------------------------------------------------------------- /test/data/favicon.pink.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/test/data/favicon.pink.png -------------------------------------------------------------------------------- /test/data/gradient.iconvg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/test/data/gradient.iconvg -------------------------------------------------------------------------------- /test/data/lod-polygon.ivg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/test/data/lod-polygon.ivg -------------------------------------------------------------------------------- /test/data/lod-polygon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/test/data/lod-polygon.png -------------------------------------------------------------------------------- /test/data/action-info.iconvg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/test/data/action-info.iconvg -------------------------------------------------------------------------------- /test/data/lod-polygon.64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/test/data/lod-polygon.64.png -------------------------------------------------------------------------------- /test/data/lod-polygon.iconvg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/test/data/lod-polygon.iconvg -------------------------------------------------------------------------------- /test/data/action-info.hires.ivg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/test/data/action-info.hires.ivg -------------------------------------------------------------------------------- /test/data/action-info.hires.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/test/data/action-info.hires.png -------------------------------------------------------------------------------- /test/data/action-info.lores.ivg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/test/data/action-info.lores.ivg -------------------------------------------------------------------------------- /test/data/action-info.lores.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/test/data/action-info.lores.png -------------------------------------------------------------------------------- /test/data/video-005.primitive.ivg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/test/data/video-005.primitive.ivg -------------------------------------------------------------------------------- /test/data/video-005.primitive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/test/data/video-005.primitive.png -------------------------------------------------------------------------------- /test/data/blank.iconvg.disassembly: -------------------------------------------------------------------------------- 1 | 8a 49 56 47 IconVG Magic Identifier 2 | 01 Number of metadata chunks: 0 3 | -------------------------------------------------------------------------------- /test/data/blank.ivg.disassembly: -------------------------------------------------------------------------------- 1 | 89 49 56 47 IconVG Magic identifier 2 | 00 Number of metadata chunks: 0 3 | -------------------------------------------------------------------------------- /test/data/video-005.primitive.iconvg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/test/data/video-005.primitive.iconvg -------------------------------------------------------------------------------- /src/dart/test/goldens/arcs.1024x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/src/dart/test/goldens/arcs.1024x128.png -------------------------------------------------------------------------------- /src/dart/test/goldens/arcs.128x1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/src/dart/test/goldens/arcs.128x1024.png -------------------------------------------------------------------------------- /src/dart/test/goldens/arcs.512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/src/dart/test/goldens/arcs.512x512.png -------------------------------------------------------------------------------- /src/dart/test/goldens/arcs.default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/src/dart/test/goldens/arcs.default.png -------------------------------------------------------------------------------- /src/dart/test/goldens/favicon.pink.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/src/dart/test/goldens/favicon.pink.png -------------------------------------------------------------------------------- /src/dart/test/goldens/favicon.teal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/src/dart/test/goldens/favicon.teal.png -------------------------------------------------------------------------------- /src/dart/test/goldens/cowbell.default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/src/dart/test/goldens/cowbell.default.png -------------------------------------------------------------------------------- /src/dart/test/goldens/favicon.default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/src/dart/test/goldens/favicon.default.png -------------------------------------------------------------------------------- /src/dart/test/goldens/gradient.default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/src/dart/test/goldens/gradient.default.png -------------------------------------------------------------------------------- /src/dart/test/goldens/lod-polygon.10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/src/dart/test/goldens/lod-polygon.10.png -------------------------------------------------------------------------------- /src/dart/test/goldens/lod-polygon.200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/src/dart/test/goldens/lod-polygon.200.png -------------------------------------------------------------------------------- /src/dart/test/goldens/lod-polygon.75.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/src/dart/test/goldens/lod-polygon.75.png -------------------------------------------------------------------------------- /src/dart/test/goldens/lod-polygon.85.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/src/dart/test/goldens/lod-polygon.85.png -------------------------------------------------------------------------------- /src/dart/test/goldens/elliptical.default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/src/dart/test/goldens/elliptical.default.png -------------------------------------------------------------------------------- /src/dart/test/goldens/elliptical.rotated.aa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/src/dart/test/goldens/elliptical.rotated.aa.png -------------------------------------------------------------------------------- /src/dart/test/goldens/lod-polygon.75.green.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/src/dart/test/goldens/lod-polygon.75.green.png -------------------------------------------------------------------------------- /src/dart/test/goldens/lod-polygon.85.green.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/src/dart/test/goldens/lod-polygon.85.green.png -------------------------------------------------------------------------------- /src/dart/test/goldens/lod-polygon.default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/src/dart/test/goldens/lod-polygon.default.png -------------------------------------------------------------------------------- /src/dart/test/goldens/action-info.hires.teal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/src/dart/test/goldens/action-info.hires.teal.png -------------------------------------------------------------------------------- /src/dart/test/goldens/action-info.lores.teal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/src/dart/test/goldens/action-info.lores.teal.png -------------------------------------------------------------------------------- /src/dart/test/goldens/action-info.hires.default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/src/dart/test/goldens/action-info.hires.default.png -------------------------------------------------------------------------------- /src/dart/test/goldens/action-info.hires.orange.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/src/dart/test/goldens/action-info.hires.orange.png -------------------------------------------------------------------------------- /src/dart/test/goldens/action-info.lores.default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/src/dart/test/goldens/action-info.lores.default.png -------------------------------------------------------------------------------- /src/dart/test/goldens/action-info.lores.orange.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/src/dart/test/goldens/action-info.lores.orange.png -------------------------------------------------------------------------------- /src/dart/test/goldens/elliptical.rotated.default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/src/dart/test/goldens/elliptical.rotated.default.png -------------------------------------------------------------------------------- /src/dart/test/goldens/elliptical.rotated.noclip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/src/dart/test/goldens/elliptical.rotated.noclip.png -------------------------------------------------------------------------------- /src/dart/test/goldens/video-005-primitive.default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/src/dart/test/goldens/video-005-primitive.default.png -------------------------------------------------------------------------------- /src/dart/test/goldens/video-005-primitive.rotated.aa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/src/dart/test/goldens/video-005-primitive.rotated.aa.png -------------------------------------------------------------------------------- /src/dart/test/goldens/elliptical.rotated.aaWithSaveLayer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/src/dart/test/goldens/elliptical.rotated.aaWithSaveLayer.png -------------------------------------------------------------------------------- /src/dart/test/goldens/video-005-primitive.rotated.default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/src/dart/test/goldens/video-005-primitive.rotated.default.png -------------------------------------------------------------------------------- /src/dart/test/goldens/video-005-primitive.rotated.noclip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/src/dart/test/goldens/video-005-primitive.rotated.noclip.png -------------------------------------------------------------------------------- /src/dart/test/goldens/video-005-primitive.rotated.aaWithSaveLayer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/iconvg/HEAD/src/dart/test/goldens/video-005-primitive.rotated.aaWithSaveLayer.png -------------------------------------------------------------------------------- /iconvg-root-directory.txt: -------------------------------------------------------------------------------- 1 | This placeholder file indicates the root of the IconVG repository. 2 | 3 | For example, filenames like "test/data/cowbell.ivg" are relative to this root 4 | directory. 5 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Fixes # 2 | 3 | > It's a good idea to open an issue first for discussion. 4 | 5 | - [ ] Tests pass 6 | - [ ] Appropriate changes to README are included in PR -------------------------------------------------------------------------------- /test/data/action-info.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Expected Behavior 2 | 3 | 4 | ## Actual Behavior 5 | 6 | 7 | ## Steps to Reproduce the Problem 8 | 9 | 1. 10 | 1. 11 | 1. 12 | 13 | ## Specifications 14 | 15 | - Version: 16 | - Platform: -------------------------------------------------------------------------------- /src/dart/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 7311b9d6ac7b1292b141f118123f8d89e5d05120 8 | channel: master 9 | 10 | project_type: package 11 | -------------------------------------------------------------------------------- /src/dart/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: iconvg 2 | description: IconVG renderer 3 | version: 0.0.1 4 | homepage: 5 | 6 | environment: 7 | sdk: ">=2.12.0 <3.0.0" 8 | flutter: ">=1.17.0" 9 | 10 | dependencies: 11 | flutter: 12 | sdk: flutter 13 | vector_math: any 14 | 15 | dev_dependencies: 16 | flutter_test: 17 | sdk: flutter 18 | flutter_lints: ^1.0.0 19 | -------------------------------------------------------------------------------- /src/dart/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:flutter_lints/flutter.yaml 2 | 3 | # Additional information about this file can be found at 4 | # https://dart.dev/guides/language/analysis-options 5 | 6 | linter: 7 | rules: 8 | non_constant_identifier_names: false 9 | prefer_final_fields: true 10 | prefer_final_in_for_each: true 11 | prefer_final_locals: true 12 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | # This is the official list of IconVG authors for copyright purposes. 2 | # This file is distinct from the CONTRIBUTORS files. 3 | # See the latter for an explanation. 4 | 5 | # Names should be added to this file as one of 6 | # Organization's name 7 | # Individual's name 8 | # Individual's name 9 | # See CONTRIBUTORS for the meaning of multiple email addresses. 10 | 11 | # Please keep the list sorted. 12 | 13 | Google Inc. 14 | -------------------------------------------------------------------------------- /src/dart/README.md: -------------------------------------------------------------------------------- 1 | # iconvg 2 | 3 | IconVG renderer 4 | 5 | This package contains these potentially useful APIs: 6 | 7 | * The `IconVG` widget, which is like `IconImage` but for `IconVG` assets. 8 | 9 | * The `IconVGImage` widget, which is like `Image` but for `IconVG` assets. 10 | 11 | * The `RawIconVGImage` widget, which is like `IconVGImage` but works 12 | with raw bytes instead of an asset name. 13 | 14 | * The `IconVGFile` class, which parses IconVG files and provides a 15 | `Picture`. 16 | 17 | * The `PicturePainter` widget, which renders `Picture`s. 18 | 19 | The widgets are available via `package:iconvg/widgets.dart`, and the 20 | parser via `package:iconvg/decoder.dart`. 21 | -------------------------------------------------------------------------------- /src/c/README.md: -------------------------------------------------------------------------------- 1 | This directory's source code is *amalgamated*, similar to 2 | [SQLite](https://www.sqlite.org/amalgamation.html), to form a [single file C 3 | library](https://github.com/nothings/stb/blob/master/docs/stb_howto.txt). 4 | 5 | After editing any source code, run `script/gen-release-c.go` from the 6 | repository's top-level directory to produce the single `release/c/etc.c` file 7 | used by the example programs. 8 | 9 | During local development (but not for committing), an alternative approach 10 | avoids the need to run that script. After editing any source code, also modify 11 | the example programs to change lines like: 12 | 13 | ``` 14 | #include "../../release/c/etc.c" 15 | ``` 16 | 17 | to instead be: 18 | 19 | ``` 20 | #include "../../src/c/aaa_package.h" 21 | ``` 22 | -------------------------------------------------------------------------------- /test/data/action-info.iconvg.disassembly: -------------------------------------------------------------------------------- 1 | 8a 49 56 47 IconVG Magic Identifier 2 | 03 Number of metadata chunks: 1 3 | 0b Metadata chunk length: 5 4 | 11 Metadata Identifier: 8 (ViewBox) 5 | 51 -24 6 | 51 -24 7 | b1 +24 8 | b1 +24 9 | 35 #0000 ClosePath; MoveTo 10 | 81 +0 11 | 59 -20 12 | 33 #0001 Ellipse (4 quarters) 13 | 59 -20 14 | 81 +0 15 | 81 +0 16 | a9 +20 17 | 35 #0002 ClosePath; MoveTo 18 | 85 +2 19 | 95 +10 20 | 34 #0003 Parallelogram 21 | 7d -2 22 | 95 +10 23 | 7d -2 24 | 7d -2 25 | 35 #0004 ClosePath; MoveTo 26 | 85 +2 27 | 75 -6 28 | 34 #0005 Parallelogram 29 | 7d -2 30 | 75 -6 31 | 7d -2 32 | 6d -10 33 | 88 #0006 ClosePath; Fill (flat color) with REGS[SEL+8] 34 | -------------------------------------------------------------------------------- /doc/contributing.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | We'd love to accept your patches and contributions to this project. There are 4 | just a few small guidelines you need to follow. 5 | 6 | ## Contributor License Agreement 7 | 8 | Contributions to this project must be accompanied by a Contributor License 9 | Agreement. You (or your employer) retain the copyright to your contribution; 10 | this simply gives us permission to use and redistribute your contributions as 11 | part of the project. Head over to to see 12 | your current agreements on file or to sign a new one. 13 | 14 | You generally only need to submit a CLA once, so if you've already submitted one 15 | (even if it was for a different project), you probably don't need to do it 16 | again. 17 | 18 | ## Code Reviews 19 | 20 | All submissions, including submissions by project members, require review. We 21 | use GitHub pull requests for this purpose. Consult 22 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more 23 | information on using pull requests. 24 | 25 | ## Community Guidelines 26 | 27 | This project follows [Google's Open Source Community 28 | Guidelines](https://opensource.google/conduct/). 29 | -------------------------------------------------------------------------------- /src/c/aaa_package.h: -------------------------------------------------------------------------------- 1 | #ifndef ICONVG_INCLUDE_GUARD 2 | #define ICONVG_INCLUDE_GUARD 3 | 4 | // Copyright 2021 The IconVG Authors. 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // https://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | // ---- 19 | 20 | // IconVG ships as a "single file C library" or "header file library" as per 21 | // https://github.com/nothings/stb/blob/master/docs/stb_howto.txt 22 | // 23 | // To use that single file as a "foo.c"-like implementation, instead of a 24 | // "foo.h"-like header, #define ICONVG_IMPLEMENTATION before #include'ing or 25 | // compiling it. 26 | 27 | #include "./aaa_public.h" 28 | #ifdef ICONVG_IMPLEMENTATION 29 | #include "./aaa_private.h" 30 | #include "./broken.c" 31 | #include "./cairo.c" 32 | #include "./color.c" 33 | #include "./debug.c" 34 | #include "./decoder.c" 35 | #include "./error.c" 36 | #include "./matrix.c" 37 | #include "./paint.c" 38 | #include "./rectangle.c" 39 | #include "./skia.c" 40 | #endif // ICONVG_IMPLEMENTATION 41 | 42 | #endif // ICONVG_INCLUDE_GUARD 43 | -------------------------------------------------------------------------------- /CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | # This is the official list of people who can contribute 2 | # (and typically have contributed) code to the IconVG repository. 3 | # The AUTHORS file lists the copyright holders; this file 4 | # lists people. For example, Google employees are listed here 5 | # but not in AUTHORS, because Google holds the copyright. 6 | # 7 | # The submission process automatically checks to make sure 8 | # that people submitting code are listed in this file (by email address). 9 | # 10 | # Names should be added to this file only after verifying that 11 | # the individual or the individual's organization has agreed to 12 | # the appropriate Contributor License Agreement, found here: 13 | # 14 | # http://code.google.com/legal/individual-cla-v1.0.html 15 | # http://code.google.com/legal/corporate-cla-v1.0.html 16 | # 17 | # The agreement for individuals can be filled out on the web. 18 | # 19 | # When adding J Random Contributor's name to this file, 20 | # either J's name or J's organization's name should be 21 | # added to the AUTHORS file, depending on whether the 22 | # individual or corporate CLA was used. 23 | 24 | # Names should be added to this file like so: 25 | # Individual's name 26 | # Individual's name 27 | # 28 | # An entry with multiple email addresses specifies that the 29 | # first address should be used in the submit logs and 30 | # that the other addresses should be recognized as the 31 | # same person when interacting with Gerrit. 32 | 33 | # Please keep the list sorted. 34 | 35 | Ian Hickson 36 | Nigel Tao 37 | -------------------------------------------------------------------------------- /src/c/rectangle.c: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The IconVG Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "./aaa_private.h" 16 | 17 | // Note that iconvg_rectangle_f32 fields may be NaN, so that (min < max) is not 18 | // the same as !(min >= max). 19 | 20 | bool // 21 | iconvg_rectangle_f32__is_finite_and_not_empty( 22 | const iconvg_rectangle_f32* self) { 23 | return self && // 24 | (-INFINITY < self->min_x) && // 25 | (self->min_x < self->max_x) && // 26 | (self->max_x < +INFINITY) && // 27 | (-INFINITY < self->min_y) && // 28 | (self->min_y < self->max_y) && // 29 | (self->max_y < +INFINITY); 30 | } 31 | 32 | double // 33 | iconvg_rectangle_f32__width_f64(const iconvg_rectangle_f32* self) { 34 | if (self && (self->max_x > self->min_x)) { 35 | return ((double)self->max_x) - ((double)self->min_x); 36 | } 37 | return 0.0; 38 | } 39 | 40 | double // 41 | iconvg_rectangle_f32__height_f64(const iconvg_rectangle_f32* self) { 42 | if (self && (self->max_y > self->min_y)) { 43 | return ((double)self->max_y) - ((double)self->min_y); 44 | } 45 | return 0.0; 46 | } 47 | -------------------------------------------------------------------------------- /src/dart/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | .dart_tool/ 26 | .flutter-plugins 27 | .flutter-plugins-dependencies 28 | .packages 29 | .pub-cache/ 30 | .pub/ 31 | build/ 32 | 33 | # Android related 34 | **/android/**/gradle-wrapper.jar 35 | **/android/.gradle 36 | **/android/captures/ 37 | **/android/gradlew 38 | **/android/gradlew.bat 39 | **/android/local.properties 40 | **/android/**/GeneratedPluginRegistrant.java 41 | 42 | # iOS/XCode related 43 | **/ios/**/*.mode1v3 44 | **/ios/**/*.mode2v3 45 | **/ios/**/*.moved-aside 46 | **/ios/**/*.pbxuser 47 | **/ios/**/*.perspectivev3 48 | **/ios/**/*sync/ 49 | **/ios/**/.sconsign.dblite 50 | **/ios/**/.tags* 51 | **/ios/**/.vagrant/ 52 | **/ios/**/DerivedData/ 53 | **/ios/**/Icon? 54 | **/ios/**/Pods/ 55 | **/ios/**/.symlinks/ 56 | **/ios/**/profile 57 | **/ios/**/xcuserdata 58 | **/ios/.generated/ 59 | **/ios/Flutter/App.framework 60 | **/ios/Flutter/Flutter.framework 61 | **/ios/Flutter/Flutter.podspec 62 | **/ios/Flutter/Generated.xcconfig 63 | **/ios/Flutter/ephemeral 64 | **/ios/Flutter/app.flx 65 | **/ios/Flutter/app.zip 66 | **/ios/Flutter/flutter_assets/ 67 | **/ios/Flutter/flutter_export_environment.sh 68 | **/ios/ServiceDefinitions.json 69 | **/ios/Runner/GeneratedPluginRegistrant.* 70 | 71 | # Exceptions to above rules. 72 | !**/ios/**/default.mode1v3 73 | !**/ios/**/default.mode2v3 74 | !**/ios/**/default.pbxuser 75 | !**/ios/**/default.perspectivev3 76 | -------------------------------------------------------------------------------- /cmd/iconvg-disassemble/iconvg-disassemble.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The IconVG Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // ---------------- 16 | 17 | // iconvg-disassemble prints a human-readable disassembly of IconVG byte-code. 18 | // 19 | // Usage: iconvg-disassemble in.ivg > out.ivg.disassembly 20 | // in.ivg may be omitted, in which case stdin is read. 21 | package main 22 | 23 | import ( 24 | "fmt" 25 | "io" 26 | "os" 27 | 28 | "github.com/google/iconvg/src/go/lowlevel" 29 | ) 30 | 31 | func main() { 32 | if err := main1(); err != nil { 33 | os.Stderr.WriteString(err.Error() + "\n") 34 | os.Exit(1) 35 | } 36 | } 37 | 38 | func main1() error { 39 | cmd := "iconvg-disassemble" 40 | if len(os.Args) > 0 { 41 | cmd = os.Args[0] 42 | } 43 | 44 | data := []byte(nil) 45 | in := os.Stdin 46 | if len(os.Args) > 2 { 47 | return fmt.Errorf("Usage: %s in.ivg > out.ivg.disassembly\n"+ 48 | " in.ivg may be omitted, in which case stdin is read.", cmd) 49 | } else if len(os.Args) == 2 { 50 | if f, err := os.Open(os.Args[1]); err != nil { 51 | return err 52 | } else { 53 | defer f.Close() 54 | in = f 55 | } 56 | } 57 | data, err := io.ReadAll(in) 58 | if err != nil { 59 | return err 60 | } 61 | 62 | return lowlevel.Disassemble(os.Stdout, data) 63 | } 64 | -------------------------------------------------------------------------------- /script/print-one-byte-colors.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The IconVG Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build ignore 16 | 17 | package main 18 | 19 | // print-one-byte-colors.go prints IconVG's first 128 1 byte colors. 20 | 21 | import ( 22 | "fmt" 23 | "os" 24 | ) 25 | 26 | func main() { 27 | if err := main1(); err != nil { 28 | os.Stderr.WriteString(err.Error() + "\n") 29 | os.Exit(1) 30 | } 31 | } 32 | 33 | func main1() error { 34 | fmt.Printf("// iconvg_private_one_byte_colors holds the first 128 one-byte colors, in\n") 35 | fmt.Printf("// 0xAABBGGRR alpha-premultiplied format.\n") 36 | fmt.Printf("const uint32_t iconvg_private_one_byte_colors[128] = {\n") 37 | fmt.Printf(" 0x00000000, //\n") 38 | fmt.Printf(" 0x80808080, //\n") 39 | fmt.Printf(" 0xC0C0C0C0, //\n") 40 | for b := 0; b < 5; b++ { 41 | for g := 0; g < 5; g++ { 42 | for r := 0; r < 5; r++ { 43 | fmt.Printf(" 0xFF%02X%02X%02X, //\n", table[b], table[g], table[r]) 44 | } 45 | } 46 | } 47 | fmt.Printf("};\n\n") 48 | 49 | fmt.Printf("const iconvg_palette iconvg_private_default_palette = {{\n") 50 | for i := 0; i < 64; i++ { 51 | fmt.Printf(" {{0x00, 0x00, 0x00, 0xFF}}, //\n") 52 | } 53 | fmt.Printf("}};\n") 54 | 55 | return nil 56 | } 57 | 58 | var table = [5]uint8{0x00, 0x40, 0x80, 0xC0, 0xFF} 59 | -------------------------------------------------------------------------------- /src/go/lowlevel/disassemble.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The IconVG Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package lowlevel 16 | 17 | import ( 18 | "bufio" 19 | "fmt" 20 | "io" 21 | ) 22 | 23 | // Disassemble writes src's disassembly to w. 24 | // 25 | // See https://github.com/google/iconvg/blob/main/spec/iconvg-spec.md#example 26 | // (look for the text "annotated disassembly") or test/data/*.ivg.disassembly 27 | // for example output. 28 | func Disassemble(w io.Writer, src []byte) error { 29 | // Calling disassemble will make lots of small writes. If w is an 30 | // io.ByteWriter then assume that it is already buffered. Otherwise, wrap w 31 | // with our own buffering. 32 | if _, ok := w.(io.ByteWriter); ok { 33 | return disassemble(w, src) 34 | } 35 | bw := bufio.NewWriter(w) 36 | err0 := disassemble(bw, src) 37 | err1 := bw.Flush() 38 | if err0 != nil { 39 | return err0 40 | } 41 | return err1 42 | } 43 | 44 | func disassemble(w io.Writer, src []byte) error { 45 | var buf [14]byte 46 | p := func(b []byte, format string, args ...interface{}) { 47 | const hex = "0123456789abcdef" 48 | for i := range buf { 49 | buf[i] = ' ' 50 | } 51 | for i, x := range b { 52 | buf[3*i+0] = hex[x>>4] 53 | buf[3*i+1] = hex[x&0x0f] 54 | } 55 | w.Write(buf[:]) 56 | fmt.Fprintf(w, format, args...) 57 | } 58 | return decode(nil, p, nil, false, src, nil) 59 | } 60 | -------------------------------------------------------------------------------- /test/data/lod-polygon.ivg.disassembly: -------------------------------------------------------------------------------- 1 | 89 49 56 47 IconVG Magic identifier 2 | 00 Number of metadata chunks: 0 3 | c0 Start path, filled with CREG[CSEL-0]; M (absolute moveTo) 4 | 48 -28 5 | 58 -20 6 | e8 V (absolute vertical lineTo) 7 | 48 -28 8 | e6 H (absolute horizontal lineTo) 9 | 58 -20 10 | e1 z (closePath); end path 11 | c7 Set LOD 12 | 00 +0 13 | a0 +80 14 | c0 Start path, filled with CREG[CSEL-0]; M (absolute moveTo) 15 | b8 +28 16 | 80 +0 17 | 01 L (absolute lineTo), 2 reps 18 | 64 -14 19 | 41 98 +24.25 20 | L (absolute lineTo), implicit 21 | 64 -14 22 | c1 67 -24.25 23 | e1 z (closePath); end path 24 | c7 Set LOD 25 | a0 +80 26 | 03 00 80 7f +Inf 27 | c0 Start path, filled with CREG[CSEL-0]; M (absolute moveTo) 28 | b8 +28 29 | 80 +0 30 | 03 L (absolute lineTo), 4 reps 31 | a9 88 +8.65625 32 | a1 9a +26.625 33 | L (absolute lineTo), implicit 34 | 59 69 -22.65625 35 | 75 90 +16.453125 36 | L (absolute lineTo), implicit 37 | 59 69 -22.65625 38 | 8d 6f -16.453125 39 | L (absolute lineTo), implicit 40 | a9 88 +8.65625 41 | 61 65 -26.625 42 | e1 z (closePath); end path 43 | c7 Set LOD 44 | 00 +0 45 | 03 00 80 7f +Inf 46 | c0 Start path, filled with CREG[CSEL-0]; M (absolute moveTo) 47 | b8 +28 48 | a8 +20 49 | e8 V (absolute vertical lineTo) 50 | b8 +28 51 | e6 H (absolute horizontal lineTo) 52 | a8 +20 53 | e1 z (closePath); end path 54 | -------------------------------------------------------------------------------- /test/data/elliptical.iconvg.disassembly: -------------------------------------------------------------------------------- 1 | 8a 49 56 47 IconVG Magic Identifier 2 | 01 Number of metadata chunks: 0 3 | 35 #0000 ClosePath; MoveTo 4 | 41 -32 5 | 41 -32 6 | 34 #0001 Parallelogram 7 | c1 +32 8 | 41 -32 9 | c1 +32 10 | c1 +32 11 | 70 #0002 SEL -= 2; Set REGS[SEL+1 .. SEL+3] 12 | 00 00 00 00 lo32 = 0x0000_0000 13 | c0 00 00 ff hi32 = rgba(C0:00:00:FF) 14 | 00 00 01 00 lo32 = 0x0001_0000 15 | 00 00 c0 ff hi32 = rgba(00:00:C0:FF) 16 | a1 80 #0003 ClosePath; Fill (radial gradient; reflect) with REGS[SEL+1 .. SEL+3] 17 | ac aa aa bc -0.020833336 18 | ab aa 2a 3d +0.041666668 19 | 00 00 00 00 +0 20 | 88 88 08 3d +0.03333333 21 | 00 00 00 00 +0 22 | ab aa 2a 3f +0.6666667 23 | 36 02 #0004 SEL += 2 24 | 35 #0005 ClosePath; MoveTo 25 | 57 -21 26 | 6d -10 27 | 34 #0006 Parallelogram 28 | 59 -20 29 | 6b -11 30 | 5b -19 31 | 6d -10 32 | 57 #0007 Set REGS[SEL+7].hi32 33 | ff ff ff ff hi32 = rgba(FF:FF:FF:FF) 34 | 87 #0008 ClosePath; Fill (flat color) with REGS[SEL+7] 35 | 35 #0009 ClosePath; MoveTo 36 | 57 -21 37 | 9d +14 38 | 34 #0010 Parallelogram 39 | 59 -20 40 | 9b +13 41 | 5b -19 42 | 9d +14 43 | 87 #0011 ClosePath; Fill (flat color) with REGS[SEL+7] 44 | 35 #0012 ClosePath; MoveTo 45 | 93 +9 46 | 8b +5 47 | 34 #0013 Parallelogram 48 | 95 +10 49 | 89 +4 50 | 97 +11 51 | 8b +5 52 | 87 #0014 ClosePath; Fill (flat color) with REGS[SEL+7] 53 | -------------------------------------------------------------------------------- /test/data/action-info.hires.ivg.disassembly: -------------------------------------------------------------------------------- 1 | 89 49 56 47 IconVG Magic identifier 2 | 02 Number of metadata chunks: 1 3 | 0a Metadata chunk length: 5 4 | 00 Metadata Identifier: 0 (viewBox) 5 | 50 -24 6 | 50 -24 7 | b0 +24 8 | b0 +24 9 | c0 Start path, filled with CREG[CSEL-0]; M (absolute moveTo) 10 | 80 +0 11 | 58 -20 12 | a0 C (absolute cubeTo), 1 reps 13 | cf cc 30 c1 -11.049999 14 | 58 -20 15 | 58 -20 16 | cf cc 30 c1 -11.049999 17 | 58 -20 18 | 80 +0 19 | 91 s (relative smooth cubeTo), 2 reps 20 | 37 33 0f 41 +8.950001 21 | a8 +20 22 | a8 +20 23 | a8 +20 24 | s (relative smooth cubeTo), implicit 25 | a8 +20 26 | 37 33 0f c1 -8.950001 27 | a8 +20 28 | 58 -20 29 | 80 S (absolute smooth cubeTo), 1 reps 30 | cf cc 30 41 +11.049999 31 | 58 -20 32 | 80 +0 33 | 58 -20 34 | e3 z (closePath); m (relative moveTo) 35 | 84 +2 36 | bc +30 37 | e7 h (relative horizontal lineTo) 38 | 78 -4 39 | e8 V (absolute vertical lineTo) 40 | 7c -2 41 | e7 h (relative horizontal lineTo) 42 | 88 +4 43 | e9 v (relative vertical lineTo) 44 | 98 +12 45 | e3 z (closePath); m (relative moveTo) 46 | 80 +0 47 | 60 -16 48 | e7 h (relative horizontal lineTo) 49 | 78 -4 50 | e9 v (relative vertical lineTo) 51 | 78 -4 52 | e7 h (relative horizontal lineTo) 53 | 88 +4 54 | e9 v (relative vertical lineTo) 55 | 88 +4 56 | e1 z (closePath); end path 57 | -------------------------------------------------------------------------------- /test/data/action-info.lores.ivg.disassembly: -------------------------------------------------------------------------------- 1 | 89 49 56 47 IconVG Magic identifier 2 | 02 Number of metadata chunks: 1 3 | 0a Metadata chunk length: 5 4 | 00 Metadata Identifier: 0 (viewBox) 5 | 50 -24 6 | 50 -24 7 | b0 +24 8 | b0 +24 9 | c0 Start path, filled with CREG[CSEL-0]; M (absolute moveTo) 10 | 80 +0 11 | 58 -20 12 | a0 C (absolute cubeTo), 1 reps 13 | f5 74 -11.046875 14 | 58 -20 15 | 58 -20 16 | f5 74 -11.046875 17 | 58 -20 18 | 80 +0 19 | 91 s (relative smooth cubeTo), 2 reps 20 | f5 88 +8.953125 21 | a8 +20 22 | a8 +20 23 | a8 +20 24 | s (relative smooth cubeTo), implicit 25 | a8 +20 26 | 0d 77 -8.953125 27 | a8 +20 28 | 58 -20 29 | 80 S (absolute smooth cubeTo), 1 reps 30 | 0d 8b +11.046875 31 | 58 -20 32 | 80 +0 33 | 58 -20 34 | e3 z (closePath); m (relative moveTo) 35 | 84 +2 36 | bc +30 37 | e7 h (relative horizontal lineTo) 38 | 78 -4 39 | e8 V (absolute vertical lineTo) 40 | 7c -2 41 | e7 h (relative horizontal lineTo) 42 | 88 +4 43 | e9 v (relative vertical lineTo) 44 | 98 +12 45 | e3 z (closePath); m (relative moveTo) 46 | 80 +0 47 | 60 -16 48 | e7 h (relative horizontal lineTo) 49 | 78 -4 50 | e9 v (relative vertical lineTo) 51 | 78 -4 52 | e7 h (relative horizontal lineTo) 53 | 88 +4 54 | e9 v (relative vertical lineTo) 55 | 88 +4 56 | e1 z (closePath); end path 57 | -------------------------------------------------------------------------------- /test/data/lod-polygon.iconvg.disassembly: -------------------------------------------------------------------------------- 1 | 8a 49 56 47 IconVG Magic Identifier 2 | 01 Number of metadata chunks: 0 3 | 35 #0000 ClosePath; MoveTo 4 | 49 -28 5 | 59 -20 6 | 02 #0001 LineTo (2 reps) 7 | 49 -28 8 | 49 -28 9 | (rep) 10 | 59 -20 11 | 49 -28 12 | 88 #0002 ClosePath; Fill (flat color) with REGS[SEL+8] 13 | 3a #0003 Jump Level-of-Detail 14 | 07 Target: #0007 (PC+3) 15 | 81 +0 16 | 02 d0 +80 17 | 35 #0004 ClosePath; MoveTo 18 | b9 +28 19 | 81 +0 20 | 02 #0005 LineTo (2 reps) 21 | 65 -14 22 | 42 98 +24.25 23 | (rep) 24 | 65 -14 25 | c2 67 -24.25 26 | 88 #0006 ClosePath; Fill (flat color) with REGS[SEL+8] 27 | 3a #0007 Jump Level-of-Detail 28 | 07 Target: #0011 (PC+3) 29 | 02 d0 +80 30 | 00 00 80 7f +Inf 31 | 35 #0008 ClosePath; MoveTo 32 | b9 +28 33 | 81 +0 34 | 04 #0009 LineTo (4 reps) 35 | aa 88 +8.65625 36 | a2 9a +26.625 37 | (rep) 38 | 5a 69 -22.65625 39 | 76 90 +16.453125 40 | (rep) 41 | 5a 69 -22.65625 42 | 8e 6f -16.453125 43 | (rep) 44 | aa 88 +8.65625 45 | 62 65 -26.625 46 | 88 #0010 ClosePath; Fill (flat color) with REGS[SEL+8] 47 | 35 #0011 ClosePath; MoveTo 48 | b9 +28 49 | a9 +20 50 | 02 #0012 LineTo (2 reps) 51 | b9 +28 52 | b9 +28 53 | (rep) 54 | a9 +20 55 | b9 +28 56 | 88 #0013 ClosePath; Fill (flat color) with REGS[SEL+8] 57 | -------------------------------------------------------------------------------- /src/c/matrix.c: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The IconVG Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "./aaa_private.h" 16 | 17 | iconvg_matrix_2x3_f64 // 18 | iconvg_matrix_2x3_f64__inverse(iconvg_matrix_2x3_f64* self) { 19 | double inv = 1.0 / iconvg_matrix_2x3_f64__determinant(self); 20 | if (isinf(inv) || isnan(inv)) { 21 | return iconvg_matrix_2x3_f64__make(1.0, 0.0, 0.0, 0.0, 1.0, 0.0); 22 | } 23 | 24 | // https://ardoris.wordpress.com/2008/07/18/general-formula-for-the-inverse-of-a-3x3-matrix/ 25 | // recalling that self's implicit bottom row is [0, 0, 1]. 26 | double e02 = (self->elems[0][1] * self->elems[1][2]) - 27 | (self->elems[0][2] * self->elems[1][1]); 28 | double e12 = (self->elems[0][0] * self->elems[1][2]) - 29 | (self->elems[0][2] * self->elems[1][0]); 30 | return iconvg_matrix_2x3_f64__make(+inv * self->elems[1][1], // 31 | -inv * self->elems[0][1], // 32 | +inv * e02, // 33 | -inv * self->elems[1][0], // 34 | +inv * self->elems[0][0], // 35 | -inv * e12); // 36 | } 37 | 38 | void // 39 | iconvg_matrix_2x3_f64__override_second_row(iconvg_matrix_2x3_f64* self) { 40 | if (!self) { 41 | return; 42 | } 43 | if (self->elems[0][0] != 0.0) { 44 | self->elems[1][0] = 0.0; 45 | self->elems[1][1] = 1.0; 46 | } else if (self->elems[0][1] != 0.0) { 47 | self->elems[1][0] = 1.0; 48 | self->elems[1][1] = 0.0; 49 | } else { 50 | // 1e-10 is arbitrary but very small and squaring it still gives 51 | // something larger than FLT_MIN, approximately 1.175494e-38. 52 | self->elems[0][0] = 1e-10; 53 | self->elems[0][1] = 0.0; 54 | self->elems[1][0] = 0.0; 55 | self->elems[1][1] = 1e-10; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/c/error.c: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The IconVG Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "./aaa_private.h" 16 | 17 | const char iconvg_error_bad_coordinate[] = // 18 | "iconvg: bad coordinate"; 19 | const char iconvg_error_bad_jump[] = // 20 | "iconvg: bad jump"; 21 | const char iconvg_error_bad_magic_identifier[] = // 22 | "iconvg: bad magic identifier"; 23 | const char iconvg_error_bad_metadata[] = // 24 | "iconvg: bad metadata"; 25 | const char iconvg_error_bad_metadata_id_order[] = // 26 | "iconvg: bad metadata ID order"; 27 | const char iconvg_error_bad_metadata_suggested_palette[] = // 28 | "iconvg: bad metadata (suggested palette)"; 29 | const char iconvg_error_bad_metadata_viewbox[] = // 30 | "iconvg: bad metadata (viewbox)"; 31 | const char iconvg_error_bad_number[] = // 32 | "iconvg: bad number"; 33 | const char iconvg_error_bad_opcode_length[] = // 34 | "iconvg: bad opcode length"; 35 | 36 | const char iconvg_error_system_failure_out_of_memory[] = // 37 | "iconvg: system failure: out of memory"; 38 | 39 | const char iconvg_error_invalid_backend_not_enabled[] = // 40 | "iconvg: invalid backend (not enabled)"; 41 | const char iconvg_error_invalid_constructor_argument[] = // 42 | "iconvg: invalid constructor argument"; 43 | const char iconvg_error_invalid_paint_type[] = // 44 | "iconvg: invalid paint type"; 45 | const char iconvg_error_invalid_vtable[] = // 46 | "iconvg: invalid vtable"; 47 | 48 | // ---- 49 | 50 | bool // 51 | iconvg_error_is_file_format_error(const char* err_msg) { 52 | return (err_msg == iconvg_error_bad_coordinate) || 53 | (err_msg == iconvg_error_bad_jump) || 54 | (err_msg == iconvg_error_bad_magic_identifier) || 55 | (err_msg == iconvg_error_bad_metadata) || 56 | (err_msg == iconvg_error_bad_metadata_id_order) || 57 | (err_msg == iconvg_error_bad_metadata_suggested_palette) || 58 | (err_msg == iconvg_error_bad_metadata_viewbox) || 59 | (err_msg == iconvg_error_bad_number) || 60 | (err_msg == iconvg_error_bad_opcode_length); 61 | } 62 | -------------------------------------------------------------------------------- /test/data/video-005.primitive.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # IconVG 2 | 3 | IconVG is a compact, binary format for simple vector graphics: icons, logos, 4 | glyphs and emoji. 5 | 6 | **WARNING: THIS FORMAT IS EXPERIMENTAL AND SUBJECT TO INCOMPATIBLE CHANGES.** 7 | 8 | It is similar in concept to SVG (Scalable Vector Graphics) but much simpler. 9 | Compared to [SVG Tiny](https://www.w3.org/TR/SVGTiny12/), which isn't actually 10 | tiny, it does not have features for text, multimedia, interactivity, linking, 11 | scripting, animation, XSLT, DOM, combination with raster graphics such as JPEG 12 | formatted textures, etc. 13 | 14 | It is a format for efficient presentation, not an authoring format. For 15 | example, it does not provide grouping individual paths into higher level 16 | objects. Instead, the anticipated workflow is that artists use other tools and 17 | authoring formats like Inkscape and SVG, or commercial equivalents, and export 18 | IconVG versions of their assets, the same way that they would produce PNG 19 | versions of their vector art. It is not a goal to be able to recover the 20 | original SVG from a derived IconVG. 21 | 22 | It is not a pixel-exact format. Different implementations may produce slightly 23 | different renderings, due to implementation-specific rounding errors in the 24 | mathematical computations when rasterizing vector paths to pixels. Artifacts 25 | may appear when scaling up to extreme sizes, say 1 million by 1 million pixels. 26 | Nonetheless, at typical scales, e.g. up to 4096 × 4096, such differences are 27 | not expected to be perceptible to the naked eye. 28 | 29 | 30 | ## Example 31 | 32 | ![Cowbell image](./test/data/cowbell.png) 33 | 34 | - `cowbell.png` is 18555 bytes (256 × 256 pixels) 35 | - `cowbell.svg` is 4506 bytes 36 | - `cowbell.iconvg` is 1012 bytes (see also its 37 | [disassembly](./test/data/cowbell.iconvg.disassembly)) 38 | 39 | The [test/data](./test/data) directory holds these files and other examples. 40 | 41 | 42 | ## File Format 43 | 44 | - [IconVG Specification](spec/iconvg-spec.md) 45 | - Magic number: `0x8A 0x49 0x56 0x47`, which is `"\x8aIVG"`. 46 | - Suggested file extension: `.iconvg` 47 | - Suggested MIME type: `image/x-iconvg` 48 | 49 | 50 | ## Implementations 51 | 52 | This repository contains: 53 | 54 | - a decoder [written in C](./release/c) 55 | - a decoder [written in Dart](./src/dart), albeit for an [older (obsolete) 56 | version of the file format](https://github.com/google/iconvg/issues/4) 57 | - a low-level decoder [written in Go](./src/go). Low-level means that it 58 | outputs numbers (vector coordinates), not pixels. 59 | 60 | The [original Go IconVG 61 | package](https://pkg.go.dev/golang.org/x/exp/shiny/iconvg) also implements a 62 | decoder and encoder, albeit for an [older (obsolete) version of the file 63 | format](https://github.com/google/iconvg/issues/4). 64 | 65 | 66 | ## Disclaimer 67 | 68 | This is not an official Google product, it is just code that happens to be 69 | owned by Google. 70 | 71 | 72 | --- 73 | 74 | Updated on January 2022. 75 | -------------------------------------------------------------------------------- /test/data/README.txt: -------------------------------------------------------------------------------- 1 | action-info.svg comes from the Material Design icon set. See 2 | action/svg/production/ic_info_48px.svg in the 3 | github.com/google/material-design-icons repository. 4 | 5 | action-info.{lo,hi}res.ivg are low- and high-resolution IconVG versions of that 6 | SVG file. Low resolution means that coordinates are quantized to 1/64th of a 7 | unit; the graphic's size is 48 by 48 units. High resolution means that 8 | coordinates are represented by all but the 2 least significant bits of a 9 | float32. Each low resolution coordinate is encoded in either 1 or 2 bytes. Each 10 | high resolution coordinate is encoded in either 1, 2 or 4 bytes. 11 | 12 | action-info.{lo,hi}res.ivg.disassembly are disassemblies of those IconVG files. 13 | 14 | action-info.{lo,hi}res.png are renderings of those IconVG files. 15 | 16 | 17 | 18 | arcs.ivg is inspired by the two examples at 19 | https://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands 20 | 21 | arcs.ivg.disassembly is a disassembly of that IconVG file. 22 | 23 | arcs.png is a rendering of that IconVG file. 24 | 25 | 26 | 27 | blank.ivg is a blank, square graphic. 28 | 29 | blank.ivg.disassembly is a disassembly of that IconVG file. 30 | 31 | blank.png is a rendering of that IconVG file. 32 | 33 | 34 | 35 | cowbell.svg is an original artwork by nigeltao@golang.org. 36 | 37 | cowbell.ivg is an IconVG version of that SVG file. 38 | 39 | cowbell.ivg.disassembly is a disassembly of that IconVG file. 40 | 41 | cowbell.png is a rendering of that IconVG file. 42 | 43 | 44 | 45 | elliptical.ivg was created manually. 46 | 47 | elliptical.ivg.disassembly is a disassembly of that IconVG file. 48 | 49 | elliptical.png is a rendering of that IconVG file. 50 | 51 | 52 | 53 | favicon.svg is based on doc/gopher/favicon.svg from the Go 1.7 release, after 54 | using Inkscape to convert strokes and circles to paths, and saving it as an 55 | "Optimized SVG". 56 | 57 | favicon.ivg is an IconVG version of that SVG file. 58 | 59 | favicon.ivg.disassembly is a disassembly of that IconVG file. 60 | 61 | favicon.png and favicon.pink.png are renderings of that IconVG file. 62 | 63 | 64 | 65 | gradient.ivg was created manually. 66 | 67 | gradient.ivg.disassembly is a disassembly of that IconVG file. 68 | 69 | gradient.png is a rendering of that IconVG file. 70 | 71 | 72 | 73 | lod-polygon.ivg was created manually. 74 | 75 | lod-polygon.ivg.disassembly is a disassembly of that IconVG file. 76 | 77 | lod-polygon.png and lod-polygon.64.png are renderings of that IconVG file. 78 | 79 | 80 | 81 | video-005.jpeg comes from an old version of the Go repository. See 82 | https://codereview.appspot.com/5758047/ 83 | 84 | video-005.primitive.svg was based on running github.com/fogleman/primitive on a 85 | 256x192 scaled version of video-005.jpeg. 86 | 87 | video-005.primitive.ivg is an IconVG version of that SVG file. 88 | 89 | video-005.primitive.ivg.disassembly is a disassembly of that IconVG file. 90 | 91 | video-005.primitive.png is a rendering of that IconVG file. 92 | -------------------------------------------------------------------------------- /test/data/elliptical.ivg.disassembly: -------------------------------------------------------------------------------- 1 | 89 49 56 47 IconVG Magic identifier 2 | 00 Number of metadata chunks: 0 3 | 98 Set CREG[CSEL-0] to a 4 byte color 4 | 02 8a ca 00 gradient (NSTOPS=2, CBASE=10, NBASE=10, radial, reflect) 5 | 0a Set CSEL = 10 6 | 4a Set NSEL = 10 7 | ae Set NREG[NSEL-6] to a real number 8 | af aa aa bc -0.020833336 9 | bd Set NREG[NSEL-5] to a zero-to-one number 10 | 0a 0.041666668 11 | ac Set NREG[NSEL-4] to a real number 12 | 00 0 13 | ab Set NREG[NSEL-3] to a real number 14 | 8b 88 08 3d 0.03333333 15 | aa Set NREG[NSEL-2] to a real number 16 | 00 0 17 | b9 Set NREG[NSEL-1] to a zero-to-one number 18 | a0 0.6666667 19 | 87 Set CREG[CSEL-0] to a 1 byte color; CSEL++ 20 | 4b RGBA c00000ff 21 | af Set NREG[NSEL-0] to a real number; NSEL++ 22 | 00 0 23 | 87 Set CREG[CSEL-0] to a 1 byte color; CSEL++ 24 | 03 RGBA 0000c0ff 25 | af Set NREG[NSEL-0] to a real number; NSEL++ 26 | 02 1 27 | 00 Set CSEL = 0 28 | 40 Set NSEL = 0 29 | c0 Start path, filled with CREG[CSEL-0]; M (absolute moveTo) 30 | 40 -32 31 | 40 -32 32 | e6 H (absolute horizontal lineTo) 33 | c0 +32 34 | e8 V (absolute vertical lineTo) 35 | c0 +32 36 | e6 H (absolute horizontal lineTo) 37 | 40 -32 38 | e1 z (closePath); end path 39 | 80 Set CREG[CSEL-0] to a 1 byte color 40 | 7c RGBA ffffffff 41 | c0 Start path, filled with CREG[CSEL-0]; M (absolute moveTo) 42 | 56 -21 43 | 6c -10 44 | 02 L (absolute lineTo), 3 reps 45 | 58 -20 46 | 6a -11 47 | L (absolute lineTo), implicit 48 | 5a -19 49 | 6c -10 50 | L (absolute lineTo), implicit 51 | 58 -20 52 | 6e -9 53 | e1 z (closePath); end path 54 | c0 Start path, filled with CREG[CSEL-0]; M (absolute moveTo) 55 | 56 -21 56 | 9c +14 57 | 02 L (absolute lineTo), 3 reps 58 | 58 -20 59 | 9a +13 60 | L (absolute lineTo), implicit 61 | 5a -19 62 | 9c +14 63 | L (absolute lineTo), implicit 64 | 58 -20 65 | 9e +15 66 | e1 z (closePath); end path 67 | c0 Start path, filled with CREG[CSEL-0]; M (absolute moveTo) 68 | 92 +9 69 | 8a +5 70 | 02 L (absolute lineTo), 3 reps 71 | 94 +10 72 | 88 +4 73 | L (absolute lineTo), implicit 74 | 96 +11 75 | 8a +5 76 | L (absolute lineTo), implicit 77 | 94 +10 78 | 8c +6 79 | e1 z (closePath); end path 80 | -------------------------------------------------------------------------------- /src/go/lowlevel/buffer.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The IconVG Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package lowlevel 16 | 17 | import ( 18 | "math" 19 | ) 20 | 21 | // buffer holds an encoded IconVG graphic. 22 | // 23 | // The decodeXxx methods return the decoded value and an integer n, the number 24 | // of bytes that value was encoded in. They return n == 0 if an error occured. 25 | // 26 | // The encodeXxx methods append to the buffer, modifying the slice in place. 27 | type buffer []byte 28 | 29 | func (b buffer) decodeNatural() (u uint32, n int) { 30 | if len(b) < 1 { 31 | return 0, 0 32 | } 33 | x := b[0] 34 | if x&0x01 != 0 { 35 | return uint32(x) >> 1, 1 36 | } 37 | if x&0x02 != 0 { 38 | if len(b) >= 2 { 39 | y := uint16(b[0]) | uint16(b[1])<<8 40 | return uint32(y) >> 2, 2 41 | } 42 | return 0, 0 43 | } 44 | if len(b) >= 4 { 45 | y := uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 46 | return y >> 2, 4 47 | } 48 | return 0, 0 49 | } 50 | 51 | func (b buffer) decodeCoordinate() (f float32, n int) { 52 | switch u, n := b.decodeNatural(); n { 53 | case 0: 54 | return 0, n 55 | case 1: 56 | return float32(int32(u) - 64), n 57 | case 2: 58 | return float32(int32(u)-64*128) / 64, n 59 | default: 60 | f = math.Float32frombits(u << 2) 61 | if f != f { // Reject NaN. 62 | return 0, 0 63 | } 64 | return f, n 65 | } 66 | } 67 | 68 | func (b buffer) decodeFloat32() (f float32, n int) { 69 | if len(b) < 4 { 70 | return 0, 0 71 | } 72 | u := uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 73 | f = math.Float32frombits(u) 74 | if f != f { // Reject NaN. 75 | return 0, 0 76 | } 77 | return f, 4 78 | } 79 | 80 | func (b *buffer) encodeNatural(u uint32) { 81 | if u < 1<<7 { 82 | u = (u << 1) | 0x01 83 | *b = append(*b, uint8(u)) 84 | } else if u < 1<<14 { 85 | u = (u << 2) | 0x02 86 | *b = append(*b, uint8(u), uint8(u>>8)) 87 | } else { 88 | u = (u << 2) 89 | *b = append(*b, uint8(u), uint8(u>>8), uint8(u>>16), uint8(u>>24)) 90 | } 91 | } 92 | 93 | func (b *buffer) encodeCoordinate(f float32) { 94 | if i := int32(f); -64 <= i && i < +64 && float32(i) == f { 95 | u := uint32(i + 64) 96 | u = (u << 1) | 0x01 97 | *b = append(*b, uint8(u)) 98 | } else if i := int32(f * 64); -128*64 <= i && i < +128*64 && float32(i) == f*64 { 99 | u := uint32(i + 128*64) 100 | u = (u << 2) | 0x02 101 | *b = append(*b, uint8(u), uint8(u>>8)) 102 | } else { 103 | u := math.Float32bits(f) 104 | 105 | // Round the fractional bits (the low 23 bits) to the nearest multiple 106 | // of 4, being careful not to overflow into the upper bits. 107 | v := u & 0x007fffff 108 | if v < 0x007ffffe { 109 | v += 2 110 | } 111 | u = (u & 0xff800000) | v 112 | 113 | *b = append(*b, uint8(u), uint8(u>>8), uint8(u>>16), uint8(u>>24)) 114 | } 115 | } 116 | 117 | func (b *buffer) encodeFloat32(f float32) { 118 | u := math.Float32bits(f) 119 | *b = append(*b, uint8(u), uint8(u>>8), uint8(u>>16), uint8(u>>24)) 120 | } 121 | -------------------------------------------------------------------------------- /test/data/gradient.iconvg.disassembly: -------------------------------------------------------------------------------- 1 | 8a 49 56 47 IconVG Magic Identifier 2 | 01 Number of metadata chunks: 0 3 | 35 #0000 ClosePath; MoveTo 4 | 45 -30 5 | 45 -30 6 | 34 #0001 Parallelogram 7 | bd +30 8 | 45 -30 9 | bd +30 10 | 5d -18 11 | 72 #0002 SEL -= 4; Set REGS[SEL+1 .. SEL+5] 12 | 00 00 00 00 lo32 = 0x0000_0000 13 | ff 00 00 ff hi32 = rgba(FF:00:00:FF) 14 | 00 40 00 00 lo32 = 0x0000_4000 15 | 00 ff 00 ff hi32 = rgba(00:FF:00:FF) 16 | 00 80 00 00 lo32 = 0x0000_8000 17 | 00 00 ff ff hi32 = rgba(00:00:FF:FF) 18 | 00 00 01 00 lo32 = 0x0001_0000 19 | 00 00 00 ff hi32 = rgba(00:00:00:FF) 20 | 91 02 #0003 ClosePath; Fill (linear gradient; none) with REGS[SEL+1 .. SEL+5] 21 | 88 88 08 3d +0.03333333 22 | 88 88 88 3c +0.016666666 23 | 68 66 66 3f +0.9000001 24 | 36 04 #0004 SEL += 4 25 | 35 #0005 ClosePath; MoveTo 26 | 45 -30 27 | 65 -14 28 | 34 #0006 Parallelogram 29 | bd +30 30 | 65 -14 31 | bd +30 32 | 7d -2 33 | 73 #0007 SEL -= 5; Set REGS[SEL+1 .. SEL+6] 34 | 00 00 00 00 lo32 = 0x0000_0000 35 | 00 ff ff ff hi32 = rgba(00:FF:FF:FF) 36 | 00 40 00 00 lo32 = 0x0000_4000 37 | ff ff ff ff hi32 = rgba(FF:FF:FF:FF) 38 | 00 80 00 00 lo32 = 0x0000_8000 39 | ff 00 ff ff hi32 = rgba(FF:00:FF:FF) 40 | 00 c0 00 00 lo32 = 0x0000_C000 41 | 00 00 00 00 hi32 = rgba(00:00:00:00) 42 | 00 00 01 00 lo32 = 0x0001_0000 43 | ff ff 00 ff hi32 = rgba(FF:FF:00:FF) 44 | 91 43 #0008 ClosePath; Fill (linear gradient; pad) with REGS[SEL+1 .. SEL+6] 45 | 88 88 08 3d +0.03333333 46 | 88 88 88 3c +0.016666666 47 | 24 22 22 3f +0.63333344 48 | 36 05 #0009 SEL += 5 49 | 35 #0010 ClosePath; MoveTo 50 | 45 -30 51 | 85 +2 52 | 34 #0011 Parallelogram 53 | bd +30 54 | 85 +2 55 | bd +30 56 | 9d +14 57 | 72 #0012 SEL -= 4; Set REGS[SEL+1 .. SEL+5] 58 | 00 00 00 00 lo32 = 0x0000_0000 59 | ff 00 00 ff hi32 = rgba(FF:00:00:FF) 60 | 00 40 00 00 lo32 = 0x0000_4000 61 | 00 ff 00 ff hi32 = rgba(00:FF:00:FF) 62 | 00 80 00 00 lo32 = 0x0000_8000 63 | 00 00 ff ff hi32 = rgba(00:00:FF:FF) 64 | 00 00 01 00 lo32 = 0x0001_0000 65 | 00 00 00 ff hi32 = rgba(00:00:00:FF) 66 | a1 82 #0013 ClosePath; Fill (radial gradient; reflect) with REGS[SEL+1 .. SEL+5] 67 | 00 00 80 3d +0.0625 68 | 00 00 00 00 +0 69 | 00 00 00 3f +0.5 70 | 00 00 00 00 +0 71 | 00 00 80 3d +0.0625 72 | 00 00 00 bf -0.5 73 | 36 04 #0014 SEL += 4 74 | 35 #0015 ClosePath; MoveTo 75 | 45 -30 76 | a5 +18 77 | 34 #0016 Parallelogram 78 | bd +30 79 | a5 +18 80 | bd +30 81 | bd +30 82 | 73 #0017 SEL -= 5; Set REGS[SEL+1 .. SEL+6] 83 | 00 00 00 00 lo32 = 0x0000_0000 84 | 00 ff ff ff hi32 = rgba(00:FF:FF:FF) 85 | 00 40 00 00 lo32 = 0x0000_4000 86 | ff ff ff ff hi32 = rgba(FF:FF:FF:FF) 87 | 00 80 00 00 lo32 = 0x0000_8000 88 | ff 00 ff ff hi32 = rgba(FF:00:FF:FF) 89 | 00 c0 00 00 lo32 = 0x0000_C000 90 | 00 00 00 00 hi32 = rgba(00:00:00:00) 91 | 00 00 01 00 lo32 = 0x0001_0000 92 | ff ff 00 ff hi32 = rgba(FF:FF:00:FF) 93 | a1 c3 #0018 ClosePath; Fill (radial gradient; repeat) with REGS[SEL+1 .. SEL+6] 94 | 00 00 80 3d +0.0625 95 | 00 00 00 00 +0 96 | 00 00 00 3f +0.5 97 | 00 00 00 00 +0 98 | 00 00 80 3d +0.0625 99 | 00 00 c0 bf -1.5 100 | 36 05 #0019 SEL += 5 101 | -------------------------------------------------------------------------------- /test/data/cowbell.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /doc/code-of-conduct.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of 9 | experience, education, socio-economic status, nationality, personal appearance, 10 | race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or reject 41 | comments, commits, code, wiki edits, issues, and other contributions that are 42 | not aligned to this Code of Conduct, or to ban temporarily or permanently any 43 | contributor for other behaviors that they deem inappropriate, threatening, 44 | offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | This Code of Conduct also applies outside the project spaces when the Project 56 | Steward has a reasonable belief that an individual's behavior may have a 57 | negative impact on the project or its community. 58 | 59 | ## Conflict Resolution 60 | 61 | We do not believe that all conflict is bad; healthy debate and disagreement 62 | often yield positive results. However, it is never okay to be disrespectful or 63 | to engage in behavior that violates the project’s code of conduct. 64 | 65 | If you see someone violating the code of conduct, you are encouraged to address 66 | the behavior directly with those involved. Many issues can be resolved quickly 67 | and easily, and this gives people more control over the outcome of their 68 | dispute. If you are unable to resolve the matter for any reason, or if the 69 | behavior is threatening or harassing, report it. We are dedicated to providing 70 | an environment where participants feel welcome and safe. 71 | 72 | Reports should be directed to *[PROJECT STEWARD NAME(s) AND EMAIL(s)]*, the 73 | Project Steward(s) for *[PROJECT NAME]*. It is the Project Steward’s duty to 74 | receive and address reported violations of the code of conduct. They will then 75 | work with a committee consisting of representatives from the Open Source 76 | Programs Office and the Google Open Source Strategy team. If for any reason you 77 | are uncomfortable reaching out to the Project Steward, please email 78 | opensource@google.com. 79 | 80 | We will investigate every complaint, but you may not receive a direct response. 81 | We will use our discretion in determining when and how to follow up on reported 82 | incidents, which may range from not taking action to permanent expulsion from 83 | the project and project-sponsored spaces. We will notify the accused of the 84 | report and provide them an opportunity to discuss it before any action is taken. 85 | The identity of the reporter will be omitted from the details of the report 86 | supplied to the accused. In potentially harmful situations, such as ongoing 87 | harassment or threats to anyone's safety, we may take action without notice. 88 | 89 | ## Attribution 90 | 91 | This Code of Conduct is adapted from the Contributor Covenant, version 1.4, 92 | available at 93 | https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 94 | -------------------------------------------------------------------------------- /test/data/arcs.ivg.disassembly: -------------------------------------------------------------------------------- 1 | 89 49 56 47 IconVG Magic identifier 2 | 00 Number of metadata chunks: 0 3 | 81 Set CREG[CSEL-1] to a 1 byte color 4 | 64 RGBA ff0000ff 5 | 82 Set CREG[CSEL-2] to a 1 byte color 6 | 78 RGBA ffff00ff 7 | 83 Set CREG[CSEL-3] to a 1 byte color 8 | 00 RGBA 000000ff 9 | 84 Set CREG[CSEL-4] to a 1 byte color 10 | 02 RGBA 000080ff 11 | c1 Start path, filled with CREG[CSEL-1]; M (absolute moveTo) 12 | 6c -10 13 | 80 +0 14 | e7 h (relative horizontal lineTo) 15 | 62 -15 16 | d0 a (relative arcTo), 1 reps 17 | 9e +15 18 | 9e +15 19 | 00 0 × 360 degrees (0 degrees) 20 | 02 0x1 (largeArc=1, sweep=0) 21 | 9e +15 22 | 62 -15 23 | e1 z (closePath); end path 24 | c2 Start path, filled with CREG[CSEL-2]; M (absolute moveTo) 25 | 64 -14 26 | 78 -4 27 | e9 v (relative vertical lineTo) 28 | 62 -15 29 | d0 a (relative arcTo), 1 reps 30 | 9e +15 31 | 9e +15 32 | 00 0 × 360 degrees (0 degrees) 33 | 00 0x0 (largeArc=0, sweep=0) 34 | 62 -15 35 | 9e +15 36 | e1 z (closePath); end path 37 | c3 Start path, filled with CREG[CSEL-3]; M (absolute moveTo) 38 | 62 -15 39 | bc +30 40 | 20 l (relative lineTo), 1 reps 41 | 8a +5 42 | 81 7d -2.5 43 | d0 a (relative arcTo), 1 reps 44 | 81 82 +2.5 45 | 81 82 +2.5 46 | dc 0.9166667 × 360 degrees (330 degrees) 47 | 04 0x2 (largeArc=0, sweep=1) 48 | 8a +5 49 | 81 7d -2.5 50 | 20 l (relative lineTo), 1 reps 51 | 8a +5 52 | 81 7d -2.5 53 | d0 a (relative arcTo), 1 reps 54 | 81 82 +2.5 55 | 8a +5 56 | dc 0.9166667 × 360 degrees (330 degrees) 57 | 04 0x2 (largeArc=0, sweep=1) 58 | 8a +5 59 | 81 7d -2.5 60 | 20 l (relative lineTo), 1 reps 61 | 8a +5 62 | 81 7d -2.5 63 | d0 a (relative arcTo), 1 reps 64 | 81 82 +2.5 65 | 81 87 +7.5 66 | dc 0.9166667 × 360 degrees (330 degrees) 67 | 04 0x2 (largeArc=0, sweep=1) 68 | 8a +5 69 | 81 7d -2.5 70 | 20 l (relative lineTo), 1 reps 71 | 8a +5 72 | 81 7d -2.5 73 | d0 a (relative arcTo), 1 reps 74 | 81 82 +2.5 75 | 94 +10 76 | dc 0.9166667 × 360 degrees (330 degrees) 77 | 04 0x2 (largeArc=0, sweep=1) 78 | 8a +5 79 | 81 7d -2.5 80 | 20 l (relative lineTo), 1 reps 81 | 8a +5 82 | 81 7d -2.5 83 | e8 V (absolute vertical lineTo) 84 | bc +30 85 | e1 z (closePath); end path 86 | c4 Start path, filled with CREG[CSEL-4]; M (absolute moveTo) 87 | 94 +10 88 | 48 -28 89 | d0 a (relative arcTo), 1 reps 90 | 8c +6 91 | 86 +3 92 | 00 0 × 360 degrees (0 degrees) 93 | 00 0x0 (largeArc=0, sweep=0) 94 | 8c +6 95 | 86 +3 96 | e1 z (closePath); end path 97 | c4 Start path, filled with CREG[CSEL-4]; M (absolute moveTo) 98 | a4 +18 99 | 48 -28 100 | d0 a (relative arcTo), 1 reps 101 | 8c +6 102 | 86 +3 103 | 00 0 × 360 degrees (0 degrees) 104 | 04 0x2 (largeArc=0, sweep=1) 105 | 8c +6 106 | 86 +3 107 | e1 z (closePath); end path 108 | c4 Start path, filled with CREG[CSEL-4]; M (absolute moveTo) 109 | 94 +10 110 | 58 -20 111 | d0 a (relative arcTo), 1 reps 112 | 8c +6 113 | 86 +3 114 | 00 0 × 360 degrees (0 degrees) 115 | 02 0x1 (largeArc=1, sweep=0) 116 | 8c +6 117 | 86 +3 118 | e1 z (closePath); end path 119 | c4 Start path, filled with CREG[CSEL-4]; M (absolute moveTo) 120 | a4 +18 121 | 58 -20 122 | d0 a (relative arcTo), 1 reps 123 | 8c +6 124 | 86 +3 125 | 00 0 × 360 degrees (0 degrees) 126 | 06 0x3 (largeArc=1, sweep=1) 127 | 8c +6 128 | 86 +3 129 | e1 z (closePath); end path 130 | -------------------------------------------------------------------------------- /src/c/broken.c: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The IconVG Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "./aaa_private.h" 16 | 17 | static const char* // 18 | iconvg_private_broken_canvas__begin_decode(iconvg_canvas* c, 19 | iconvg_rectangle_f32 dst_rect) { 20 | return ((const char*)(c->context.const_ptr3)); 21 | } 22 | 23 | static const char* // 24 | iconvg_private_broken_canvas__end_decode(iconvg_canvas* c, 25 | const char* err_msg, 26 | size_t num_bytes_consumed, 27 | size_t num_bytes_remaining) { 28 | return err_msg ? err_msg : ((const char*)(c->context.const_ptr3)); 29 | } 30 | 31 | static const char* // 32 | iconvg_private_broken_canvas__begin_drawing(iconvg_canvas* c) { 33 | return ((const char*)(c->context.const_ptr3)); 34 | } 35 | 36 | static const char* // 37 | iconvg_private_broken_canvas__end_drawing(iconvg_canvas* c, 38 | const iconvg_paint* p) { 39 | return ((const char*)(c->context.const_ptr3)); 40 | } 41 | 42 | static const char* // 43 | iconvg_private_broken_canvas__begin_path(iconvg_canvas* c, float x0, float y0) { 44 | return ((const char*)(c->context.const_ptr3)); 45 | } 46 | 47 | static const char* // 48 | iconvg_private_broken_canvas__end_path(iconvg_canvas* c) { 49 | return ((const char*)(c->context.const_ptr3)); 50 | } 51 | 52 | static const char* // 53 | iconvg_private_broken_canvas__path_line_to(iconvg_canvas* c, 54 | float x1, 55 | float y1) { 56 | return ((const char*)(c->context.const_ptr3)); 57 | } 58 | 59 | static const char* // 60 | iconvg_private_broken_canvas__path_quad_to(iconvg_canvas* c, 61 | float x1, 62 | float y1, 63 | float x2, 64 | float y2) { 65 | return ((const char*)(c->context.const_ptr3)); 66 | } 67 | 68 | static const char* // 69 | iconvg_private_broken_canvas__path_cube_to(iconvg_canvas* c, 70 | float x1, 71 | float y1, 72 | float x2, 73 | float y2, 74 | float x3, 75 | float y3) { 76 | return ((const char*)(c->context.const_ptr3)); 77 | } 78 | 79 | static const char* // 80 | iconvg_private_broken_canvas__on_metadata_viewbox( 81 | iconvg_canvas* c, 82 | iconvg_rectangle_f32 viewbox) { 83 | return ((const char*)(c->context.const_ptr3)); 84 | } 85 | 86 | static const char* // 87 | iconvg_private_broken_canvas__on_metadata_suggested_palette( 88 | iconvg_canvas* c, 89 | const iconvg_palette* suggested_palette) { 90 | return ((const char*)(c->context.const_ptr3)); 91 | } 92 | 93 | static const iconvg_canvas_vtable // 94 | iconvg_private_broken_canvas_vtable = { 95 | sizeof(iconvg_canvas_vtable), 96 | &iconvg_private_broken_canvas__begin_decode, 97 | &iconvg_private_broken_canvas__end_decode, 98 | &iconvg_private_broken_canvas__begin_drawing, 99 | &iconvg_private_broken_canvas__end_drawing, 100 | &iconvg_private_broken_canvas__begin_path, 101 | &iconvg_private_broken_canvas__end_path, 102 | &iconvg_private_broken_canvas__path_line_to, 103 | &iconvg_private_broken_canvas__path_quad_to, 104 | &iconvg_private_broken_canvas__path_cube_to, 105 | &iconvg_private_broken_canvas__on_metadata_viewbox, 106 | &iconvg_private_broken_canvas__on_metadata_suggested_palette, 107 | }; 108 | 109 | iconvg_canvas // 110 | iconvg_canvas__make_broken(const char* err_msg) { 111 | iconvg_canvas c; 112 | c.vtable = &iconvg_private_broken_canvas_vtable; 113 | memset(&c.context, 0, sizeof(c.context)); 114 | c.context.const_ptr3 = err_msg; 115 | return c; 116 | } 117 | 118 | bool // 119 | iconvg_canvas__does_nothing(const iconvg_canvas* self) { 120 | return !self || (self->vtable == NULL) || 121 | (self->vtable == &iconvg_private_broken_canvas_vtable); 122 | } 123 | -------------------------------------------------------------------------------- /src/go/lowlevel/color.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The IconVG Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package lowlevel 16 | 17 | import ( 18 | "image/color" 19 | ) 20 | 21 | // TODO: convert from FFV0's CREG (32-bit) to FFV1's REGS (64-bit). 22 | 23 | func validAlphaPremulColor(c color.RGBA) bool { 24 | return c.R <= c.A && c.G <= c.A && c.B <= c.A 25 | } 26 | 27 | // colorType distinguishes types of Colors. 28 | type colorType uint8 29 | 30 | const ( 31 | // colorTypeRGBA is a direct RGBA color. 32 | colorTypeRGBA colorType = iota 33 | 34 | // colorTypePaletteIndex is an indirect color, indexing the custom palette. 35 | colorTypePaletteIndex 36 | 37 | // colorTypeCReg is an indirect color, indexing the CREG color registers. 38 | colorTypeCReg 39 | 40 | // colorTypeBlend is an indirect color, blending two other colors. 41 | colorTypeBlend 42 | ) 43 | 44 | // Color is an IconVG color, whose RGBA values can depend on context. Some 45 | // Colors are direct RGBA values. Other Colors are indirect, referring to an 46 | // index of the custom palette, a color register of the decoder virtual 47 | // machine, or a blend of two other Colors. 48 | // 49 | // See the "Colors" section in the specification for details. 50 | type Color struct { 51 | typ colorType 52 | data color.RGBA 53 | } 54 | 55 | func (c Color) rgba() color.RGBA { return c.data } 56 | func (c Color) paletteIndex() uint8 { return c.data.R } 57 | func (c Color) cReg() uint8 { return c.data.R } 58 | func (c Color) blend() (t, c0, c1 uint8) { return c.data.R, c.data.G, c.data.B } 59 | 60 | // Resolve resolves the Color's RGBA value, given its context: the custom 61 | // palette and the color registers of the decoder virtual machine. 62 | func (c Color) Resolve(pal *Palette, cReg *[64]color.RGBA) color.RGBA { 63 | switch c.typ { 64 | case colorTypeRGBA: 65 | return c.rgba() 66 | case colorTypePaletteIndex: 67 | return pal[c.paletteIndex()&0x3f] 68 | case colorTypeCReg: 69 | return cReg[c.cReg()&0x3f] 70 | } 71 | t, c0, c1 := c.blend() 72 | p, q := uint32(255-t), uint32(t) 73 | rgba0 := decodeColor1(c0).Resolve(pal, cReg) 74 | rgba1 := decodeColor1(c1).Resolve(pal, cReg) 75 | return color.RGBA{ 76 | uint8(((p * uint32(rgba0.R)) + q*uint32(rgba1.R) + 128) / 255), 77 | uint8(((p * uint32(rgba0.G)) + q*uint32(rgba1.G) + 128) / 255), 78 | uint8(((p * uint32(rgba0.B)) + q*uint32(rgba1.B) + 128) / 255), 79 | uint8(((p * uint32(rgba0.A)) + q*uint32(rgba1.A) + 128) / 255), 80 | } 81 | } 82 | 83 | // RGBAColor returns a direct Color. 84 | func RGBAColor(c color.RGBA) Color { return Color{colorTypeRGBA, c} } 85 | 86 | // PaletteIndexColor returns an indirect Color referring to an index of the 87 | // custom palette. 88 | func PaletteIndexColor(i uint8) Color { return Color{colorTypePaletteIndex, color.RGBA{R: i & 0x3f}} } 89 | 90 | // CRegColor returns an indirect Color referring to a color register of the 91 | // decoder virtual machine. 92 | func CRegColor(i uint8) Color { return Color{colorTypeCReg, color.RGBA{R: i & 0x3f}} } 93 | 94 | // BlendColor returns an indirect Color that blends two other Colors. Those two 95 | // other Colors must both be encodable as a 1 byte color. 96 | // 97 | // To blend a Color that is not encodable as a 1 byte color, first load that 98 | // Color into a CREG color register, then call CRegColor to produce a Color 99 | // that is encodable as a 1 byte color. See testdata/favicon.ivg for an 100 | // example. 101 | // 102 | // See the "Colors" section in the specification for details. 103 | // 104 | // TODO: update this for FFV1. 105 | func BlendColor(t, c0, c1 uint8) Color { return Color{colorTypeBlend, color.RGBA{R: t, G: c0, B: c1}} } 106 | 107 | func decodeColor1(x byte) Color { 108 | if x >= 0x80 { 109 | if x >= 0xc0 { 110 | return CRegColor(x) 111 | } else { 112 | return PaletteIndexColor(x) 113 | } 114 | } 115 | switch x { 116 | case 0: 117 | return RGBAColor(color.RGBA{0x00, 0x00, 0x00, 0x00}) 118 | case 1: 119 | return RGBAColor(color.RGBA{0x80, 0x80, 0x80, 0x80}) 120 | case 2: 121 | return RGBAColor(color.RGBA{0xc0, 0xc0, 0xc0, 0xc0}) 122 | } 123 | x -= 3 124 | red := dc1Table[x%5] 125 | x /= 5 126 | green := dc1Table[x%5] 127 | x /= 5 128 | blue := dc1Table[x] 129 | return RGBAColor(color.RGBA{red, green, blue, 0xff}) 130 | } 131 | 132 | var dc1Table = [5]byte{0x00, 0x40, 0x80, 0xc0, 0xff} 133 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 2 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 3 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 4 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 5 | golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= 6 | golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= 7 | golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= 8 | golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ= 9 | golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E= 10 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 11 | golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 12 | golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 13 | golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 14 | golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 15 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 16 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 17 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 18 | golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 19 | golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= 20 | golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= 21 | golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= 22 | golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= 23 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 24 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 25 | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 26 | golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= 27 | golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 28 | golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 29 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 30 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 31 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 32 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 33 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 34 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 35 | golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 36 | golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 37 | golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 38 | golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 39 | golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= 40 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 41 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 42 | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= 43 | golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= 44 | golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= 45 | golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= 46 | golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= 47 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 48 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 49 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 50 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 51 | golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 52 | golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 53 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 54 | golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 55 | golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= 56 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 57 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 58 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 59 | golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= 60 | golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= 61 | golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= 62 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 63 | -------------------------------------------------------------------------------- /src/c/aaa_private.h: -------------------------------------------------------------------------------- 1 | #ifndef ICONVG_PRIVATE_INCLUDE_GUARD 2 | #define ICONVG_PRIVATE_INCLUDE_GUARD 3 | 4 | // Copyright 2021 The IconVG Authors. 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // https://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | #include 19 | #include 20 | 21 | #include "./aaa_public.h" 22 | 23 | #define ICONVG_PRIVATE_TRY(err_msg) \ 24 | do { \ 25 | const char* iconvg_private_try_err_msg = err_msg; \ 26 | if (iconvg_private_try_err_msg) { \ 27 | return iconvg_private_try_err_msg; \ 28 | } \ 29 | } while (false) 30 | 31 | // ---- 32 | 33 | static inline uint16_t // 34 | iconvg_private_peek_u16le(const uint8_t* p) { 35 | return (uint16_t)(((uint16_t)(p[0]) << 0) | ((uint16_t)(p[1]) << 8)); 36 | } 37 | 38 | static inline uint32_t // 39 | iconvg_private_peek_u32le(const uint8_t* p) { 40 | return ((uint32_t)(p[0]) << 0) | ((uint32_t)(p[1]) << 8) | 41 | ((uint32_t)(p[2]) << 16) | ((uint32_t)(p[3]) << 24); 42 | } 43 | 44 | static inline uint64_t // 45 | iconvg_private_peek_u64le(const uint8_t* p) { 46 | return ((uint64_t)(p[0]) << 0) | ((uint64_t)(p[1]) << 8) | 47 | ((uint64_t)(p[2]) << 16) | ((uint64_t)(p[3]) << 24) | 48 | ((uint64_t)(p[4]) << 32) | ((uint64_t)(p[5]) << 40) | 49 | ((uint64_t)(p[6]) << 48) | ((uint64_t)(p[7]) << 56); 50 | } 51 | 52 | static inline void // 53 | iconvg_private_poke_u32le(uint8_t* p, uint32_t x) { 54 | p[0] = (uint8_t)(x >> 0); 55 | p[1] = (uint8_t)(x >> 8); 56 | p[2] = (uint8_t)(x >> 16); 57 | p[3] = (uint8_t)(x >> 24); 58 | } 59 | 60 | static inline float // 61 | iconvg_private_reinterpret_from_u32_to_f32(uint32_t u) { 62 | float f = 0; 63 | if (sizeof(uint32_t) == sizeof(float)) { 64 | memcpy(&f, &u, sizeof(uint32_t)); 65 | } 66 | return f; 67 | } 68 | 69 | // ---- 70 | 71 | static inline size_t // 72 | iconvg_private_canvas_sizeof_vtable(iconvg_canvas* c) { 73 | if (c && c->vtable) { 74 | return c->vtable->sizeof__iconvg_canvas_vtable; 75 | } 76 | return 0; 77 | } 78 | 79 | // ---- 80 | 81 | static inline iconvg_rectangle_f32 // 82 | iconvg_private_default_viewbox() { 83 | iconvg_rectangle_f32 r; 84 | r.min_x = -32.0f; 85 | r.min_y = -32.0f; 86 | r.max_x = +32.0f; 87 | r.max_y = +32.0f; 88 | return r; 89 | } 90 | 91 | // ---- 92 | 93 | typedef struct iconvg_private_decoder_struct { 94 | const uint8_t* ptr; 95 | size_t len; 96 | } iconvg_private_decoder; 97 | 98 | // ---- 99 | 100 | extern const uint32_t iconvg_private_one_byte_colors[128]; 101 | extern const iconvg_palette iconvg_private_default_palette; 102 | 103 | // ---- 104 | 105 | static inline int // 106 | iconvg_private_last_color_that_isnt_opaque_black( 107 | const iconvg_palette* palette) { 108 | int i = 63; 109 | for (; i >= 0; i--) { 110 | if (iconvg_private_peek_u32le(&palette->colors[i].rgba[0]) != 0xFF000000u) { 111 | break; 112 | } 113 | } 114 | return i; 115 | } 116 | 117 | // ---- 118 | 119 | struct iconvg_paint_struct { 120 | iconvg_rectangle_f32 viewbox; 121 | int64_t height_in_pixels; 122 | iconvg_palette custom_palette; 123 | 124 | // iconvg_private_initialize_remaining_paint_fields sets the fields below. 125 | 126 | // Scale and bias convert between dst coordinates (what this library calls 127 | // user or canvas coordinate space) and src coordinates (what this library 128 | // calls viewbox or graphic coordinate space). When converting from p to q: 129 | // 130 | // q_x = (p_x * p2q_scale_x) + p2q_bias_x 131 | // q_y = (p_y * p2q_scale_y) + p2q_bias_y 132 | // 133 | // For example, an IconVG file might declare its viewbox ranging from -32 to 134 | // +32 along the X axis, in ideal (not pixel) space. The user might rasterize 135 | // this on screen from x=400 to x=500, 100 pixels wide. This corresponds to 136 | // s2d_scale_x = (100 / (+32 - -32)) = 1.5625 and s2d_bias_x = 450, because: 137 | // 138 | // 400 = ((-32) * 1.5625) + 450 139 | // 500 = ((+32) * 1.5625) + 450 140 | 141 | double s2d_scale_x; 142 | double s2d_bias_x; 143 | double s2d_scale_y; 144 | double s2d_bias_y; 145 | 146 | double d2s_scale_x; 147 | double d2s_bias_x; 148 | double d2s_scale_y; 149 | double d2s_bias_y; 150 | 151 | uint8_t sel; 152 | bool begun_drawing; 153 | bool begun_path; 154 | 155 | uint8_t paint_type; 156 | uint8_t num_stops; 157 | uint8_t spread; 158 | uint8_t which_regs; 159 | 160 | union { 161 | // coords[0] are the current x and y coordinates. coords[1..4] are the x 162 | // and y coordinates of the path op arguments. That final space (6 floats) 163 | // is also used to hold gradient transformation matrices. 164 | float coords[4][2]; 165 | struct { 166 | float current_x; 167 | float current_y; 168 | float transform[6]; 169 | }; 170 | }; 171 | 172 | uint64_t regs[64]; 173 | }; 174 | 175 | // ---- 176 | 177 | const char* // 178 | iconvg_private_path_arc_to(iconvg_canvas* c, 179 | double scale_x, 180 | double bias_x, 181 | double scale_y, 182 | double bias_y, 183 | float initial_x, 184 | float initial_y, 185 | float radius_x, 186 | float radius_y, 187 | float x_axis_rotation, 188 | bool large_arc, 189 | bool sweep, 190 | float final_x, 191 | float final_y); 192 | 193 | #endif // ICONVG_PRIVATE_INCLUDE_GUARD 194 | -------------------------------------------------------------------------------- /src/go/lowlevel/lowlevel.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The IconVG Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package lowlevel provides a low-level decoder for the IconVG file format. 16 | // 17 | // IconVG is specified at 18 | // https://github.com/google/iconvg/blob/main/spec/iconvg-spec.md 19 | package lowlevel 20 | 21 | import ( 22 | "errors" 23 | "image/color" 24 | "math" 25 | 26 | "golang.org/x/image/math/f32" 27 | ) 28 | 29 | var ( 30 | errInconsistentMetadataChunkLength = errors.New("iconvg: inconsistent metadata chunk length") 31 | errInvalidColor = errors.New("iconvg: invalid color") 32 | errInvalidExtraDataLength = errors.New("iconvg: invalid extra data length") 33 | errInvalidMagicIdentifier = errors.New("iconvg: invalid magic identifier") 34 | errInvalidMetadataChunkLength = errors.New("iconvg: invalid metadata chunk length") 35 | errInvalidMetadataIdentifier = errors.New("iconvg: invalid metadata identifier") 36 | errInvalidNumber = errors.New("iconvg: invalid number") 37 | errInvalidNumberOfMetadataChunks = errors.New("iconvg: invalid number of metadata chunks") 38 | errInvalidSuggestedPalette = errors.New("iconvg: invalid suggested palette") 39 | errInvalidViewBox = errors.New("iconvg: invalid view box") 40 | errUnsupportedMetadataIdentifier = errors.New("iconvg: unsupported metadata identifier") 41 | ) 42 | 43 | // featureBits are a bitmask of optional extensions to the core IconVG file 44 | // format. Over time, features may be added to the format and newer IconVG 45 | // files can tell older decoders to skip over parts they wouldn't understand. 46 | type featureBits uint32 47 | 48 | const ( 49 | featureBitsAnimation featureBits = 0x00000001 50 | ) 51 | 52 | var gradientShapeNames = [2]string{ 53 | "linear", 54 | "radial", 55 | } 56 | 57 | var gradientSpreadNames = [4]string{ 58 | "none", 59 | "pad", 60 | "reflect", 61 | "repeat", 62 | } 63 | 64 | const magic = "\x8AIVG" 65 | 66 | var magicBytes = []byte(magic) 67 | 68 | func isNaNOrInfinity(f float32) bool { 69 | return math.Float32bits(f)&0x7f800000 == 0x7f800000 70 | } 71 | 72 | // Rectangle is defined by its minimum and maximum coordinates. 73 | type Rectangle struct { 74 | Min, Max f32.Vec2 75 | } 76 | 77 | // AspectRatio returns the Rectangle's aspect ratio. An IconVG graphic is 78 | // scalable; these dimensions do not necessarily map 1:1 to pixels. 79 | func (r *Rectangle) AspectRatio() (dx, dy float32) { 80 | return r.Max[0] - r.Min[0], r.Max[1] - r.Min[1] 81 | } 82 | 83 | // Palette is an IconVG palette. 84 | type Palette [64]color.RGBA 85 | 86 | // Metadata is an IconVG's metadata. 87 | type Metadata struct { 88 | ViewBox Rectangle 89 | 90 | // Palette is a 64 color palette. When encoding, it is the suggested 91 | // palette to place within the IconVG graphic. When decoding, it is either 92 | // the optional palette passed to Decode, or if no optional palette was 93 | // given, the suggested palette within the IconVG graphic. 94 | Palette Palette 95 | } 96 | 97 | const ( 98 | midViewBox = 8 99 | midSuggestedPalette = 16 100 | ) 101 | 102 | // DefaultViewBox is the default ViewBox. Its values should not be modified. 103 | var DefaultViewBox = Rectangle{ 104 | Min: f32.Vec2{-32, -32}, 105 | Max: f32.Vec2{+32, +32}, 106 | } 107 | 108 | // DefaultPalette is the default Palette. Its values should not be modified. 109 | var DefaultPalette = Palette{ 110 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 111 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 112 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 113 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 114 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 115 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 116 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 117 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 118 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 119 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 120 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 121 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 122 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 123 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 124 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 125 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 126 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 127 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 128 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 129 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 130 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 131 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 132 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 133 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 134 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 135 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 136 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 137 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 138 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 139 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 140 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 141 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 142 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 143 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 144 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 145 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 146 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 147 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 148 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 149 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 150 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 151 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 152 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 153 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 154 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 155 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 156 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 157 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 158 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 159 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 160 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 161 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 162 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 163 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 164 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 165 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 166 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 167 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 168 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 169 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 170 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 171 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 172 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 173 | color.RGBA{0x00, 0x00, 0x00, 0xff}, 174 | } 175 | -------------------------------------------------------------------------------- /src/c/color.c: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The IconVG Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "./aaa_private.h" 16 | 17 | // iconvg_private_one_byte_colors holds the first 128 one-byte colors, in 18 | // 0xAABBGGRR alpha-premultiplied format. 19 | const uint32_t iconvg_private_one_byte_colors[128] = { 20 | 0x00000000, // 21 | 0x80808080, // 22 | 0xC0C0C0C0, // 23 | 0xFF000000, // 24 | 0xFF000040, // 25 | 0xFF000080, // 26 | 0xFF0000C0, // 27 | 0xFF0000FF, // 28 | 0xFF004000, // 29 | 0xFF004040, // 30 | 0xFF004080, // 31 | 0xFF0040C0, // 32 | 0xFF0040FF, // 33 | 0xFF008000, // 34 | 0xFF008040, // 35 | 0xFF008080, // 36 | 0xFF0080C0, // 37 | 0xFF0080FF, // 38 | 0xFF00C000, // 39 | 0xFF00C040, // 40 | 0xFF00C080, // 41 | 0xFF00C0C0, // 42 | 0xFF00C0FF, // 43 | 0xFF00FF00, // 44 | 0xFF00FF40, // 45 | 0xFF00FF80, // 46 | 0xFF00FFC0, // 47 | 0xFF00FFFF, // 48 | 0xFF400000, // 49 | 0xFF400040, // 50 | 0xFF400080, // 51 | 0xFF4000C0, // 52 | 0xFF4000FF, // 53 | 0xFF404000, // 54 | 0xFF404040, // 55 | 0xFF404080, // 56 | 0xFF4040C0, // 57 | 0xFF4040FF, // 58 | 0xFF408000, // 59 | 0xFF408040, // 60 | 0xFF408080, // 61 | 0xFF4080C0, // 62 | 0xFF4080FF, // 63 | 0xFF40C000, // 64 | 0xFF40C040, // 65 | 0xFF40C080, // 66 | 0xFF40C0C0, // 67 | 0xFF40C0FF, // 68 | 0xFF40FF00, // 69 | 0xFF40FF40, // 70 | 0xFF40FF80, // 71 | 0xFF40FFC0, // 72 | 0xFF40FFFF, // 73 | 0xFF800000, // 74 | 0xFF800040, // 75 | 0xFF800080, // 76 | 0xFF8000C0, // 77 | 0xFF8000FF, // 78 | 0xFF804000, // 79 | 0xFF804040, // 80 | 0xFF804080, // 81 | 0xFF8040C0, // 82 | 0xFF8040FF, // 83 | 0xFF808000, // 84 | 0xFF808040, // 85 | 0xFF808080, // 86 | 0xFF8080C0, // 87 | 0xFF8080FF, // 88 | 0xFF80C000, // 89 | 0xFF80C040, // 90 | 0xFF80C080, // 91 | 0xFF80C0C0, // 92 | 0xFF80C0FF, // 93 | 0xFF80FF00, // 94 | 0xFF80FF40, // 95 | 0xFF80FF80, // 96 | 0xFF80FFC0, // 97 | 0xFF80FFFF, // 98 | 0xFFC00000, // 99 | 0xFFC00040, // 100 | 0xFFC00080, // 101 | 0xFFC000C0, // 102 | 0xFFC000FF, // 103 | 0xFFC04000, // 104 | 0xFFC04040, // 105 | 0xFFC04080, // 106 | 0xFFC040C0, // 107 | 0xFFC040FF, // 108 | 0xFFC08000, // 109 | 0xFFC08040, // 110 | 0xFFC08080, // 111 | 0xFFC080C0, // 112 | 0xFFC080FF, // 113 | 0xFFC0C000, // 114 | 0xFFC0C040, // 115 | 0xFFC0C080, // 116 | 0xFFC0C0C0, // 117 | 0xFFC0C0FF, // 118 | 0xFFC0FF00, // 119 | 0xFFC0FF40, // 120 | 0xFFC0FF80, // 121 | 0xFFC0FFC0, // 122 | 0xFFC0FFFF, // 123 | 0xFFFF0000, // 124 | 0xFFFF0040, // 125 | 0xFFFF0080, // 126 | 0xFFFF00C0, // 127 | 0xFFFF00FF, // 128 | 0xFFFF4000, // 129 | 0xFFFF4040, // 130 | 0xFFFF4080, // 131 | 0xFFFF40C0, // 132 | 0xFFFF40FF, // 133 | 0xFFFF8000, // 134 | 0xFFFF8040, // 135 | 0xFFFF8080, // 136 | 0xFFFF80C0, // 137 | 0xFFFF80FF, // 138 | 0xFFFFC000, // 139 | 0xFFFFC040, // 140 | 0xFFFFC080, // 141 | 0xFFFFC0C0, // 142 | 0xFFFFC0FF, // 143 | 0xFFFFFF00, // 144 | 0xFFFFFF40, // 145 | 0xFFFFFF80, // 146 | 0xFFFFFFC0, // 147 | 0xFFFFFFFF, // 148 | }; 149 | 150 | const iconvg_palette iconvg_private_default_palette = {{ 151 | {{0x00, 0x00, 0x00, 0xFF}}, // 152 | {{0x00, 0x00, 0x00, 0xFF}}, // 153 | {{0x00, 0x00, 0x00, 0xFF}}, // 154 | {{0x00, 0x00, 0x00, 0xFF}}, // 155 | {{0x00, 0x00, 0x00, 0xFF}}, // 156 | {{0x00, 0x00, 0x00, 0xFF}}, // 157 | {{0x00, 0x00, 0x00, 0xFF}}, // 158 | {{0x00, 0x00, 0x00, 0xFF}}, // 159 | {{0x00, 0x00, 0x00, 0xFF}}, // 160 | {{0x00, 0x00, 0x00, 0xFF}}, // 161 | {{0x00, 0x00, 0x00, 0xFF}}, // 162 | {{0x00, 0x00, 0x00, 0xFF}}, // 163 | {{0x00, 0x00, 0x00, 0xFF}}, // 164 | {{0x00, 0x00, 0x00, 0xFF}}, // 165 | {{0x00, 0x00, 0x00, 0xFF}}, // 166 | {{0x00, 0x00, 0x00, 0xFF}}, // 167 | {{0x00, 0x00, 0x00, 0xFF}}, // 168 | {{0x00, 0x00, 0x00, 0xFF}}, // 169 | {{0x00, 0x00, 0x00, 0xFF}}, // 170 | {{0x00, 0x00, 0x00, 0xFF}}, // 171 | {{0x00, 0x00, 0x00, 0xFF}}, // 172 | {{0x00, 0x00, 0x00, 0xFF}}, // 173 | {{0x00, 0x00, 0x00, 0xFF}}, // 174 | {{0x00, 0x00, 0x00, 0xFF}}, // 175 | {{0x00, 0x00, 0x00, 0xFF}}, // 176 | {{0x00, 0x00, 0x00, 0xFF}}, // 177 | {{0x00, 0x00, 0x00, 0xFF}}, // 178 | {{0x00, 0x00, 0x00, 0xFF}}, // 179 | {{0x00, 0x00, 0x00, 0xFF}}, // 180 | {{0x00, 0x00, 0x00, 0xFF}}, // 181 | {{0x00, 0x00, 0x00, 0xFF}}, // 182 | {{0x00, 0x00, 0x00, 0xFF}}, // 183 | {{0x00, 0x00, 0x00, 0xFF}}, // 184 | {{0x00, 0x00, 0x00, 0xFF}}, // 185 | {{0x00, 0x00, 0x00, 0xFF}}, // 186 | {{0x00, 0x00, 0x00, 0xFF}}, // 187 | {{0x00, 0x00, 0x00, 0xFF}}, // 188 | {{0x00, 0x00, 0x00, 0xFF}}, // 189 | {{0x00, 0x00, 0x00, 0xFF}}, // 190 | {{0x00, 0x00, 0x00, 0xFF}}, // 191 | {{0x00, 0x00, 0x00, 0xFF}}, // 192 | {{0x00, 0x00, 0x00, 0xFF}}, // 193 | {{0x00, 0x00, 0x00, 0xFF}}, // 194 | {{0x00, 0x00, 0x00, 0xFF}}, // 195 | {{0x00, 0x00, 0x00, 0xFF}}, // 196 | {{0x00, 0x00, 0x00, 0xFF}}, // 197 | {{0x00, 0x00, 0x00, 0xFF}}, // 198 | {{0x00, 0x00, 0x00, 0xFF}}, // 199 | {{0x00, 0x00, 0x00, 0xFF}}, // 200 | {{0x00, 0x00, 0x00, 0xFF}}, // 201 | {{0x00, 0x00, 0x00, 0xFF}}, // 202 | {{0x00, 0x00, 0x00, 0xFF}}, // 203 | {{0x00, 0x00, 0x00, 0xFF}}, // 204 | {{0x00, 0x00, 0x00, 0xFF}}, // 205 | {{0x00, 0x00, 0x00, 0xFF}}, // 206 | {{0x00, 0x00, 0x00, 0xFF}}, // 207 | {{0x00, 0x00, 0x00, 0xFF}}, // 208 | {{0x00, 0x00, 0x00, 0xFF}}, // 209 | {{0x00, 0x00, 0x00, 0xFF}}, // 210 | {{0x00, 0x00, 0x00, 0xFF}}, // 211 | {{0x00, 0x00, 0x00, 0xFF}}, // 212 | {{0x00, 0x00, 0x00, 0xFF}}, // 213 | {{0x00, 0x00, 0x00, 0xFF}}, // 214 | {{0x00, 0x00, 0x00, 0xFF}}, // 215 | }}; 216 | -------------------------------------------------------------------------------- /test/data/arcs.iconvg.disassembly: -------------------------------------------------------------------------------- 1 | 8a 49 56 47 IconVG Magic Identifier 2 | 01 Number of metadata chunks: 0 3 | 35 #0000 ClosePath; MoveTo 4 | 6d -10 5 | 81 +0 6 | 01 #0001 LineTo (1 reps) 7 | 4f -25 8 | 81 +0 9 | 23 #0002 CubeTo (3 reps) 10 | 4f -25 11 | 4a 88 +8.28125 12 | ba 6d -18.28125 13 | 9f +15 14 | 6d -10 15 | 9f +15 16 | (rep) 17 | 4a 7e -1.71875 18 | 9f +15 19 | 8b +5 20 | 4a 88 +8.28125 21 | 8b +5 22 | 81 +0 23 | (rep) 24 | 8b +5 25 | ba 77 -8.28125 26 | 4a 7e -1.71875 27 | 63 -15 28 | 6d -10 29 | 63 -15 30 | 57 #0003 Set REGS[SEL+7].hi32 31 | ff 00 00 ff hi32 = rgba(FF:00:00:FF) 32 | 87 #0004 ClosePath; Fill (flat color) with REGS[SEL+7] 33 | 35 #0005 ClosePath; MoveTo 34 | 65 -14 35 | 79 -4 36 | 01 #0006 LineTo (1 reps) 37 | 65 -14 38 | 5b -19 39 | 21 #0007 CubeTo (1 reps) 40 | ba 69 -22.28125 41 | 5b -19 42 | 47 -29 43 | ba 73 -12.28125 44 | 47 -29 45 | 79 -4 46 | 57 #0008 Set REGS[SEL+7].hi32 47 | ff ff 00 ff hi32 = rgba(FF:FF:00:FF) 48 | 87 #0009 ClosePath; Fill (flat color) with REGS[SEL+7] 49 | 35 #0010 ClosePath; MoveTo 50 | 63 -15 51 | bd +30 52 | 01 #0011 LineTo (1 reps) 53 | 6d -10 54 | 82 9b +27.5 55 | 22 #0012 CubeTo (2 reps) 56 | 52 75 -10.6875 57 | 22 9a +26.125 58 | e2 75 -10.125 59 | 72 98 +24.4375 60 | 42 77 -8.75 61 | c2 97 +23.75 62 | (rep) 63 | a2 78 -7.375 64 | 12 97 +23.0625 65 | 52 7a -5.6875 66 | a2 97 +23.625 67 | 77 -5 68 | b3 +25 69 | 01 #0013 LineTo (1 reps) 70 | 81 +0 71 | 82 96 +22.5 72 | 22 #0014 CubeTo (2 reps) 73 | 82 7e -1.5 74 | d2 93 +19.8125 75 | 6a 7e -1.59375 76 | 12 91 +17.0625 77 | ca 7f -0.21875 78 | 62 90 +16.375 79 | (rep) 80 | 2a 81 +1.15625 81 | b2 8f +15.6875 82 | 82 83 +3.5 83 | 52 91 +17.3125 84 | 8b +5 85 | a9 +20 86 | 01 #0015 LineTo (1 reps) 87 | 95 +10 88 | 82 91 +17.5 89 | 22 #0016 CubeTo (2 reps) 90 | ba 87 +7.71875 91 | 7e 8d +13.484375 92 | fe 86 +6.984375 93 | aa 89 +9.65625 94 | 5e 88 +8.359375 95 | fa 88 +8.96875 96 | (rep) 97 | c2 89 +9.75 98 | 4a 88 +8.28125 99 | ba 8c +12.71875 100 | fe 8a +10.984375 101 | 9f +15 102 | 9f +15 103 | 01 #0017 LineTo (1 reps) 104 | a9 +20 105 | 82 8c +12.5 106 | 22 #0018 CubeTo (2 reps) 107 | f2 90 +16.9375 108 | 2a 87 +7.15625 109 | 96 8f +15.578125 110 | 42 82 +2.25 111 | f6 90 +16.953125 112 | 92 81 +1.5625 113 | (rep) 114 | 5a 92 +18.34375 115 | e2 80 +0.875 116 | f2 95 +21.9375 117 | aa 84 +4.65625 118 | b3 +25 119 | 95 +10 120 | 02 #0019 LineTo (2 reps) 121 | bd +30 122 | 82 87 +7.5 123 | (rep) 124 | bd +30 125 | bd +30 126 | 57 #0020 Set REGS[SEL+7].hi32 127 | 00 00 00 ff hi32 = rgba(00:00:00:FF) 128 | 87 #0021 ClosePath; Fill (flat color) with REGS[SEL+7] 129 | 35 #0022 ClosePath; MoveTo 130 | 95 +10 131 | 49 -28 132 | 21 #0023 CubeTo (1 reps) 133 | 95 +10 134 | aa 65 -26.34375 135 | b2 8c +12.6875 136 | 4f -25 137 | a1 +16 138 | 4f -25 139 | 57 #0024 Set REGS[SEL+7].hi32 140 | 00 00 80 ff hi32 = rgba(00:00:80:FF) 141 | 87 #0025 ClosePath; Fill (flat color) with REGS[SEL+7] 142 | 35 #0026 ClosePath; MoveTo 143 | a5 +18 144 | 49 -28 145 | 21 #0027 CubeTo (1 reps) 146 | 52 95 +21.3125 147 | 49 -28 148 | b1 +24 149 | 5a 65 -26.65625 150 | b1 +24 151 | 4f -25 152 | 87 #0028 ClosePath; Fill (flat color) with REGS[SEL+7] 153 | 35 #0029 ClosePath; MoveTo 154 | 95 +10 155 | 59 -20 156 | 23 #0030 CubeTo (3 reps) 157 | b2 86 +6.6875 158 | 59 -20 159 | 89 +4 160 | 5a 6d -18.65625 161 | 89 +4 162 | 5f -17 163 | (rep) 164 | 89 +4 165 | aa 70 -15.34375 166 | b2 86 +6.6875 167 | 65 -14 168 | 95 +10 169 | 65 -14 170 | (rep) 171 | 52 8d +13.3125 172 | 65 -14 173 | a1 +16 174 | aa 70 -15.34375 175 | a1 +16 176 | 5f -17 177 | 87 #0031 ClosePath; Fill (flat color) with REGS[SEL+7] 178 | 35 #0032 ClosePath; MoveTo 179 | a5 +18 180 | 59 -20 181 | 23 #0033 CubeTo (3 reps) 182 | a5 +18 183 | 5a 6a -21.65625 184 | b2 94 +20.6875 185 | 53 -23 186 | b1 +24 187 | 53 -23 188 | (rep) 189 | 52 9b +27.3125 190 | 53 -23 191 | bd +30 192 | 5a 6a -21.65625 193 | bd +30 194 | 59 -20 195 | (rep) 196 | bd +30 197 | aa 6d -18.34375 198 | 52 9b +27.3125 199 | 5f -17 200 | b1 +24 201 | 5f -17 202 | 87 #0034 ClosePath; Fill (flat color) with REGS[SEL+7] 203 | -------------------------------------------------------------------------------- /test/data/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /test/data/gradient.ivg.disassembly: -------------------------------------------------------------------------------- 1 | 89 49 56 47 IconVG Magic identifier 2 | 00 Number of metadata chunks: 0 3 | 98 Set CREG[CSEL-0] to a 4 byte color 4 | 04 0a 8a 00 gradient (NSTOPS=4, CBASE=10, NBASE=10, linear, none) 5 | 0a Set CSEL = 10 6 | 4a Set NSEL = 10 7 | ae Set NREG[NSEL-6] to a real number 8 | 8b 88 08 3d 0.03333333 9 | ad Set NREG[NSEL-5] to a real number 10 | 8b 88 88 3c 0.016666666 11 | ac Set NREG[NSEL-4] to a real number 12 | 6b 66 66 3f 0.9000001 13 | ab Set NREG[NSEL-3] to a real number 14 | 00 0 15 | aa Set NREG[NSEL-2] to a real number 16 | 00 0 17 | a9 Set NREG[NSEL-1] to a real number 18 | 00 0 19 | 87 Set CREG[CSEL-0] to a 1 byte color; CSEL++ 20 | 64 RGBA ff0000ff 21 | af Set NREG[NSEL-0] to a real number; NSEL++ 22 | 00 0 23 | 87 Set CREG[CSEL-0] to a 1 byte color; CSEL++ 24 | 14 RGBA 00ff00ff 25 | bf Set NREG[NSEL-0] to a zero-to-one number; NSEL++ 26 | 3c 0.25 27 | 87 Set CREG[CSEL-0] to a 1 byte color; CSEL++ 28 | 04 RGBA 0000ffff 29 | bf Set NREG[NSEL-0] to a zero-to-one number; NSEL++ 30 | 78 0.5 31 | 87 Set CREG[CSEL-0] to a 1 byte color; CSEL++ 32 | 00 RGBA 000000ff 33 | af Set NREG[NSEL-0] to a real number; NSEL++ 34 | 02 1 35 | 00 Set CSEL = 0 36 | 40 Set NSEL = 0 37 | c0 Start path, filled with CREG[CSEL-0]; M (absolute moveTo) 38 | 44 -30 39 | 44 -30 40 | e6 H (absolute horizontal lineTo) 41 | bc +30 42 | e8 V (absolute vertical lineTo) 43 | 5c -18 44 | e6 H (absolute horizontal lineTo) 45 | 44 -30 46 | e1 z (closePath); end path 47 | 98 Set CREG[CSEL-0] to a 4 byte color 48 | 05 4a 8a 00 gradient (NSTOPS=5, CBASE=10, NBASE=10, linear, pad) 49 | 0a Set CSEL = 10 50 | 4a Set NSEL = 10 51 | ae Set NREG[NSEL-6] to a real number 52 | 8b 88 08 3d 0.03333333 53 | ad Set NREG[NSEL-5] to a real number 54 | 8b 88 88 3c 0.016666666 55 | ac Set NREG[NSEL-4] to a real number 56 | 27 22 22 3f 0.63333344 57 | ab Set NREG[NSEL-3] to a real number 58 | 00 0 59 | aa Set NREG[NSEL-2] to a real number 60 | 00 0 61 | a9 Set NREG[NSEL-1] to a real number 62 | 00 0 63 | 87 Set CREG[CSEL-0] to a 1 byte color; CSEL++ 64 | 18 RGBA 00ffffff 65 | af Set NREG[NSEL-0] to a real number; NSEL++ 66 | 00 0 67 | 87 Set CREG[CSEL-0] to a 1 byte color; CSEL++ 68 | 7c RGBA ffffffff 69 | bf Set NREG[NSEL-0] to a zero-to-one number; NSEL++ 70 | 3c 0.25 71 | 87 Set CREG[CSEL-0] to a 1 byte color; CSEL++ 72 | 68 RGBA ff00ffff 73 | bf Set NREG[NSEL-0] to a zero-to-one number; NSEL++ 74 | 78 0.5 75 | 87 Set CREG[CSEL-0] to a 1 byte color; CSEL++ 76 | 7f RGBA 00000000 77 | bf Set NREG[NSEL-0] to a zero-to-one number; NSEL++ 78 | b4 0.75 79 | 87 Set CREG[CSEL-0] to a 1 byte color; CSEL++ 80 | 78 RGBA ffff00ff 81 | af Set NREG[NSEL-0] to a real number; NSEL++ 82 | 02 1 83 | 00 Set CSEL = 0 84 | 40 Set NSEL = 0 85 | c0 Start path, filled with CREG[CSEL-0]; M (absolute moveTo) 86 | 44 -30 87 | 64 -14 88 | e6 H (absolute horizontal lineTo) 89 | bc +30 90 | e8 V (absolute vertical lineTo) 91 | 7c -2 92 | e6 H (absolute horizontal lineTo) 93 | 44 -30 94 | e1 z (closePath); end path 95 | 98 Set CREG[CSEL-0] to a 4 byte color 96 | 04 8a ca 00 gradient (NSTOPS=4, CBASE=10, NBASE=10, radial, reflect) 97 | 0a Set CSEL = 10 98 | 4a Set NSEL = 10 99 | b6 Set NREG[NSEL-6] to a coordinate number 100 | 11 80 0.0625 101 | ad Set NREG[NSEL-5] to a real number 102 | 00 0 103 | bc Set NREG[NSEL-4] to a zero-to-one number 104 | 78 0.5 105 | ab Set NREG[NSEL-3] to a real number 106 | 00 0 107 | b2 Set NREG[NSEL-2] to a coordinate number 108 | 11 80 0.0625 109 | b1 Set NREG[NSEL-1] to a coordinate number 110 | 81 7f -0.5 111 | 87 Set CREG[CSEL-0] to a 1 byte color; CSEL++ 112 | 64 RGBA ff0000ff 113 | af Set NREG[NSEL-0] to a real number; NSEL++ 114 | 00 0 115 | 87 Set CREG[CSEL-0] to a 1 byte color; CSEL++ 116 | 14 RGBA 00ff00ff 117 | bf Set NREG[NSEL-0] to a zero-to-one number; NSEL++ 118 | 3c 0.25 119 | 87 Set CREG[CSEL-0] to a 1 byte color; CSEL++ 120 | 04 RGBA 0000ffff 121 | bf Set NREG[NSEL-0] to a zero-to-one number; NSEL++ 122 | 78 0.5 123 | 87 Set CREG[CSEL-0] to a 1 byte color; CSEL++ 124 | 00 RGBA 000000ff 125 | af Set NREG[NSEL-0] to a real number; NSEL++ 126 | 02 1 127 | 00 Set CSEL = 0 128 | 40 Set NSEL = 0 129 | c0 Start path, filled with CREG[CSEL-0]; M (absolute moveTo) 130 | 44 -30 131 | 84 +2 132 | e6 H (absolute horizontal lineTo) 133 | bc +30 134 | e8 V (absolute vertical lineTo) 135 | 9c +14 136 | e6 H (absolute horizontal lineTo) 137 | 44 -30 138 | e1 z (closePath); end path 139 | 98 Set CREG[CSEL-0] to a 4 byte color 140 | 05 ca ca 00 gradient (NSTOPS=5, CBASE=10, NBASE=10, radial, repeat) 141 | 0a Set CSEL = 10 142 | 4a Set NSEL = 10 143 | b6 Set NREG[NSEL-6] to a coordinate number 144 | 11 80 0.0625 145 | ad Set NREG[NSEL-5] to a real number 146 | 00 0 147 | bc Set NREG[NSEL-4] to a zero-to-one number 148 | 78 0.5 149 | ab Set NREG[NSEL-3] to a real number 150 | 00 0 151 | b2 Set NREG[NSEL-2] to a coordinate number 152 | 11 80 0.0625 153 | b1 Set NREG[NSEL-1] to a coordinate number 154 | 81 7e -1.5 155 | 87 Set CREG[CSEL-0] to a 1 byte color; CSEL++ 156 | 18 RGBA 00ffffff 157 | af Set NREG[NSEL-0] to a real number; NSEL++ 158 | 00 0 159 | 87 Set CREG[CSEL-0] to a 1 byte color; CSEL++ 160 | 7c RGBA ffffffff 161 | bf Set NREG[NSEL-0] to a zero-to-one number; NSEL++ 162 | 3c 0.25 163 | 87 Set CREG[CSEL-0] to a 1 byte color; CSEL++ 164 | 68 RGBA ff00ffff 165 | bf Set NREG[NSEL-0] to a zero-to-one number; NSEL++ 166 | 78 0.5 167 | 87 Set CREG[CSEL-0] to a 1 byte color; CSEL++ 168 | 7f RGBA 00000000 169 | bf Set NREG[NSEL-0] to a zero-to-one number; NSEL++ 170 | b4 0.75 171 | 87 Set CREG[CSEL-0] to a 1 byte color; CSEL++ 172 | 78 RGBA ffff00ff 173 | af Set NREG[NSEL-0] to a real number; NSEL++ 174 | 02 1 175 | 00 Set CSEL = 0 176 | 40 Set NSEL = 0 177 | c0 Start path, filled with CREG[CSEL-0]; M (absolute moveTo) 178 | 44 -30 179 | a4 +18 180 | e6 H (absolute horizontal lineTo) 181 | bc +30 182 | e8 V (absolute vertical lineTo) 183 | bc +30 184 | e6 H (absolute horizontal lineTo) 185 | 44 -30 186 | e1 z (closePath); end path 187 | -------------------------------------------------------------------------------- /src/c/paint.c: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The IconVG Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "./aaa_private.h" 16 | 17 | iconvg_paint_type // 18 | iconvg_paint__type(const iconvg_paint* self) { 19 | return self ? ((iconvg_paint_type)(self->paint_type)) 20 | : ICONVG_PAINT_TYPE__INVALID; 21 | } 22 | 23 | // ---- 24 | 25 | static inline iconvg_nonpremul_color // 26 | iconvg_private_flat_color_as_nonpremul_color(uint32_t u) { 27 | uint32_t ur = 0xFF & (u >> 0); 28 | uint32_t ug = 0xFF & (u >> 8); 29 | uint32_t ub = 0xFF & (u >> 16); 30 | uint32_t ua = 0xFF & (u >> 24); 31 | iconvg_nonpremul_color k; 32 | if (ua == 0) { 33 | k.rgba[0] = 0; 34 | k.rgba[1] = 0; 35 | k.rgba[2] = 0; 36 | k.rgba[3] = 0; 37 | } else if (ua == 0xFF) { 38 | k.rgba[0] = (uint8_t)ur; 39 | k.rgba[1] = (uint8_t)ug; 40 | k.rgba[2] = (uint8_t)ub; 41 | k.rgba[3] = (uint8_t)ua; 42 | } else { 43 | k.rgba[0] = (uint8_t)((ur * 0xFF) / ua); 44 | k.rgba[1] = (uint8_t)((ug * 0xFF) / ua); 45 | k.rgba[2] = (uint8_t)((ub * 0xFF) / ua); 46 | k.rgba[3] = (uint8_t)(ua); 47 | } 48 | return k; 49 | } 50 | 51 | static inline iconvg_premul_color // 52 | iconvg_private_flat_color_as_premul_color(uint32_t u) { 53 | iconvg_premul_color k; 54 | k.rgba[0] = (uint8_t)(u >> 0); 55 | k.rgba[1] = (uint8_t)(u >> 8); 56 | k.rgba[2] = (uint8_t)(u >> 16); 57 | k.rgba[3] = (uint8_t)(u >> 24); 58 | return k; 59 | } 60 | 61 | // ---- 62 | 63 | static uint32_t // 64 | iconvg_private_paint__resolve_nonrecursive(const iconvg_paint* self, 65 | uint32_t i) { 66 | uint32_t u = (uint32_t)(self->regs[i & 63] >> 32); 67 | uint32_t ur = 0xFF & (u >> 0); 68 | uint32_t ug = 0xFF & (u >> 8); 69 | uint32_t ub = 0xFF & (u >> 16); 70 | uint32_t ua = 0xFF & (u >> 24); 71 | if ((ur <= ua) && (ug <= ua) && (ub <= ua)) { 72 | return u; 73 | } 74 | return 0; 75 | } 76 | 77 | static uint32_t // 78 | iconvg_private_paint__one_byte_color(const iconvg_paint* self, 79 | uint32_t i, 80 | uint32_t u) { 81 | if (u < 0x80) { 82 | return iconvg_private_one_byte_colors[u]; 83 | } else if (u < 0xC0) { 84 | return iconvg_private_peek_u32le( 85 | &self->custom_palette.colors[u & 63].rgba[0]); 86 | } 87 | return iconvg_private_paint__resolve_nonrecursive(self, i + u); 88 | } 89 | 90 | static uint32_t // 91 | iconvg_private_paint__resolve(const iconvg_paint* self, uint32_t i) { 92 | uint32_t u = (uint32_t)(self->regs[i & 63] >> 32); 93 | uint32_t ur = 0xFF & (u >> 0); 94 | uint32_t ug = 0xFF & (u >> 8); 95 | uint32_t ub = 0xFF & (u >> 16); 96 | uint32_t ua = 0xFF & (u >> 24); 97 | if ((ur <= ua) && (ug <= ua) && (ub <= ua)) { 98 | return u; 99 | } else if (ua != 0) { 100 | return 0; 101 | } 102 | 103 | uint32_t p_blend = 255 - ur; 104 | uint32_t p = iconvg_private_paint__one_byte_color(self, i, ug); 105 | uint32_t pr = 0xFF & (p >> 0); 106 | uint32_t pg = 0xFF & (p >> 8); 107 | uint32_t pb = 0xFF & (p >> 16); 108 | uint32_t pa = 0xFF & (p >> 24); 109 | 110 | uint32_t q_blend = ur; 111 | uint32_t q = iconvg_private_paint__one_byte_color(self, i, ub); 112 | uint32_t qr = 0xFF & (q >> 0); 113 | uint32_t qg = 0xFF & (q >> 8); 114 | uint32_t qb = 0xFF & (q >> 16); 115 | uint32_t qa = 0xFF & (q >> 24); 116 | 117 | ur = ((p_blend * pr) + (q_blend * qr) + 128) / 255; 118 | ug = ((p_blend * pg) + (q_blend * qg) + 128) / 255; 119 | ub = ((p_blend * pb) + (q_blend * qb) + 128) / 255; 120 | ua = ((p_blend * pa) + (q_blend * qa) + 128) / 255; 121 | 122 | return (ur << 0) | (ug << 8) | (ub << 16) | (ua << 24); 123 | } 124 | 125 | // ---- 126 | 127 | iconvg_nonpremul_color // 128 | iconvg_paint__flat_color_as_nonpremul_color(const iconvg_paint* self) { 129 | return iconvg_private_flat_color_as_nonpremul_color( 130 | self ? iconvg_private_paint__resolve(self, self->which_regs) : 0); 131 | } 132 | 133 | iconvg_premul_color // 134 | iconvg_paint__flat_color_as_premul_color(const iconvg_paint* self) { 135 | return iconvg_private_flat_color_as_premul_color( 136 | self ? iconvg_private_paint__resolve(self, self->which_regs) : 0); 137 | } 138 | 139 | // ---- 140 | 141 | iconvg_gradient_spread // 142 | iconvg_paint__gradient_spread(const iconvg_paint* self) { 143 | return self ? ((iconvg_gradient_spread)(self->spread)) : 0; 144 | } 145 | 146 | uint32_t // 147 | iconvg_paint__gradient_number_of_stops(const iconvg_paint* self) { 148 | return self ? self->num_stops : 0; 149 | } 150 | 151 | iconvg_nonpremul_color // 152 | iconvg_paint__gradient_stop_color_as_nonpremul_color(const iconvg_paint* self, 153 | uint32_t which_stop) { 154 | uint32_t i = self->which_regs + which_stop; 155 | uint32_t u = ((uint32_t)(self->regs[i & 63] >> 32)); 156 | return iconvg_private_flat_color_as_nonpremul_color(u); 157 | } 158 | 159 | iconvg_premul_color // 160 | iconvg_paint__gradient_stop_color_as_premul_color(const iconvg_paint* self, 161 | uint32_t which_stop) { 162 | uint32_t i = self->which_regs + which_stop; 163 | uint32_t u = ((uint32_t)(self->regs[i & 63] >> 32)); 164 | return iconvg_private_flat_color_as_premul_color(u); 165 | } 166 | 167 | float // 168 | iconvg_paint__gradient_stop_offset(const iconvg_paint* self, 169 | uint32_t which_stop) { 170 | if (!self) { 171 | return 0.0f; 172 | } 173 | uint32_t i = self->which_regs + which_stop; 174 | uint32_t u = ((uint32_t)(self->regs[i & 63])); 175 | return (u >= 0x10000) ? 1.0f : (((float)u) / 0x10000); 176 | } 177 | 178 | iconvg_matrix_2x3_f64 // 179 | iconvg_paint__gradient_transformation_matrix(const iconvg_paint* self) { 180 | if (!self) { 181 | return iconvg_matrix_2x3_f64__make(1.0, 0.0, 0.0, 0.0, 1.0, 0.0); 182 | } 183 | 184 | double s00 = self->transform[0]; 185 | double s01 = self->transform[1]; 186 | double s02 = self->transform[2]; 187 | double s10 = self->transform[3]; 188 | double s11 = self->transform[4]; 189 | double s12 = self->transform[5]; 190 | 191 | // The [s00, s01, s02; s10, s11, s12] matrix transforms from *src* 192 | // coordinates to pattern coordinates. 193 | // 194 | // pat_x = (src_x * s00) + (src_y * s01) + s02 195 | // pat_y = (src_x * s10) + (src_y * s11) + s12 196 | // 197 | // Pattern coordinate space (also known as paint or gradient coordinate 198 | // space) is where linear gradients always range from x=0 to x=1 and radial 199 | // gradients are always center=(0,0) and radius=1. We can't just return this 200 | // matrix to the caller. We need to produce the equivalent [d00, d01, d02; 201 | // d10, d11, d12] matrix that transforms from *dst* coordinates to pattern 202 | // coordinates. Recall that: 203 | // 204 | // src_x = (dst_x * d2s_scale_x) + d2s_bias_x 205 | // src_y = (dst_y * d2s_scale_y) + d2s_bias_y 206 | // 207 | // Combining the above, we can solve for d00, d01, etc such that: 208 | // 209 | // pat_x = (dst_x * d00) + (dst_y * d01) + d02 210 | // pat_y = (dst_x * d10) + (dst_y * d11) + d12 211 | double d00 = s00 * self->d2s_scale_x; 212 | double d01 = s01 * self->d2s_scale_y; 213 | double d02 = (s00 * self->d2s_bias_x) + (s01 * self->d2s_bias_y) + s02; 214 | double d10 = s10 * self->d2s_scale_x; 215 | double d11 = s11 * self->d2s_scale_y; 216 | double d12 = (s10 * self->d2s_bias_x) + (s11 * self->d2s_bias_y) + s12; 217 | 218 | return iconvg_matrix_2x3_f64__make(d00, d01, d02, d10, d11, d12); 219 | } 220 | -------------------------------------------------------------------------------- /src/dart/lib/widgets.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The IconVG Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import 'dart:ui' show Picture, Rect; 16 | 17 | import 'package:flutter/services.dart'; 18 | import 'package:flutter/widgets.dart'; 19 | 20 | import 'decoder.dart'; 21 | 22 | /// Render the specified IconVG [asset] as an icon. 23 | /// 24 | /// This honors the ambient [IconTheme]. 25 | /// 26 | /// The [IconThemeData.color] is used as a one-color custom palette. 27 | class IconVG extends StatelessWidget { 28 | const IconVG(this.asset, { 29 | Key? key, 30 | this.semanticLabel, 31 | }) : super(key: key); 32 | 33 | /// The name of the asset to render. 34 | /// 35 | /// The ambient [DefaultAssetBundle] is used to resolve this name. 36 | final String asset; 37 | 38 | /// Semantic label for the icon. 39 | /// 40 | /// Announced in accessibility modes (e.g TalkBack/VoiceOver). 41 | /// This label does not show in the UI. 42 | /// 43 | /// * [SemanticsProperties.label], which is set to [semanticLabel] in the 44 | /// underlying [Semantics] widget. 45 | final String? semanticLabel; 46 | 47 | @override 48 | Widget build(BuildContext context) { 49 | final IconThemeData theme = IconTheme.of(context); 50 | return Opacity( 51 | opacity: theme.opacity!, 52 | child: IconVGImage( 53 | asset, 54 | palette: [ theme.color! ], 55 | size: Size.square(theme.size!), 56 | semanticLabel: semanticLabel, 57 | ), 58 | ); 59 | } 60 | } 61 | 62 | /// Render the specified IconVG [asset]. 63 | /// 64 | /// If the image is not available in the application's assets, consider fetching 65 | /// the bytes manually and using a [RawIconVGImage]. 66 | class IconVGImage extends StatefulWidget { 67 | const IconVGImage(this.asset, { 68 | Key? key, 69 | this.palette, 70 | this.size, 71 | this.clipBehavior = Clip.hardEdge, 72 | this.semanticLabel, 73 | }) : super(key: key); 74 | 75 | /// The name of the asset to render. 76 | /// 77 | /// The ambient [DefaultAssetBundle] is used to resolve this name. 78 | final String asset; 79 | 80 | /// The custom color palette used to decode the image. 81 | /// 82 | /// Must not have more than 64 colors. 83 | final List? palette; 84 | 85 | /// The target render size. Defaults to the [BoxConstraints.maxHeight]. 86 | final Size? size; 87 | 88 | /// How to clip the image. 89 | /// 90 | /// This can be used to enable anti-aliased clips if the image is rotated, or 91 | /// disable clips entirely (for improved performance) if the image does not 92 | /// need them. 93 | final Clip clipBehavior; 94 | 95 | /// A Semantic description of the image. 96 | /// 97 | /// Used to provide a description of the image to TalkBack on Android, and 98 | /// VoiceOver on iOS. 99 | final String? semanticLabel; 100 | 101 | @override 102 | State createState() => _IconVGImageState(); 103 | } 104 | 105 | class _IconVGImageState extends State { 106 | AssetBundle? _assetBundle; 107 | Uint8List? _bytes; 108 | 109 | @override 110 | void didChangeDependencies() { 111 | super.didChangeDependencies(); 112 | final AssetBundle assetBundle = DefaultAssetBundle.of(context); 113 | if (assetBundle != _assetBundle) { 114 | _assetBundle = assetBundle; 115 | _bytes = null; 116 | _fetchBytes(); 117 | } 118 | } 119 | 120 | @override 121 | void didUpdateWidget(IconVGImage oldWidget) { 122 | super.didUpdateWidget(oldWidget); 123 | if (oldWidget.asset != widget.asset) { 124 | _bytes = null; 125 | _fetchBytes(); 126 | } 127 | } 128 | 129 | Future _fetchBytes() async { 130 | assert(_assetBundle != null); 131 | final ByteData data = await _assetBundle!.load(widget.asset); 132 | if (mounted) { 133 | setState(() { 134 | _bytes = data.buffer.asUint8List(); 135 | }); 136 | } 137 | } 138 | 139 | @override 140 | Widget build(BuildContext context) { 141 | return RawIconVGImage( 142 | bytes: _bytes, 143 | palette: widget.palette, 144 | size: widget.size, 145 | clipBehavior: widget.clipBehavior, 146 | semanticLabel: widget.semanticLabel, 147 | ); 148 | } 149 | } 150 | 151 | /// Render the specified [bytes] as an IconVG image. 152 | /// 153 | /// If the image is an asset, consider using [IconVGImage] instead. 154 | class RawIconVGImage extends StatefulWidget { 155 | const RawIconVGImage({ 156 | Key? key, 157 | this.bytes, 158 | this.palette, 159 | this.size, 160 | this.clipBehavior = Clip.hardEdge, 161 | this.semanticLabel, 162 | }) : super(key: key); 163 | 164 | /// The bytes. Must be in the IconVG format. 165 | final Uint8List? bytes; 166 | 167 | /// The custom color palette used to decode the image. 168 | /// 169 | /// Must not have more than 64 colors. 170 | final List? palette; 171 | 172 | /// The target render size. Defaults to the [BoxConstraints.maxHeight]. 173 | final Size? size; 174 | 175 | /// How to clip the image. 176 | /// 177 | /// This can be used to enable anti-aliased clips if the image is rotated, or 178 | /// disable clips entirely (for improved performance) if the image does not 179 | /// need them. 180 | final Clip clipBehavior; 181 | 182 | /// A Semantic description of the image. 183 | /// 184 | /// Used to provide a description of the image to TalkBack on Android, and 185 | /// VoiceOver on iOS. 186 | final String? semanticLabel; 187 | 188 | @override 189 | State createState() => _RawIconVGImageState(); 190 | } 191 | 192 | class _RawIconVGImageState extends State { 193 | IconVGFile? _image; 194 | double? _decodedHeight; 195 | 196 | @override 197 | void didUpdateWidget(RawIconVGImage oldWidget) { 198 | super.didUpdateWidget(oldWidget); 199 | if (oldWidget.bytes != widget.bytes || 200 | oldWidget.palette != widget.palette || 201 | oldWidget.clipBehavior != widget.clipBehavior) { 202 | // widget.size is handled in build 203 | _image = null; 204 | } 205 | } 206 | 207 | @override 208 | Widget build(BuildContext context) { 209 | if (widget.bytes == null) { 210 | return SizedBox.fromSize(size: widget.size ?? Size.zero); 211 | } 212 | return Semantics( 213 | container: widget.semanticLabel != null, 214 | image: true, 215 | label: widget.semanticLabel ?? '', 216 | child: LayoutBuilder( 217 | builder: (BuildContext context, BoxConstraints constraints) { 218 | final double height = widget.size?.height ?? constraints.maxHeight; 219 | if (_image == null || _decodedHeight != height) { 220 | try { 221 | _image = IconVGFile( 222 | widget.bytes!, 223 | palette: widget.palette, 224 | height: height, 225 | clipBehavior: widget.clipBehavior, 226 | ); 227 | _decodedHeight = height; 228 | } catch (error, stack) { 229 | FlutterError.reportError(FlutterErrorDetails( 230 | exception: error, 231 | stack: stack, 232 | library: 'iconvg', 233 | silent: true, 234 | )); 235 | Widget? message; 236 | assert(() { 237 | message = Text('$error', style: const TextStyle(fontSize: 14.0, color: Color(0xFFFF0000), inherit: false)); 238 | return true; 239 | }()); 240 | return SizedBox.fromSize( 241 | size: widget.size ?? Size.zero, 242 | child: message, // only shows in debug builds 243 | ); 244 | } 245 | } 246 | final Widget result = PicturePainter( 247 | picture: _image!.picture, 248 | rect: _image!.rect, 249 | ); 250 | if (widget.size != null) { 251 | return SizedBox.fromSize( 252 | size: widget.size, 253 | child: result, 254 | ); 255 | } 256 | return AspectRatio( 257 | aspectRatio: _image!.rect.size.aspectRatio, 258 | child: result, 259 | ); 260 | }, 261 | ), 262 | ); 263 | } 264 | } 265 | 266 | class _PicturePainter extends CustomPainter { 267 | const _PicturePainter({ 268 | required this.picture, 269 | required this.rect, 270 | }); 271 | 272 | final Picture picture; 273 | final Rect rect; 274 | 275 | @override 276 | void paint(Canvas canvas, Size size) { 277 | canvas.save(); 278 | canvas.scale(size.width / rect.width, size.height / rect.height); 279 | canvas.translate(-rect.left, -rect.top); 280 | canvas.drawPicture(picture); 281 | canvas.restore(); 282 | } 283 | 284 | @override 285 | bool shouldRepaint(_PicturePainter oldPainter) { 286 | return oldPainter.picture != picture 287 | || oldPainter.rect != rect; 288 | } 289 | } 290 | 291 | /// Render a [Picture] in the widget tree. 292 | class PicturePainter extends StatelessWidget { 293 | const PicturePainter({ 294 | Key? key, 295 | required this.picture, 296 | required this.rect, 297 | }) : super(key: key); 298 | 299 | /// The [Picture] to paint. This is added to the current layer. 300 | final Picture picture; 301 | 302 | /// The segment of the [Picture] to paint. No clip is applied, so parts 303 | /// outside of this rect will still be visible outside the bounds of the 304 | /// widget. 305 | final Rect rect; 306 | 307 | @override 308 | Widget build(BuildContext context) { 309 | return SizedBox.fromSize( 310 | size: rect.size, 311 | child: CustomPaint( 312 | painter: _PicturePainter( 313 | picture: picture, 314 | rect: rect, 315 | ), 316 | ), 317 | ); 318 | } 319 | } 320 | -------------------------------------------------------------------------------- /src/dart/test/iconvg_test.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:flutter_test/flutter_test.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | import 'package:iconvg/decoder.dart'; 7 | import 'package:iconvg/widgets.dart'; 8 | 9 | void main() { 10 | testWidgets('built-in tests', (WidgetTester tester) async { 11 | IconVGFile.tests(); 12 | }); 13 | 14 | testWidgets('simple invalid files', (WidgetTester tester) async { 15 | expect(() => IconVGFile(Uint8List.fromList([])), throwsA(isA())); 16 | expect(() => IconVGFile(Uint8List.fromList([0x00])), throwsA(isA())); 17 | expect(() => IconVGFile(Uint8List.fromList([0x89, 0x49, 0x56])), throwsA(isA())); 18 | expect(() => IconVGFile(Uint8List.fromList([0x89, 0x49, 0x56, 0x46, 0x00])), throwsA(isA())); 19 | }); 20 | 21 | testWidgets('blank.ivg', (WidgetTester tester) async { 22 | IconVGFile(Uint8List.fromList([0x89, 0x49, 0x56, 0x47, 0x00])); 23 | }); 24 | 25 | final Uint8List actionInfoHiResBytes = File('../../test/data/action-info.hires.ivg').readAsBytesSync(); 26 | final Uint8List actionInfoLoResBytes = File('../../test/data/action-info.lores.ivg').readAsBytesSync(); 27 | final Uint8List arcsBytes = File('../../test/data/arcs.ivg').readAsBytesSync(); 28 | final Uint8List cowbellBytes = File('../../test/data/cowbell.ivg').readAsBytesSync(); 29 | final Uint8List ellipticalBytes = File('../../test/data/elliptical.ivg').readAsBytesSync(); 30 | final Uint8List faviconBytes = File('../../test/data/favicon.ivg').readAsBytesSync(); 31 | final Uint8List gradientBytes = File('../../test/data/gradient.ivg').readAsBytesSync(); 32 | final Uint8List lodPolygonBytes = File('../../test/data/lod-polygon.ivg').readAsBytesSync(); 33 | final Uint8List video005PrimitiveBytes = File('../../test/data/video-005.primitive.ivg').readAsBytesSync(); 34 | 35 | testWidgets('action-info.hires.ivg', (WidgetTester tester) async { 36 | await tester.pumpWidget(Center(child: RepaintBoundary(child: RawIconVGImage(bytes: actionInfoHiResBytes)))); 37 | await expectLater(find.byType(RawIconVGImage), matchesGoldenFile('goldens/action-info.hires.default.png')); 38 | await tester.pumpWidget(Center(child: RepaintBoundary(child: RawIconVGImage(bytes: actionInfoHiResBytes, palette: const [Colors.teal])))); 39 | await expectLater(find.byType(RawIconVGImage), matchesGoldenFile('goldens/action-info.hires.teal.png')); 40 | await tester.pumpWidget(Center(child: RepaintBoundary(child: RawIconVGImage(bytes: actionInfoHiResBytes, palette: const [Colors.orange])))); 41 | await expectLater(find.byType(RawIconVGImage), matchesGoldenFile('goldens/action-info.hires.orange.png')); 42 | }); 43 | 44 | testWidgets('action-info.lores.ivg', (WidgetTester tester) async { 45 | await tester.pumpWidget(Center(child: RepaintBoundary(child: RawIconVGImage(bytes: actionInfoLoResBytes)))); 46 | await expectLater(find.byType(RawIconVGImage), matchesGoldenFile('goldens/action-info.lores.default.png')); 47 | await tester.pumpWidget(Center(child: RepaintBoundary(child: RawIconVGImage(bytes: actionInfoLoResBytes, palette: const [Colors.teal])))); 48 | await expectLater(find.byType(RawIconVGImage), matchesGoldenFile('goldens/action-info.lores.teal.png')); 49 | await tester.pumpWidget(Center(child: RepaintBoundary(child: RawIconVGImage(bytes: actionInfoLoResBytes, palette: const [Colors.orange])))); 50 | await expectLater(find.byType(RawIconVGImage), matchesGoldenFile('goldens/action-info.lores.orange.png')); 51 | }); 52 | 53 | testWidgets('arcs.ivg', (WidgetTester tester) async { 54 | await tester.pumpWidget(Center(child: RepaintBoundary(child: RawIconVGImage(bytes: arcsBytes)))); 55 | await expectLater(find.byType(RawIconVGImage), matchesGoldenFile('goldens/arcs.default.png')); 56 | await tester.pumpWidget(Center(child: RepaintBoundary(child: RawIconVGImage(bytes: arcsBytes, size: const Size(512.0, 512.0))))); 57 | await expectLater(find.byType(RawIconVGImage), matchesGoldenFile('goldens/arcs.512x512.png')); 58 | await tester.pumpWidget(Center(child: RepaintBoundary(child: RawIconVGImage(bytes: arcsBytes, size: const Size(1024.0, 128.0))))); 59 | await expectLater(find.byType(RawIconVGImage), matchesGoldenFile('goldens/arcs.1024x128.png')); 60 | await tester.pumpWidget(Center(child: RepaintBoundary(child: RawIconVGImage(bytes: arcsBytes, size: const Size(128.0, 1024.0))))); 61 | await expectLater(find.byType(RawIconVGImage), matchesGoldenFile('goldens/arcs.128x1024.png')); 62 | }); 63 | 64 | testWidgets('cowbell.ivg', (WidgetTester tester) async { 65 | await tester.pumpWidget(Center(child: RepaintBoundary(child: RawIconVGImage(bytes: cowbellBytes)))); 66 | await expectLater(find.byType(RawIconVGImage), matchesGoldenFile('goldens/cowbell.default.png')); 67 | }); 68 | 69 | testWidgets('elliptical.ivg', (WidgetTester tester) async { 70 | await tester.pumpWidget(Center(child: RepaintBoundary(child: RawIconVGImage(bytes: ellipticalBytes)))); 71 | await expectLater(find.byType(RawIconVGImage), matchesGoldenFile('goldens/elliptical.default.png')); 72 | await tester.pumpWidget(Center(child: RepaintBoundary(child: Transform.rotate(angle: 1.0, child: Padding(padding: const EdgeInsets.all(96.0), child: RawIconVGImage(bytes: ellipticalBytes)))))); 73 | await expectLater(find.byType(RepaintBoundary), matchesGoldenFile('goldens/elliptical.rotated.default.png')); 74 | await tester.pumpWidget(Center(child: RepaintBoundary(child: Transform.rotate(angle: 1.0, child: Padding(padding: const EdgeInsets.all(96.0), child: RawIconVGImage(bytes: ellipticalBytes, clipBehavior: Clip.none)))))); 75 | await expectLater(find.byType(RepaintBoundary), matchesGoldenFile('goldens/elliptical.rotated.noclip.png')); 76 | await tester.pumpWidget(Center(child: RepaintBoundary(child: Transform.rotate(angle: 1.0, child: Padding(padding: const EdgeInsets.all(96.0), child: RawIconVGImage(bytes: ellipticalBytes, clipBehavior: Clip.antiAlias)))))); 77 | await expectLater(find.byType(RepaintBoundary), matchesGoldenFile('goldens/elliptical.rotated.aa.png')); 78 | await tester.pumpWidget(Center(child: RepaintBoundary(child: Transform.rotate(angle: 1.0, child: Padding(padding: const EdgeInsets.all(96.0), child: RawIconVGImage(bytes: ellipticalBytes, clipBehavior: Clip.antiAliasWithSaveLayer)))))); 79 | await expectLater(find.byType(RepaintBoundary), matchesGoldenFile('goldens/elliptical.rotated.aaWithSaveLayer.png')); 80 | }); 81 | 82 | testWidgets('favicon.ivg', (WidgetTester tester) async { 83 | await tester.pumpWidget(Center(child: RepaintBoundary(child: RawIconVGImage(bytes: faviconBytes)))); 84 | await expectLater(find.byType(RawIconVGImage), matchesGoldenFile('goldens/favicon.default.png')); 85 | await tester.pumpWidget(Center(child: RepaintBoundary(child: RawIconVGImage(bytes: faviconBytes, palette: const [Colors.teal])))); 86 | await expectLater(find.byType(RawIconVGImage), matchesGoldenFile('goldens/favicon.teal.png')); 87 | await tester.pumpWidget(Center(child: RepaintBoundary(child: RawIconVGImage(bytes: faviconBytes, palette: const [Colors.pink])))); 88 | await expectLater(find.byType(RawIconVGImage), matchesGoldenFile('goldens/favicon.pink.png')); 89 | }); 90 | 91 | testWidgets('gradient.ivg', (WidgetTester tester) async { 92 | await tester.pumpWidget(Center(child: RepaintBoundary(child: RawIconVGImage(bytes: gradientBytes)))); 93 | await expectLater(find.byType(RawIconVGImage), matchesGoldenFile('goldens/gradient.default.png')); 94 | }); 95 | 96 | testWidgets('lod-polygon.ivg', (WidgetTester tester) async { 97 | await tester.pumpWidget(Center(child: RepaintBoundary(child: RawIconVGImage(bytes: lodPolygonBytes)))); 98 | await expectLater(find.byType(RawIconVGImage), matchesGoldenFile('goldens/lod-polygon.default.png')); 99 | await tester.pumpWidget(Center(child: RepaintBoundary(child: RawIconVGImage(bytes: lodPolygonBytes, size: const Size(10.0, 10.0))))); 100 | await expectLater(find.byType(RawIconVGImage), matchesGoldenFile('goldens/lod-polygon.10.png')); 101 | await tester.pumpWidget(Center(child: RepaintBoundary(child: RawIconVGImage(bytes: lodPolygonBytes, size: const Size(75.0, 75.0))))); 102 | await expectLater(find.byType(RawIconVGImage), matchesGoldenFile('goldens/lod-polygon.75.png')); 103 | await tester.pumpWidget(Center(child: RepaintBoundary(child: RawIconVGImage(bytes: lodPolygonBytes, size: const Size(85.0, 85.0))))); 104 | await expectLater(find.byType(RawIconVGImage), matchesGoldenFile('goldens/lod-polygon.85.png')); 105 | await tester.pumpWidget(Center(child: RepaintBoundary(child: RawIconVGImage(bytes: lodPolygonBytes, size: const Size(200.0, 200.0))))); 106 | await expectLater(find.byType(RawIconVGImage), matchesGoldenFile('goldens/lod-polygon.200.png')); 107 | await tester.pumpWidget(Center(child: RepaintBoundary(child: RawIconVGImage(bytes: lodPolygonBytes, size: const Size(75.0, 75.0), palette: [ Colors.green.shade500, Colors.green.shade100, Colors.green.shade900 ])))); 108 | await expectLater(find.byType(RawIconVGImage), matchesGoldenFile('goldens/lod-polygon.75.green.png')); 109 | await tester.pumpWidget(Center(child: RepaintBoundary(child: RawIconVGImage(bytes: lodPolygonBytes, size: const Size(85.0, 85.0), palette: [ Colors.green.shade500, Colors.green.shade100, Colors.green.shade900 ])))); 110 | await expectLater(find.byType(RawIconVGImage), matchesGoldenFile('goldens/lod-polygon.85.green.png')); 111 | }); 112 | 113 | testWidgets('video005Primitive.ivg', (WidgetTester tester) async { 114 | await tester.pumpWidget(Center(child: RepaintBoundary(child: RawIconVGImage(bytes: video005PrimitiveBytes)))); 115 | await expectLater(find.byType(RawIconVGImage), matchesGoldenFile('goldens/video-005-primitive.default.png')); 116 | await tester.pumpWidget(Center(child: RepaintBoundary(child: Transform.rotate(angle: 1.0, child: Padding(padding: const EdgeInsets.all(96.0), child: RawIconVGImage(bytes: video005PrimitiveBytes)))))); 117 | await expectLater(find.byType(RepaintBoundary), matchesGoldenFile('goldens/video-005-primitive.rotated.default.png')); 118 | await tester.pumpWidget(Center(child: RepaintBoundary(child: Transform.rotate(angle: 1.0, child: Padding(padding: const EdgeInsets.all(96.0), child: RawIconVGImage(bytes: video005PrimitiveBytes, clipBehavior: Clip.none)))))); 119 | await expectLater(find.byType(RepaintBoundary), matchesGoldenFile('goldens/video-005-primitive.rotated.noclip.png')); 120 | await tester.pumpWidget(Center(child: RepaintBoundary(child: Transform.rotate(angle: 1.0, child: Padding(padding: const EdgeInsets.all(96.0), child: RawIconVGImage(bytes: video005PrimitiveBytes, clipBehavior: Clip.antiAlias)))))); 121 | await expectLater(find.byType(RepaintBoundary), matchesGoldenFile('goldens/video-005-primitive.rotated.aa.png')); 122 | await tester.pumpWidget(Center(child: RepaintBoundary(child: Transform.rotate(angle: 1.0, child: Padding(padding: const EdgeInsets.all(96.0), child: RawIconVGImage(bytes: video005PrimitiveBytes, clipBehavior: Clip.antiAliasWithSaveLayer)))))); 123 | await expectLater(find.byType(RepaintBoundary), matchesGoldenFile('goldens/video-005-primitive.rotated.aaWithSaveLayer.png')); 124 | }); 125 | } 126 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /src/dart/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /src/c/debug.c: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The IconVG Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "./aaa_private.h" 16 | 17 | static const char* // 18 | iconvg_private_debug_canvas__begin_decode(iconvg_canvas* c, 19 | iconvg_rectangle_f32 dst_rect) { 20 | FILE* f = (FILE*)(c->context.nonconst_ptr2); 21 | if (f) { 22 | fprintf(f, "%sbegin_decode({%g, %g, %g, %g})\n", 23 | ((const char*)(c->context.const_ptr3)), dst_rect.min_x, 24 | dst_rect.min_y, dst_rect.max_x, dst_rect.max_y); 25 | } 26 | iconvg_canvas* wrapped = (iconvg_canvas*)(c->context.nonconst_ptr1); 27 | if (!wrapped) { 28 | return NULL; 29 | } else if (iconvg_private_canvas_sizeof_vtable(wrapped) < 30 | sizeof(iconvg_canvas_vtable)) { 31 | return iconvg_error_invalid_vtable; 32 | } 33 | return (*wrapped->vtable->begin_decode)(wrapped, dst_rect); 34 | } 35 | 36 | static const char* // 37 | iconvg_private_debug_canvas__end_decode(iconvg_canvas* c, 38 | const char* err_msg, 39 | size_t num_bytes_consumed, 40 | size_t num_bytes_remaining) { 41 | FILE* f = (FILE*)(c->context.nonconst_ptr2); 42 | if (f) { 43 | const char* quote = err_msg ? "\"" : ""; 44 | fprintf(f, "%send_decode(%s%s%s, %zu, %zu)\n", 45 | ((const char*)(c->context.const_ptr3)), quote, 46 | err_msg ? err_msg : "NULL", quote, num_bytes_consumed, 47 | num_bytes_remaining); 48 | } 49 | iconvg_canvas* wrapped = (iconvg_canvas*)(c->context.nonconst_ptr1); 50 | if (!wrapped) { 51 | return err_msg; 52 | } else if (iconvg_private_canvas_sizeof_vtable(wrapped) < 53 | sizeof(iconvg_canvas_vtable)) { 54 | return iconvg_error_invalid_vtable; 55 | } 56 | return (*wrapped->vtable->end_decode)(wrapped, err_msg, num_bytes_consumed, 57 | num_bytes_remaining); 58 | } 59 | 60 | static const char* // 61 | iconvg_private_debug_canvas__begin_drawing(iconvg_canvas* c) { 62 | FILE* f = (FILE*)(c->context.nonconst_ptr2); 63 | if (f) { 64 | fprintf(f, "%sbegin_drawing()\n", ((const char*)(c->context.const_ptr3))); 65 | } 66 | iconvg_canvas* wrapped = (iconvg_canvas*)(c->context.nonconst_ptr1); 67 | if (!wrapped) { 68 | return NULL; 69 | } else if (iconvg_private_canvas_sizeof_vtable(wrapped) < 70 | sizeof(iconvg_canvas_vtable)) { 71 | return iconvg_error_invalid_vtable; 72 | } 73 | return (*wrapped->vtable->begin_drawing)(wrapped); 74 | } 75 | 76 | static const char* // 77 | iconvg_private_debug_canvas__end_drawing(iconvg_canvas* c, 78 | const iconvg_paint* p) { 79 | static const char* spread_names[4] = {"none", "pad", "reflect", "repeat"}; 80 | 81 | FILE* f = (FILE*)(c->context.nonconst_ptr2); 82 | if (f) { 83 | switch (iconvg_paint__type(p)) { 84 | case ICONVG_PAINT_TYPE__FLAT_COLOR: { 85 | iconvg_premul_color k = iconvg_paint__flat_color_as_premul_color(p); 86 | fprintf(f, "%send_drawing(flat_color{%02X:%02X:%02X:%02X})\n", 87 | ((const char*)(c->context.const_ptr3)), ((int)(k.rgba[0])), 88 | ((int)(k.rgba[1])), ((int)(k.rgba[2])), ((int)(k.rgba[3]))); 89 | break; 90 | } 91 | 92 | case ICONVG_PAINT_TYPE__LINEAR_GRADIENT: { 93 | fprintf(f, 94 | "%send_drawing(linear_gradient{nstops=%d, spread=%s, ...})\n", 95 | ((const char*)(c->context.const_ptr3)), 96 | ((int)(iconvg_paint__gradient_number_of_stops(p))), 97 | spread_names[iconvg_paint__gradient_spread(p)]); 98 | break; 99 | } 100 | 101 | case ICONVG_PAINT_TYPE__RADIAL_GRADIENT: { 102 | fprintf(f, 103 | "%send_drawing(radial_gradient{nstops=%d, spread=%s, ...})\n", 104 | ((const char*)(c->context.const_ptr3)), 105 | ((int)(iconvg_paint__gradient_number_of_stops(p))), 106 | spread_names[iconvg_paint__gradient_spread(p)]); 107 | break; 108 | } 109 | 110 | case ICONVG_PAINT_TYPE__INVALID: 111 | default: { 112 | return iconvg_error_invalid_paint_type; 113 | } 114 | } 115 | } 116 | iconvg_canvas* wrapped = (iconvg_canvas*)(c->context.nonconst_ptr1); 117 | if (!wrapped) { 118 | return NULL; 119 | } else if (iconvg_private_canvas_sizeof_vtable(wrapped) < 120 | sizeof(iconvg_canvas_vtable)) { 121 | return iconvg_error_invalid_vtable; 122 | } 123 | return (*wrapped->vtable->end_drawing)(wrapped, p); 124 | } 125 | 126 | static const char* // 127 | iconvg_private_debug_canvas__begin_path(iconvg_canvas* c, float x0, float y0) { 128 | FILE* f = (FILE*)(c->context.nonconst_ptr2); 129 | if (f) { 130 | fprintf(f, "%sbegin_path(%g, %g)\n", ((const char*)(c->context.const_ptr3)), 131 | x0, y0); 132 | } 133 | iconvg_canvas* wrapped = (iconvg_canvas*)(c->context.nonconst_ptr1); 134 | if (!wrapped) { 135 | return NULL; 136 | } else if (iconvg_private_canvas_sizeof_vtable(wrapped) < 137 | sizeof(iconvg_canvas_vtable)) { 138 | return iconvg_error_invalid_vtable; 139 | } 140 | return (*wrapped->vtable->begin_path)(wrapped, x0, y0); 141 | } 142 | 143 | static const char* // 144 | iconvg_private_debug_canvas__end_path(iconvg_canvas* c) { 145 | FILE* f = (FILE*)(c->context.nonconst_ptr2); 146 | if (f) { 147 | fprintf(f, "%send_path()\n", ((const char*)(c->context.const_ptr3))); 148 | } 149 | iconvg_canvas* wrapped = (iconvg_canvas*)(c->context.nonconst_ptr1); 150 | if (!wrapped) { 151 | return NULL; 152 | } else if (iconvg_private_canvas_sizeof_vtable(wrapped) < 153 | sizeof(iconvg_canvas_vtable)) { 154 | return iconvg_error_invalid_vtable; 155 | } 156 | return (*wrapped->vtable->end_path)(wrapped); 157 | } 158 | 159 | static const char* // 160 | iconvg_private_debug_canvas__path_line_to(iconvg_canvas* c, 161 | float x1, 162 | float y1) { 163 | FILE* f = (FILE*)(c->context.nonconst_ptr2); 164 | if (f) { 165 | fprintf(f, "%spath_line_to(%g, %g)\n", 166 | ((const char*)(c->context.const_ptr3)), x1, y1); 167 | } 168 | iconvg_canvas* wrapped = (iconvg_canvas*)(c->context.nonconst_ptr1); 169 | if (!wrapped) { 170 | return NULL; 171 | } else if (iconvg_private_canvas_sizeof_vtable(wrapped) < 172 | sizeof(iconvg_canvas_vtable)) { 173 | return iconvg_error_invalid_vtable; 174 | } 175 | return (*wrapped->vtable->path_line_to)(wrapped, x1, y1); 176 | } 177 | 178 | static const char* // 179 | iconvg_private_debug_canvas__path_quad_to(iconvg_canvas* c, 180 | float x1, 181 | float y1, 182 | float x2, 183 | float y2) { 184 | FILE* f = (FILE*)(c->context.nonconst_ptr2); 185 | if (f) { 186 | fprintf(f, "%spath_quad_to(%g, %g, %g, %g)\n", 187 | ((const char*)(c->context.const_ptr3)), x1, y1, x2, y2); 188 | } 189 | iconvg_canvas* wrapped = (iconvg_canvas*)(c->context.nonconst_ptr1); 190 | if (!wrapped) { 191 | return NULL; 192 | } else if (iconvg_private_canvas_sizeof_vtable(wrapped) < 193 | sizeof(iconvg_canvas_vtable)) { 194 | return iconvg_error_invalid_vtable; 195 | } 196 | return (*wrapped->vtable->path_quad_to)(wrapped, x1, y1, x2, y2); 197 | } 198 | 199 | static const char* // 200 | iconvg_private_debug_canvas__path_cube_to(iconvg_canvas* c, 201 | float x1, 202 | float y1, 203 | float x2, 204 | float y2, 205 | float x3, 206 | float y3) { 207 | FILE* f = (FILE*)(c->context.nonconst_ptr2); 208 | if (f) { 209 | fprintf(f, "%spath_cube_to(%g, %g, %g, %g, %g, %g)\n", 210 | ((const char*)(c->context.const_ptr3)), x1, y1, x2, y2, x3, y3); 211 | } 212 | iconvg_canvas* wrapped = (iconvg_canvas*)(c->context.nonconst_ptr1); 213 | if (!wrapped) { 214 | return NULL; 215 | } else if (iconvg_private_canvas_sizeof_vtable(wrapped) < 216 | sizeof(iconvg_canvas_vtable)) { 217 | return iconvg_error_invalid_vtable; 218 | } 219 | return (*wrapped->vtable->path_cube_to)(wrapped, x1, y1, x2, y2, x3, y3); 220 | } 221 | 222 | static const char* // 223 | iconvg_private_debug_canvas__on_metadata_viewbox(iconvg_canvas* c, 224 | iconvg_rectangle_f32 viewbox) { 225 | FILE* f = (FILE*)(c->context.nonconst_ptr2); 226 | if (f) { 227 | fprintf(f, "%son_metadata_viewbox({%g, %g, %g, %g})\n", 228 | ((const char*)(c->context.const_ptr3)), viewbox.min_x, 229 | viewbox.min_y, viewbox.max_x, viewbox.max_y); 230 | } 231 | iconvg_canvas* wrapped = (iconvg_canvas*)(c->context.nonconst_ptr1); 232 | if (!wrapped) { 233 | return NULL; 234 | } else if (iconvg_private_canvas_sizeof_vtable(wrapped) < 235 | sizeof(iconvg_canvas_vtable)) { 236 | return iconvg_error_invalid_vtable; 237 | } 238 | return (*wrapped->vtable->on_metadata_viewbox)(wrapped, viewbox); 239 | } 240 | 241 | static const char* // 242 | iconvg_private_debug_canvas__on_metadata_suggested_palette( 243 | iconvg_canvas* c, 244 | const iconvg_palette* suggested_palette) { 245 | FILE* f = (FILE*)(c->context.nonconst_ptr2); 246 | if (f) { 247 | int j = iconvg_private_last_color_that_isnt_opaque_black(suggested_palette); 248 | if (j < 0) { 249 | fprintf(f, "%son_metadata_suggested_palette(...)\n", 250 | ((const char*)(c->context.const_ptr3))); 251 | } else { 252 | fprintf(f, "%son_metadata_suggested_palette(", 253 | ((const char*)(c->context.const_ptr3))); 254 | for (int i = 0; i <= j; i++) { 255 | fprintf(f, "%02X:%02X:%02X:%02X%s", 256 | ((int)(suggested_palette->colors[i].rgba[0])), 257 | ((int)(suggested_palette->colors[i].rgba[1])), 258 | ((int)(suggested_palette->colors[i].rgba[2])), 259 | ((int)(suggested_palette->colors[i].rgba[3])), 260 | (i < 63) ? ", " : ")\n"); 261 | } 262 | if (j < 63) { 263 | fprintf(f, "...)\n"); 264 | } else { 265 | fprintf(f, ")\n"); 266 | } 267 | } 268 | } 269 | iconvg_canvas* wrapped = (iconvg_canvas*)(c->context.nonconst_ptr1); 270 | if (!wrapped) { 271 | return NULL; 272 | } else if (iconvg_private_canvas_sizeof_vtable(wrapped) < 273 | sizeof(iconvg_canvas_vtable)) { 274 | return iconvg_error_invalid_vtable; 275 | } 276 | return (*wrapped->vtable->on_metadata_suggested_palette)(wrapped, 277 | suggested_palette); 278 | } 279 | 280 | static const iconvg_canvas_vtable // 281 | iconvg_private_debug_canvas_vtable = { 282 | sizeof(iconvg_canvas_vtable), 283 | &iconvg_private_debug_canvas__begin_decode, 284 | &iconvg_private_debug_canvas__end_decode, 285 | &iconvg_private_debug_canvas__begin_drawing, 286 | &iconvg_private_debug_canvas__end_drawing, 287 | &iconvg_private_debug_canvas__begin_path, 288 | &iconvg_private_debug_canvas__end_path, 289 | &iconvg_private_debug_canvas__path_line_to, 290 | &iconvg_private_debug_canvas__path_quad_to, 291 | &iconvg_private_debug_canvas__path_cube_to, 292 | &iconvg_private_debug_canvas__on_metadata_viewbox, 293 | &iconvg_private_debug_canvas__on_metadata_suggested_palette, 294 | }; 295 | 296 | iconvg_canvas // 297 | iconvg_canvas__make_debug(FILE* f, 298 | const char* message_prefix, 299 | iconvg_canvas* wrapped) { 300 | if (wrapped && !wrapped->vtable) { 301 | wrapped = NULL; 302 | } 303 | iconvg_canvas c; 304 | c.vtable = &iconvg_private_debug_canvas_vtable; 305 | memset(&c.context, 0, sizeof(c.context)); 306 | c.context.nonconst_ptr1 = wrapped; 307 | c.context.nonconst_ptr2 = f; 308 | c.context.const_ptr3 = message_prefix ? message_prefix : ""; 309 | return c; 310 | } 311 | -------------------------------------------------------------------------------- /script/gen-release-c.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The IconVG Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build ignore 16 | 17 | package main 18 | 19 | // gen-release-c.go amalgamates src/c/* into a single release/c/foo.c file. 20 | 21 | import ( 22 | "bytes" 23 | "fmt" 24 | "io" 25 | "os" 26 | "path/filepath" 27 | "sort" 28 | "strings" 29 | ) 30 | 31 | func main() { 32 | if err := main1(); err != nil { 33 | os.Stderr.WriteString(err.Error() + "\n") 34 | os.Exit(1) 35 | } 36 | } 37 | 38 | func main1() error { 39 | if !cdIconVGRootDirectory() { 40 | return fmt.Errorf("main: could not find or chdir to the IconVG root directory") 41 | } 42 | 43 | buf := &bytes.Buffer{} 44 | remaining, err := os.ReadFile(filepath.FromSlash("src/c/aaa_package.h")) 45 | if err != nil { 46 | return err 47 | } 48 | for len(remaining) > 0 { 49 | line := remaining 50 | remaining = nil 51 | if i := bytes.IndexByte(line, '\n'); i >= 0 { 52 | line, remaining = line[:i+1], line[i+1:] 53 | } 54 | 55 | if !bytes.HasPrefix(line, hashIncludeDQ) { 56 | buf.Write(line) 57 | } else if err := expand(buf, line); err != nil { 58 | return err 59 | } else { 60 | buf.WriteByte('\n') 61 | } 62 | } 63 | 64 | version := "unsupported-snapshot" 65 | outFilename := filepath.FromSlash("release/c/iconvg-" + version + ".c") 66 | return os.WriteFile(outFilename, buf.Bytes(), 0o644) 67 | } 68 | 69 | var ( 70 | copyright = []byte(`// Copyright `) 71 | dquoteNL = []byte("\"\n") 72 | nl = []byte("\n") 73 | nlNL = []byte("\n\n") 74 | 75 | hashEndif = []byte(`#endif `) 76 | hashIfndef = []byte(`#ifndef `) 77 | hashIncludeAB = []byte(`#include <`) // AB = Angle Bracket. 78 | hashIncludeDQ = []byte(`#include "./`) // DQ = Double Quote. 79 | includeGuard = []byte(`_INCLUDE_GUARD`) 80 | 81 | publicAPIIndex = []byte("// Public API Index (generated by the amalgamation script)\n\n") 82 | 83 | closeCurly = []byte("} ") 84 | closeCurlyNamespace = []byte("} // namespace ") 85 | extern = []byte("extern ") 86 | namespace = []byte("namespace ") 87 | slashSlash = []byte("//") 88 | slashSlashPilcrow = []byte("// ¶") 89 | typedef = []byte("typedef ") 90 | typedefEnum = []byte("typedef enum ") 91 | typedefStruct = []byte("typedef struct ") 92 | ) 93 | 94 | func cdIconVGRootDirectory() bool { 95 | prevWD, err := os.Getwd() 96 | if err != nil { 97 | return false 98 | } 99 | for { 100 | if _, err := os.Stat("iconvg-root-directory.txt"); err == nil { 101 | break 102 | } 103 | if err := os.Chdir(".."); err != nil { 104 | return false 105 | } 106 | if wd, err := os.Getwd(); (err != nil) || (prevWD == wd) { 107 | return false 108 | } else { 109 | prevWD = wd 110 | } 111 | } 112 | return true 113 | } 114 | 115 | func expand(w io.Writer, line []byte) error { 116 | if !bytes.HasSuffix(line, dquoteNL) { 117 | return fmt.Errorf("main: unexpected line %q", line) 118 | } 119 | filename := string(line[len(hashIncludeDQ) : len(line)-len(dquoteNL)]) 120 | if _, err := fmt.Fprintf(w, "// -------------------------------- %s\n", line); err != nil { 121 | return err 122 | } 123 | 124 | src, err := os.ReadFile(filepath.FromSlash("src/c/" + filename)) 125 | if err != nil { 126 | return err 127 | } 128 | 129 | // Skip the `#ifndef ETC_INCLUDE_GUARD` and `// Copyright etc` lines. 130 | src = skipIncludeGuards(src) 131 | if !bytes.HasPrefix(src, copyright) { 132 | return fmt.Errorf("main: %q did not start with the expected copyright header", filename) 133 | } else if i := bytes.Index(src, nlNL); i < 0 { 134 | return fmt.Errorf("main: %q did not start with the expected copyright header", filename) 135 | } else { 136 | src = src[i+1:] 137 | } 138 | 139 | // Process public API. 140 | if filename == "aaa_public.h" { 141 | if src, err = processPublicAPI(src); err != nil { 142 | return err 143 | } 144 | } 145 | 146 | // Skip the `#include "./etc.h"` lines but not `#include `. 147 | sawHashIncludeAB := false 148 | for len(src) > 0 { 149 | if src[0] == '\n' { 150 | src = src[1:] 151 | } else if src[0] != '#' { 152 | break 153 | } 154 | 155 | if bytes.HasPrefix(src, hashIncludeAB) { 156 | if i := bytes.IndexByte(src, '\n'); i < 0 { 157 | return fmt.Errorf("main: %q had unsupported #include", filename) 158 | } else { 159 | if _, err := w.Write(src[:i+1]); err != nil { 160 | return err 161 | } 162 | src = src[i+1:] 163 | sawHashIncludeAB = true 164 | } 165 | } else if bytes.HasPrefix(src, hashIncludeDQ) { 166 | if i := bytes.IndexByte(src, '\n'); i < 0 { 167 | return fmt.Errorf("main: %q had unsupported #include", filename) 168 | } else { 169 | src = src[i+1:] 170 | } 171 | } else { 172 | break 173 | } 174 | } 175 | 176 | if sawHashIncludeAB { 177 | if _, err := w.Write(nl); err != nil { 178 | return err 179 | } 180 | } 181 | _, err = w.Write(src) 182 | return err 183 | } 184 | 185 | func skipIncludeGuards(src []byte) []byte { 186 | unchanged := src 187 | if !bytes.HasPrefix(src, hashIfndef) { 188 | return unchanged 189 | } 190 | 191 | // Remove leading `#ifndef FOO_INCLUDE_GUARD`. 192 | if i := bytes.IndexByte(src, '\n'); i < 0 { 193 | return unchanged 194 | } else if !bytes.HasSuffix(src[:i], includeGuard) { 195 | return unchanged 196 | } else { 197 | src = src[i+1:] 198 | } 199 | 200 | // Remove leading `#define FOO_INCLUDE_GUARD`. 201 | if i := bytes.IndexByte(src, '\n'); i < 0 { 202 | return unchanged 203 | } else if !bytes.HasSuffix(src[:i], includeGuard) { 204 | return unchanged 205 | } else { 206 | src = src[i+1:] 207 | } 208 | 209 | // Trim leading and trailing blank lines. 210 | for (len(src) > 0) && (src[0] == '\n') { 211 | src = src[1:] 212 | } 213 | for (len(src) > 0) && (src[len(src)-1] == '\n') { 214 | src = src[:len(src)-1] 215 | } 216 | 217 | // Remove trailing `#endif // FOO_INCLUDE_GUARD`. 218 | if !bytes.HasSuffix(src, includeGuard) { 219 | return unchanged 220 | } else if i := bytes.LastIndexByte(src, '\n'); i < 0 { 221 | return unchanged 222 | } else if !bytes.HasPrefix(src[i+1:], hashEndif) { 223 | return unchanged 224 | } else { 225 | src = src[:i] 226 | } 227 | 228 | return src 229 | } 230 | 231 | func processPublicAPI(src []byte) ([]byte, error) { 232 | ret := []byte(nil) 233 | if i := bytes.Index(src, publicAPIIndex); i >= 0 { 234 | prefix := src[:i] 235 | suffix := src[i+len(publicAPIIndex):] 236 | index, err := buildPublicAPIIndex(suffix) 237 | if err != nil { 238 | return nil, err 239 | } 240 | ret = append(ret, prefix...) 241 | ret = append(ret, index...) 242 | ret = append(ret, suffix...) 243 | } 244 | return ret, nil 245 | } 246 | 247 | func buildPublicAPIIndex(src []byte) ([]byte, error) { 248 | funks := []string(nil) 249 | strukts := []string(nil) 250 | enums := []string(nil) 251 | others := []string(nil) 252 | pendingEnumValues := []string(nil) 253 | ctorMap := map[string][]string{} 254 | enumMap := map[string][]string{} 255 | methMap := map[string][]string{} 256 | typedefIsEnum := false 257 | 258 | ns := "" 259 | for remaining := src; len(remaining) > 0; { 260 | line := remaining 261 | remaining = nil 262 | if i := bytes.IndexByte(line, '\n'); i >= 0 { 263 | line, remaining = line[:i+1], line[i+1:] 264 | } 265 | 266 | if bytes.HasPrefix(line, namespace) { 267 | line = line[len(namespace):] 268 | if i := bytes.IndexByte(line, ' '); i >= 0 { 269 | ns = string(line[:i]) + "::" 270 | } 271 | continue 272 | } else if bytes.HasPrefix(line, closeCurlyNamespace) { 273 | ns = "" 274 | continue 275 | } 276 | 277 | if bytes.HasPrefix(line, typedef) { 278 | typedefIsEnum = bytes.HasPrefix(line, typedefEnum) 279 | if !typedefIsEnum { 280 | // No-op. 281 | } else if len(pendingEnumValues) != 0 { 282 | return nil, fmt.Errorf("main: no matching enum type for values %q", pendingEnumValues) 283 | } else { 284 | pendingEnumValues = []string(nil) 285 | } 286 | } 287 | 288 | if i := bytes.Index(line, slashSlashPilcrow); i < 0 { 289 | continue 290 | } else { 291 | line = bytes.TrimSpace(line[:i]) 292 | } 293 | 294 | for (len(line) > 0) && (line[0] <= ' ') { 295 | line = line[1:] 296 | } 297 | if (len(line) == 0) || (line[0] == '/') { 298 | continue 299 | } 300 | 301 | if funkName := looksLikePublicAPIFunction(line); funkName != "" { 302 | funkName = ns + funkName 303 | if i := strings.Index(funkName, "__make"); i >= 0 { 304 | typName := strings.Replace(funkName[:i], "::", "_", -1) 305 | ctorMap[typName] = append(ctorMap[typName], funkName) 306 | continue 307 | } else if i := strings.Index(funkName, "__"); i >= 0 { 308 | typName := funkName[:i] 309 | methMap[typName] = append(methMap[typName], funkName) 310 | continue 311 | } 312 | funks = append(funks, funkName) 313 | continue 314 | } else if typName := looksLikePublicAPIType(line); typName != "" { 315 | typName = ns + typName 316 | if typedefIsEnum { 317 | enums = append(enums, typName) 318 | enumMap[typName] = pendingEnumValues 319 | pendingEnumValues = []string(nil) 320 | } else { 321 | strukts = append(strukts, typName) 322 | } 323 | continue 324 | } 325 | 326 | if i := bytes.IndexByte(line, '='); i >= 0 { 327 | line = bytes.TrimSpace(line[:i]) 328 | if !typedefIsEnum { 329 | return nil, fmt.Errorf("main: %q not within an enum", line) 330 | } 331 | pendingEnumValues = append(pendingEnumValues, string(line)) 332 | continue 333 | } 334 | 335 | if bytes.HasPrefix(line, extern) { 336 | line = line[1+bytes.LastIndexByte(line, ' '):] 337 | line = bytes.Trim(line, "[];") 338 | others = append(others, string(line)) 339 | continue 340 | } 341 | 342 | return nil, fmt.Errorf("main: unrecognized public API line %q", line) 343 | } 344 | 345 | sort.Strings(funks) 346 | sort.Strings(strukts) 347 | sort.Strings(enums) 348 | sort.Strings(others) 349 | 350 | b := &bytes.Buffer{} 351 | b.WriteString("// Public API Index.\n") 352 | 353 | if len(funks) > 0 { 354 | b.WriteString("//\n// Functions (-):\n") 355 | for _, funk := range funks { 356 | fmt.Fprintf(b, "// - %s\n", funk) 357 | } 358 | } 359 | 360 | if len(strukts) > 0 { 361 | b.WriteString("//\n// Data structures (-), their constructors (*) and their methods (+):\n") 362 | for _, strukt := range strukts { 363 | fmt.Fprintf(b, "// - %s\n", strukt) 364 | 365 | ctors := ctorMap[strukt] 366 | sort.Strings(ctors) 367 | for _, ctor := range ctors { 368 | fmt.Fprintf(b, "// * %s\n", ctor) 369 | } 370 | delete(ctorMap, strukt) 371 | 372 | meths := methMap[strukt] 373 | sort.Strings(meths) 374 | for _, meth := range meths { 375 | fmt.Fprintf(b, "// + %s\n", meth) 376 | } 377 | delete(methMap, strukt) 378 | } 379 | } 380 | 381 | if len(enums) > 0 { 382 | b.WriteString("//\n// Enumerations (-), their constructors (*) and their values (=):\n") 383 | for _, enum := range enums { 384 | fmt.Fprintf(b, "// - %s\n", enum) 385 | 386 | ctors := ctorMap[enum] 387 | sort.Strings(ctors) 388 | for _, ctor := range ctors { 389 | fmt.Fprintf(b, "// * %s\n", ctor) 390 | } 391 | delete(ctorMap, enum) 392 | 393 | values := enumMap[enum] 394 | sort.Strings(values) 395 | for _, value := range values { 396 | fmt.Fprintf(b, "// = %s\n", value) 397 | } 398 | delete(enumMap, enum) 399 | } 400 | } 401 | 402 | if len(others) > 0 { 403 | b.WriteString("//\n// Other globals (-):\n") 404 | for _, other := range others { 405 | fmt.Fprintf(b, "// - %s\n", other) 406 | } 407 | } 408 | 409 | if len(ctorMap) != 0 { 410 | k, v := biggestKey(ctorMap) 411 | return nil, fmt.Errorf("main: no matching return type %q for constructors %q", k, v) 412 | } 413 | if len(enumMap) != 0 { 414 | k, v := biggestKey(enumMap) 415 | return nil, fmt.Errorf("main: no matching enum type %q for values %q", k, v) 416 | } 417 | if len(methMap) != 0 { 418 | k, v := biggestKey(methMap) 419 | return nil, fmt.Errorf("main: no matching receiver type %q for methods %q", k, v) 420 | } 421 | if len(pendingEnumValues) != 0 { 422 | return nil, fmt.Errorf("main: no matching enum type for values %q", pendingEnumValues) 423 | } 424 | b.WriteString("\n") 425 | return b.Bytes(), nil 426 | } 427 | 428 | func looksLikePublicAPIFunction(src []byte) (name string) { 429 | if i := bytes.IndexByte(src, '('); i < 0 { 430 | return "" 431 | } else { 432 | src = src[:i] 433 | } 434 | if (len(src) == 0) || (bytes.IndexByte(src, ' ') >= 0) { 435 | return "" 436 | } 437 | return string(src) 438 | } 439 | 440 | func looksLikePublicAPIType(src []byte) (name string) { 441 | if bytes.HasPrefix(src, closeCurly) { 442 | src = src[len(closeCurly):] 443 | } else if bytes.HasPrefix(src, typedefStruct) { 444 | src = src[len(typedefStruct):] 445 | } else { 446 | return "" 447 | } 448 | 449 | if i := bytes.IndexByte(src, ';'); i < 0 { 450 | return "" 451 | } else { 452 | src = src[:i] 453 | } 454 | 455 | i := 1 + bytes.LastIndexByte(src, ' ') 456 | return string(src[i:]) 457 | } 458 | 459 | // biggestKey returns an element of m, deterministically (as map iteration 460 | // order is non-deterministic). 461 | func biggestKey(m map[string][]string) (key string, val []string) { 462 | for k, v := range m { 463 | if k >= key { 464 | key, val = k, v 465 | } 466 | } 467 | return key, val 468 | } 469 | --------------------------------------------------------------------------------