[docs] Port markdown to Docsy
This CL lands copies all the documentation in /site into /site2 but also adds frontmatter to each page. Additionally it adds a Hugo `config.toml` file. Once the new documentation server is live the original /site directory will be removed and /site2 will be renamed /site. Bugs: skia:11799 Change-Id: Ic300cf5c2a2a8fa2f9acc3455251bf818cb96a52 Docs-Preview: https://skia.org/?cl=386116 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/386116 Reviewed-by: Joe Gregorio <jcgregorio@google.com>
This commit is contained in:
parent
9753413043
commit
02f72022c5
17
site2/_index.html
Normal file
17
site2/_index.html
Normal file
@ -0,0 +1,17 @@
|
||||
+++
|
||||
title = "Skia"
|
||||
linkTitle = "Skia"
|
||||
|
||||
+++
|
||||
|
||||
{{< blocks/cover title="Welcome to Skia: The 2D Graphics Library" image_anchor="top" height="full" color="orange" >}}
|
||||
<div class="mx-auto">
|
||||
<a class="btn btn-lg btn-primary mr-3 mb-4" href="{{< relref "/docs" >}}">
|
||||
Learn More <i class="fas fa-arrow-alt-circle-right ml-2"></i>
|
||||
</a>
|
||||
<p class="h3 mt-5">Skia is an open source 2D graphics library which provides common APIs that work across a variety of hardware and software platforms. It serves as the graphics engine for Google Chrome and Chrome OS, Android, Flutter, Mozilla Firefox and Firefox OS, and many other products.
|
||||
</p>
|
||||
</div>
|
||||
{{< /blocks/cover >}}
|
||||
|
||||
|
49
site2/about/_index.html
Normal file
49
site2/about/_index.html
Normal file
@ -0,0 +1,49 @@
|
||||
---
|
||||
title: About Skia
|
||||
linkTitle: About
|
||||
menu:
|
||||
main:
|
||||
weight: 10
|
||||
---
|
||||
|
||||
{{< blocks/cover title="About Skia" image_anchor="bottom" height="min">}}
|
||||
|
||||
<p class="h3 mt-5">
|
||||
Skia is an open source 2D graphics library which provides common APIs that
|
||||
work across a variety of hardware and software platforms. It serves as the
|
||||
graphics engine for Google Chrome and Chrome OS, Android, Flutter, Mozilla
|
||||
Firefox and Firefox OS, and many other products. Skia is sponsored and managed
|
||||
by Google, but is available for use by anyone under the BSD Free Software
|
||||
License. While engineering of the core components is done by the Skia
|
||||
development team, we consider contributions from any source.
|
||||
</p>
|
||||
{{< /blocks/cover>}}
|
||||
|
||||
{{< blocks/section color="primary">}}
|
||||
|
||||
{{< blocks/feature icon="fa-lightbulb" title="Roadmap" url="https://docs.google.com/document/d/1LSdO3I-IdZVZCtEnW9pu3k_32gfPIcB_YeSn-OJBGZI/edit?usp=sharing" >}}
|
||||
<p class=h4>
|
||||
Check out the high level themes we have on tap the next 6-12 months. Note it is a living
|
||||
document that changes based on the requirements of our users.
|
||||
</p>
|
||||
{{< /blocks/feature >}}
|
||||
|
||||
{{< blocks/feature icon="fa-lightbulb" title="Supported Platforms" >}}
|
||||
<p class=h4>
|
||||
Windows 7, 8, 8.1, 10<br>
|
||||
macOS 10.10.5 or later<br>
|
||||
iOS 8 or later<br>
|
||||
Android 4.1 (JellyBean) or later<br>
|
||||
Ubuntu 14.04+, Debian 8+, openSUSE 13.3+, or Fedora Linux 24+
|
||||
</p>
|
||||
|
||||
{{< /blocks/feature >}}
|
||||
|
||||
|
||||
{{< blocks/feature icon="fas fa-envelope-square" title="Subscribe"
|
||||
url="https://twitter.com/docsydocs" >}}
|
||||
<p class=h4>
|
||||
Join the mailing list to keep up to date.
|
||||
</p>
|
||||
{{< /blocks/feature >}}
|
||||
{{< /blocks/section>}}
|
4
site2/blog/_index.md
Normal file
4
site2/blog/_index.md
Normal file
@ -0,0 +1,4 @@
|
||||
---
|
||||
title: 'News'
|
||||
linkTitle: 'News'
|
||||
---
|
5
site2/blog/news/_index.md
Normal file
5
site2/blog/news/_index.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
title: 'News About Skia'
|
||||
linkTitle: 'Blog'
|
||||
weight: 10
|
||||
---
|
7
site2/blog/news/first-post.md
Executable file
7
site2/blog/news/first-post.md
Executable file
@ -0,0 +1,7 @@
|
||||
---
|
||||
title: 'The first blog post'
|
||||
linkTitle: 'First blog post'
|
||||
date: 2021-03-19
|
||||
description: >
|
||||
This is a placeholder page until our first official blog post.
|
||||
---
|
178
site2/config.toml
Normal file
178
site2/config.toml
Normal file
@ -0,0 +1,178 @@
|
||||
baseURL = "/"
|
||||
title = "Skia"
|
||||
|
||||
enableRobotsTXT = false
|
||||
|
||||
# Hugo allows theme composition (and inheritance). The precedence is from left to right.
|
||||
theme = ["docsy"]
|
||||
|
||||
# Will give values to .Lastmod etc.
|
||||
enableGitInfo = false
|
||||
|
||||
disableKinds = ["taxonomy", "taxonomyTerm"]
|
||||
|
||||
# Highlighting config
|
||||
pygmentsCodeFences = true
|
||||
pygmentsUseClasses = false
|
||||
# Use the new Chroma Go highlighter in Hugo.
|
||||
pygmentsUseClassic = false
|
||||
#pygmentsOptions = "linenos=table"
|
||||
# See https://help.farbox.com/pygments.html
|
||||
pygmentsStyle = "tango"
|
||||
|
||||
# Configure how URLs look like per section.
|
||||
[permalinks]
|
||||
blog = "/:section/:year/:month/:day/:slug/"
|
||||
|
||||
## Configuration for BlackFriday markdown parser: https://github.com/russross/blackfriday
|
||||
[blackfriday]
|
||||
plainIDAnchors = true
|
||||
hrefTargetBlank = true
|
||||
angledQuotes = false
|
||||
latexDashes = true
|
||||
|
||||
# Image processing configuration.
|
||||
[imaging]
|
||||
resampleFilter = "CatmullRom"
|
||||
quality = 75
|
||||
anchor = "smart"
|
||||
|
||||
[services]
|
||||
[services.googleAnalytics]
|
||||
# Comment out the next line to disable GA tracking. Also disables the feature described in [params.ui.feedback].
|
||||
id = "UA-18058-16"
|
||||
|
||||
# Language configuration
|
||||
|
||||
[languages]
|
||||
[languages.en]
|
||||
title = "Skia"
|
||||
description = "2D Graphics Library"
|
||||
languageName ="English"
|
||||
# Weight used for sorting.
|
||||
weight = 1
|
||||
|
||||
[markup]
|
||||
[markup.goldmark]
|
||||
[markup.goldmark.renderer]
|
||||
unsafe = true
|
||||
[markup.highlight]
|
||||
# See a complete list of available styles at https://xyproto.github.io/splash/docs/all.html
|
||||
style = "tango"
|
||||
# Uncomment if you want your chosen highlight style used for code blocks without a specified language
|
||||
# guessSyntax = "true"
|
||||
|
||||
# Everything below this are Site Params
|
||||
|
||||
[params]
|
||||
copyright = "Google"
|
||||
privacy_policy = "https://policies.google.com/privacy"
|
||||
|
||||
# First one is picked as the Twitter card image if not set on page.
|
||||
# images = ["images/project-illustration.png"]
|
||||
|
||||
# Menu title if your navbar has a versions selector to access old versions of your site.
|
||||
# This menu appears only if you have at least one [params.versions] set.
|
||||
version_menu = "Releases"
|
||||
|
||||
# Flag used in the "version-banner" partial to decide whether to display a
|
||||
# banner on every page indicating that this is an archived version of the docs.
|
||||
# Set this flag to "true" if you want to display the banner.
|
||||
archived_version = false
|
||||
|
||||
# The version number for the version of the docs represented in this doc set.
|
||||
# Used in the "version-banner" partial to display a version number for the
|
||||
# current doc set.
|
||||
version = "0.0"
|
||||
|
||||
# A link to latest version of the docs. Used in the "version-banner" partial to
|
||||
# point people to the main doc site.
|
||||
url_latest_version = "https://skia.org"
|
||||
|
||||
# Repository configuration (URLs for in-page links to opening issues and suggesting changes)
|
||||
# github_repo = "https://github.com/google/skia"
|
||||
# An optional link to a related project repo. For example, the sibling repository where your product code lives.
|
||||
#github_project_repo = "https://github.com/google/docsy"
|
||||
|
||||
# Specify a value here if your content directory is not in your repo's root directory
|
||||
#github_subdir = "site2"
|
||||
|
||||
# Uncomment this if you have a newer GitHub repo with "main" as the default branch,
|
||||
# or specify a new value if you want to reference another branch in your GitHub links
|
||||
# github_branch= "main"
|
||||
|
||||
# Google Custom Search Engine ID. Remove or comment out to disable search.
|
||||
gcs_engine_id = "009791159600898516779:8-nlv0iznho"
|
||||
|
||||
# Enable Algolia DocSearch
|
||||
algolia_docsearch = false
|
||||
|
||||
# Enable Lunr.js offline search
|
||||
offlineSearch = false
|
||||
|
||||
# Enable syntax highlighting and copy buttons on code blocks with Prism
|
||||
prism_syntax_highlighting = false
|
||||
|
||||
# User interface configuration
|
||||
[params.ui]
|
||||
# Enable to show the side bar menu in its compact state.
|
||||
sidebar_menu_compact = false
|
||||
# Set to true to disable breadcrumb navigation.
|
||||
breadcrumb_disable = false
|
||||
# Set to true to hide the sidebar search box (the top nav search box will still be displayed if search is enabled)
|
||||
sidebar_search_disable = false
|
||||
# Set to false if you don't want to display a logo (/assets/icons/logo.svg) in the top nav bar
|
||||
navbar_logo = false
|
||||
# Set to true to disable the About link in the site footer
|
||||
footer_about_disable = false
|
||||
|
||||
# Adds a H2 section titled "Feedback" to the bottom of each doc. The responses are sent to Google Analytics as events.
|
||||
# This feature depends on [services.googleAnalytics] and will be disabled if "services.googleAnalytics.id" is not set.
|
||||
# If you want this feature, but occasionally need to remove the "Feedback" section from a single page,
|
||||
# add "hide_feedback: true" to the page's front matter.
|
||||
[params.ui.feedback]
|
||||
enable = false
|
||||
# The responses that the user sees after clicking "yes" (the page was helpful) or "no" (the page was not helpful).
|
||||
yes = 'Glad to hear it! Please <a href="https://bugs.chromium.org/p/skia/issues/entry">tell us how we can improve</a>.'
|
||||
no = 'Sorry to hear that. Please <a href="https://bugs.chromium.org/p/skia/issues/entry">tell us how we can improve</a>.'
|
||||
|
||||
# Adds a reading time to the top of each doc.
|
||||
# If you want this feature, but occasionally need to remove the Reading time from a single page,
|
||||
# add "hide_readingtime: true" to the page's front matter
|
||||
[params.ui.readingtime]
|
||||
enable = false
|
||||
|
||||
[params.links]
|
||||
# End user relevant links. These will show up on left side of footer and in the community page if you have one.
|
||||
[[params.links.user]]
|
||||
name = "User mailing list"
|
||||
url = "https://groups.google.com/g/skia-discuss"
|
||||
icon = "fa fa-envelope"
|
||||
desc = "Discussion and help"
|
||||
#[[params.links.user]]
|
||||
# name ="Twitter"
|
||||
# url = "https://example.org/twitter"
|
||||
# icon = "fab fa-twitter"
|
||||
# desc = "Follow us on Twitter to get the latest news!"
|
||||
#[[params.links.user]]
|
||||
# name = "Stack Overflow"
|
||||
# url = "https://example.org/stack"
|
||||
# icon = "fab fa-stack-overflow"
|
||||
# desc = "Practical questions and curated answers"
|
||||
# Developer relevant links. These will show up on right side of footer and in the community page if you have one.
|
||||
[[params.links.developer]]
|
||||
name = "GitHub"
|
||||
url = "https://github.com/google/skia"
|
||||
icon = "fab fa-github"
|
||||
desc = "Development takes place here!"
|
||||
#[[params.links.developer]]
|
||||
# name = "Slack"
|
||||
# url = "https://example.org/slack"
|
||||
# icon = "fab fa-slack"
|
||||
# desc = "Chat with other project developers"
|
||||
#[[params.links.developer]]
|
||||
# name = "Developer mailing list"
|
||||
# url = "https://example.org/mail"
|
||||
# icon = "fa fa-envelope"
|
||||
# desc = "Discuss development issues around the project"
|
||||
|
85
site2/docs/_index.md
Normal file
85
site2/docs/_index.md
Normal file
@ -0,0 +1,85 @@
|
||||
---
|
||||
title: 'Documentation'
|
||||
linkTitle: 'Documentation'
|
||||
menu:
|
||||
main:
|
||||
weight: 15
|
||||
---
|
||||
|
||||
Skia is an open source 2D graphics library which provides common APIs that work
|
||||
across a variety of hardware and software platforms. It serves as the graphics
|
||||
engine for Google Chrome and Chrome OS, Android, Flutter, Mozilla Firefox and
|
||||
Firefox OS, and many other products.
|
||||
|
||||
Skia is sponsored and managed by Google, but is available for use by anyone
|
||||
under the BSD Free Software License. While engineering of the core components
|
||||
is done by the Skia development team, we consider contributions from any
|
||||
source.
|
||||
|
||||
- Canonical source tree:
|
||||
[skia.googlesource.com/skia](https://skia.googlesource.com/skia).
|
||||
- Issue tracker:
|
||||
[bug.skia.org](https://bug.skia.org/).
|
||||
- Discussion forum:
|
||||
[skia-discuss@googlegroups.com](https://groups.google.com/forum/#!forum/skia-discuss).
|
||||
- API Reference and Overview: [skia.org/user/api](https://skia.org/user/api/).
|
||||
- Skia Fiddle: [fiddle.skia.org](https://fiddle.skia.org/c/@skcanvas_paint).
|
||||
|
||||
## Showcase
|
||||
|
||||
Click on any image below to see the source code that generated the image.
|
||||
|
||||
<table>
|
||||
<tr><th>Shapes</th><th>Bézier Curves</th></tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a href='https://fiddle.skia.org/c/@shapes'><img src='https://fiddle.skia.org/i/@shapes_raster.png'></a>
|
||||
</td>
|
||||
<td>
|
||||
<a href='https://fiddle.skia.org/c/@bezier_curves'><img src='https://fiddle.skia.org/i/@bezier_curves_raster.png'></a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr><th>Translations and Rotations</th><th>Text Rendering</th></tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a href='https://fiddle.skia.org/c/@rotations'><img src='https://fiddle.skia.org/i/@rotations_raster.png'></a>
|
||||
</td>
|
||||
<td>
|
||||
<a href='https://fiddle.skia.org/c/@text_rendering'><img src='https://fiddle.skia.org/i/@text_rendering_raster.png'></a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr><th>Discrete Path Effects</th><th>Composed Path Effects</th></tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a href='https://fiddle.skia.org/c/@discrete_path'><img src='https://fiddle.skia.org/i/@discrete_path_raster.png'></a>
|
||||
</td>
|
||||
<td>
|
||||
<a href='https://fiddle.skia.org/c/@compose_path'><img src='https://fiddle.skia.org/i/@compose_path_raster.png'></a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr><th>Sum Path Effects</th><th>Shaders</th></tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a href='https://fiddle.skia.org/c/@sum_path_effect'><img src='https://fiddle.skia.org/i/@sum_path_effect_raster.png'></a>
|
||||
</td>
|
||||
<td>
|
||||
<a href='https://fiddle.skia.org/c/@shader'><img src='https://fiddle.skia.org/i/@shader_raster.png'></a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## Roadmap
|
||||
|
||||
For a look at high level themes we have on tap the next 6-12 months, view our
|
||||
roadmap [here](https://docs.google.com/document/d/1LSdO3I-IdZVZCtEnW9pu3k_32gfPIcB_YeSn-OJBGZI/edit?usp=sharing).
|
||||
Note it is a living document that changes based on the requirements of our users.
|
||||
|
||||
## Platforms
|
||||
|
||||
- Windows 7, 8, 8.1, 10
|
||||
- macOS 10.10.5 or later
|
||||
- iOS 8 or later
|
||||
- Android 4.1 (JellyBean) or later
|
||||
- Ubuntu 14.04+, Debian 8+, openSUSE 13.3+, or Fedora Linux 24+
|
11
site2/docs/dev/_index.md
Normal file
11
site2/docs/dev/_index.md
Normal file
@ -0,0 +1,11 @@
|
||||
---
|
||||
title: 'Developer Documentation'
|
||||
linkTitle: 'Developers'
|
||||
|
||||
weight: 2
|
||||
menu:
|
||||
main:
|
||||
weight: 100
|
||||
---
|
||||
|
||||
If you want to make changes to the Skia code, this is the place for you.
|
43
site2/docs/dev/chrome/_index.md
Normal file
43
site2/docs/dev/chrome/_index.md
Normal file
@ -0,0 +1,43 @@
|
||||
|
||||
---
|
||||
title: "Skia in Chrome"
|
||||
linkTitle: "Skia in Chrome"
|
||||
|
||||
weight: 7
|
||||
|
||||
---
|
||||
|
||||
|
||||
Changes to the Skia repository will be rolled into Chromium by the AutoRoll bot
|
||||
several times per day.
|
||||
|
||||
If you have a Skia change that needs to be tested in Chrome, or which requires
|
||||
associated changes in that repository, see the guides in this section for tips
|
||||
on execution.
|
||||
|
||||
For problems in Chromium related to Skia rolls:
|
||||
|
||||
* Go to https://autoroll.skia.org/r/skia-autoroll. Login with google.com
|
||||
account and click the STOP button to pause new rolls.
|
||||
* Revert the offending DEPS roll.
|
||||
* If an obvious owner cannot be found in the list of CLs, assign to the Skia
|
||||
Gardener, listed in the gardeners widget on https://status.skia.org and as
|
||||
a reviewer on the roll CL.
|
||||
* If the Skia Gardener cannot be assigned, cc them and assign the issue to hcm@.
|
||||
|
||||
For more tips on bug triage and labeling, see the [Issue Tracker page](../../user/issue-tracker/).
|
||||
|
||||
Branching for Chrome
|
||||
--------------------
|
||||
|
||||
Every 6 weeks, we cut a new branch in Skia to reflect the new release branch in
|
||||
Chrome, eg. [refs/heads/chrome/m75](https://skia.googlesource.com/skia/+/chrome/m75).
|
||||
This process is simplified by running [tools/chrome_release_branch](https://skia.googlesource.com/skia/+/7a5b6ec0f6c01d3039e3ec30de6f8065ffc8aac4/tools/chrome_release_branch.py').
|
||||
This script handles creation of the branch itself, as well as associated
|
||||
housekeeping like updating the Chrome milestone number for the next release,
|
||||
setting up the [commit queue]('https://skia.googlesource.com/skia/+/infra/config/commit-queue.cfg')
|
||||
for the new branch. For example:
|
||||
|
||||
tools/chrome_release_branch <commit hash>
|
||||
|
||||
|
93
site2/docs/dev/chrome/blink.md
Normal file
93
site2/docs/dev/chrome/blink.md
Normal file
@ -0,0 +1,93 @@
|
||||
|
||||
---
|
||||
title: "Blink layout tests"
|
||||
linkTitle: "Blink layout tests"
|
||||
|
||||
---
|
||||
|
||||
|
||||
How to land Skia changes that change Blink layout test results.
|
||||
|
||||
Changes that affect a small number of layout test results
|
||||
---------------------------------------------------------
|
||||
Changes affecting fewer than ~20 layout tests can be rebaselined without
|
||||
special coordination with the Blink gardener using these steps:
|
||||
|
||||
1. Prepare your Skia change, taking note of which layout tests will turn red
|
||||
\(see http://www.chromium.org/developers/testing/webkit-layout-tests for more
|
||||
detail on running the Blink layout tests\).
|
||||
2. Check in your code to the Skia repo.
|
||||
3. Ahead of the Skia auto roll including your change, manually push a change to the
|
||||
Blink LayoutTests/TestExpectations [file](https://chromium.googlesource.com/chromium/src/+/master/third_party/blink/web_tests/TestExpectations), flagging tests expected to fail as a result of your change as follows:
|
||||
foo/bar/test-name.html [ Failure Pass ] # Needs rebaseline
|
||||
|
||||
4. Wait for the Skia roll to land successfully.
|
||||
5. Check in another change to the Blink TestExpectations file removing all the
|
||||
skipped test expectations you add earlier, an run `git cl rebaseline` which will prompt the automatic rebaseline.
|
||||
|
||||
|
||||
|
||||
Changes that affect a large number of test results
|
||||
--------------------------------------------------
|
||||
Where a 'large number' or 'many' means more than about 20.
|
||||
Follow the instructions below:
|
||||
|
||||
In the following the term 'code suppression' means a build flag \(a\.k\.a\. define\).
|
||||
Such code suppressions should be given a name with the form SK\_IGNORE\_xxx\_FIX.
|
||||
|
||||
Updating the version of Skia in Chromium is called a 'roll'.
|
||||
The Auto Roll Bot performs this roll multiple times per day, and can also be done manually.
|
||||
See https://chromium.googlesource.com/chromium/src/+log/master/DEPS and search for skia\-deps\-roller.
|
||||
|
||||
### Setup
|
||||
#### Code suppression does not yet exist \- Direct method
|
||||
1. Make a change in Skia which will change many Blink layout tests.
|
||||
2. Put the change behind a code suppression.
|
||||
3. Check in the change to the Skia repository.
|
||||
4. Manually roll Skia or append the autoroll with the code suppression to
|
||||
Chromium's 'skia/chromium\_skia\_defines\.gypi'
|
||||
|
||||
#### Code suppression does not yet exist \- Alternate method
|
||||
1. Add code suppression to Chromium's 'skia/chromium\_skia\_defines\.gypi' before making code
|
||||
changes in Skia.
|
||||
2. Make a change in Skia which will change many Blink layout tests.
|
||||
3. Put the change behind a code suppression.
|
||||
4. Check in the change to the Skia repository.
|
||||
5. Wait for Skia roll into Chromium.
|
||||
|
||||
#### Code suppression exists in header
|
||||
1. Remove code suppression from header file in Chromium and add code suppression to
|
||||
Chromium's 'skia/chromium\_skia\_defines\.gypi'.
|
||||
The code suppression cannot be in a header file and a defined in a gyp file at the
|
||||
same time or a multiple definition warning will be treated as an error and break
|
||||
the Chromium build.
|
||||
|
||||
### Rebaseline
|
||||
1. Choose a time when the Blink tree is likely to be quiet. Avoid PST afternoons in
|
||||
particular. The bigger the change, the more important this is. Regardless,
|
||||
determine who the Blink gardener is and notify them. You will be making the
|
||||
Chromium\.WebKit tree very red for an extended period, and the gardener needs to
|
||||
know that they are not expected to fix it.
|
||||
2. Create a CL removing the code suppression from Chromium's
|
||||
skia/chromium\_skia\_defines\.gypi while simultaneously adding [ NeedsRebaseline ]
|
||||
lines to Blink's LayoutTests/TestExpectations [file](https://chromium.googlesource.com/chromium/src/+/master/third_party/blink/web_tests/TestExpectations).
|
||||
Then the auto rebaseline bot will take care of the work of actually checking in the
|
||||
new images. This is generally acceptable for up to 600 or so rebaselined images.
|
||||
Above that you might still use [ NeedsRebaseline ], but it's best to coordinate with
|
||||
the gardener. This should go through the CQ cleanly.
|
||||
3. Be careful with tests that are already failing or flakey. These may or may not need
|
||||
to be rebaselined and flakey tests should not be removed from TestExpectations
|
||||
regardless. In such cases revert the TestExpectations changes before committing.
|
||||
4. If you are not the one handling the cleanup step, please open a Skia Issue of the
|
||||
form
|
||||
Title: "Remove code suppression SK\_IGNORE\_xxx\_FIX\."
|
||||
Comment: "Code suppression SK\_IGNORE\_xxx\_FIX rebaselined with Blink revision
|
||||
123456\." and assign it to the individual responsible for the cleanup step.
|
||||
|
||||
### Cleanup
|
||||
1. Remove the now unused old code from Skia and any defines which were introduced
|
||||
to suppress the new code.
|
||||
2. Check in the cleanup change to the Skia repository.
|
||||
3. Wait for Skia roll into Chromium.
|
||||
|
||||
|
41
site2/docs/dev/chrome/changes.md
Normal file
41
site2/docs/dev/chrome/changes.md
Normal file
@ -0,0 +1,41 @@
|
||||
|
||||
---
|
||||
title: "Chrome changes"
|
||||
linkTitle: "Chrome changes"
|
||||
|
||||
---
|
||||
|
||||
|
||||
If your change modifies the Skia API, you may also need to land a change in Chromium.
|
||||
|
||||
The strategy you use to synchronize changes in the Skia and Chromium
|
||||
repositories may differ based on the nature of the change, but in general, we
|
||||
recommend using build flag suppressions \(defines\)\.
|
||||
We also prefer making the old code path opt-in where possible.
|
||||
|
||||
Method 1 \(preferred\) \- Make the old code path opt\-in for Chromium
|
||||
|
||||
* Add new code to Skia, leaving the old code in place.
|
||||
* Deprecate the old code path so that it must be enabled with a flag such as
|
||||
'SK_SUPPORT_LEGACY_XXX'.
|
||||
* Synchronize the above changes in Skia with a Chromium commit to
|
||||
'skia/skia_common.gypi' or 'skia/config/SkUserConfig.h' to enable the
|
||||
deprecated Skia API.
|
||||
* Note that the code suppression cannot exist in both the header file and
|
||||
the gyp file, it should only reside in one location.
|
||||
* Test the new or updated Skia API within Chromium.
|
||||
* Remove the flag and code when the legacy code path is no longer in use.
|
||||
|
||||
Method 2 \- Make the new code path opt\-in for Chromium
|
||||
|
||||
* Add new code to Skia, suppressed by a flag.
|
||||
* Leave the old code path in place.
|
||||
* Set the flag in Chromium's 'skia/skia_common.gypi' or
|
||||
'skia/config/SkUserConfig.h' to enable the new or updated Skia API.
|
||||
* Test the new or updated Skia API within Chromium.
|
||||
* Remove the code suppression \(and code\) when the legacy API is no longer
|
||||
in use.
|
||||
|
||||
If your changes will affect Blink layout tests, see detailed instructions about
|
||||
how to synchronize the changes between Skia, Blink, and Chromium [here](./blink).
|
||||
|
28
site2/docs/dev/chrome/commandbuffer.md
Normal file
28
site2/docs/dev/chrome/commandbuffer.md
Normal file
@ -0,0 +1,28 @@
|
||||
|
||||
---
|
||||
title: "Chromium Command Buffer"
|
||||
linkTitle: "Chromium Command Buffer"
|
||||
|
||||
---
|
||||
|
||||
|
||||
It is possible to run Skia's correctness tool, dm, and benchmarking tool,
|
||||
nanobench, on top of the GL ES interface provided by Chromium's command
|
||||
buffer.
|
||||
|
||||
The Skia tools are always built with this support. They dynamically load
|
||||
the command buffer as a shared library and thus no GYP/GN flags are
|
||||
required.
|
||||
|
||||
The command buffer standalone shared library is built in a Chromium checkout
|
||||
by building the `command_buffer_gles2` target. The command buffer should be
|
||||
built with the `is_component_build` in GN set to false. This will produce a .so,
|
||||
.dylib, or .dll depending on the target OS. This should be copied alongside
|
||||
the dm or nanobench executable built from a Skia repository.
|
||||
|
||||
Both tools have a `commandbuffer` config which can be used with the `--config`
|
||||
option to the tool and will run the tests or benchmarks using the command buffer
|
||||
library. Unit tests in dm always run on all appropriate and available backends
|
||||
regardless of the `--config` flag.
|
||||
|
||||
|
91
site2/docs/dev/chrome/multi_repo_trybots.md
Normal file
91
site2/docs/dev/chrome/multi_repo_trybots.md
Normal file
@ -0,0 +1,91 @@
|
||||
|
||||
---
|
||||
title: "Multiple repo Chromium trybots"
|
||||
linkTitle: "Multiple repo Chromium trybots"
|
||||
|
||||
---
|
||||
|
||||
|
||||
When a proposed Skia change will require a change in Chromium or Blink it is
|
||||
often helpful to locally create the Chromium and Blink changes and test with the
|
||||
proposed Skia change. This often happens with Skia API changes and changes
|
||||
which affect Blink layout tests. While simple to do locally, this explains how
|
||||
to do so on the Chromium trybots.
|
||||
|
||||
Skia only changes
|
||||
-----------------
|
||||
If the Skia patch is already in Gerrit and there are no associated Chromium
|
||||
changes, then it is possible to just run the Chromium trybots. This will apply
|
||||
the Skia patch and run the bot.
|
||||
|
||||
Skia and Chromium changes
|
||||
-------------------------
|
||||
If the Skia patch is already in Gerrit and there are associated Chromium
|
||||
changes, then in the Chromium CL add the following to
|
||||
\<chromium>/src/DEPS in the 'hooks' array.
|
||||
|
||||
{
|
||||
'name': 'fetch_custom_patch',
|
||||
'pattern': '.',
|
||||
'action': [ 'git', '-C', 'src/third_party/skia/',
|
||||
'fetch', 'https://skia.googlesource.com/skia', 'refs/changes/13/10513/13',
|
||||
],
|
||||
},
|
||||
{
|
||||
'name': 'apply_custom_patch',
|
||||
'pattern': '.',
|
||||
'action': ['git', '-C', 'src/third_party/skia/',
|
||||
'-c', 'user.name=Custom Patch', '-c', 'user.email=custompatch@example.com',
|
||||
'cherry-pick', 'FETCH_HEAD',
|
||||
],
|
||||
},
|
||||
|
||||
Modify the 'refs/changes/XX/YYYY/ZZ' to the appropriate values (where YYYY is
|
||||
the numeric change number, ZZ is the patch set number and XX is the last two
|
||||
digits of the numeric change number). This can be seen in the 'Download' link on
|
||||
Gerrit.
|
||||
|
||||
If this is for a project other than Skia, update the checkout directory and
|
||||
fetch source. Note that this can be used multiple times to apply multiple
|
||||
issues.
|
||||
|
||||
An example of this being used can be seen at
|
||||
https://crrev.com/2786433004/#ps1 .
|
||||
|
||||
To test locally, run `gclient runhooks` to update the Skia source code.
|
||||
Note that if your local skia patch in `third_party/skia` isn't clean (e.g., you
|
||||
already applied some patch to it), then `gclient runhooks` won't successfully
|
||||
run. In that case, run `git reset --hard` inside `third_party/skia` before
|
||||
`gclient runhooks`.
|
||||
|
||||
Arbitrary changes
|
||||
-----------------
|
||||
If the patch is to files where the above is not possible, then it is still
|
||||
possible to patch the files manually by adding the following to
|
||||
\<chromium>/src/DEPS in the 'hooks' array just before the 'gyp' hook.
|
||||
|
||||
{
|
||||
'name': 'apply_custom_patch',
|
||||
'pattern': '.',
|
||||
'action': ['python2',
|
||||
'-c', 'from distutils.dir_util import copy_tree; copy_tree("src/patch/", "src/");'
|
||||
],
|
||||
},
|
||||
|
||||
Then, copy all 'out of tree' files into \<chromium>/src/patch/, using the same
|
||||
directory structure used by Chromium. When `gclient runhooks` is run, the files
|
||||
in \<chromium>/src/patch/ will be copied to and overwrite corresponding files in
|
||||
\<chromium>/src/. For example, if changing \<skia>/include/core/SkPath.h, place
|
||||
a copy of the modified SkPath.h at
|
||||
\<chromium>/src/patch/third_party/skia/include/core/SkPath.h.
|
||||
|
||||
An example of this being used can be seen at
|
||||
https://crrev.com/1866773002/#ps20001 .
|
||||
|
||||
|
||||
Try the patch
|
||||
-------------
|
||||
After committing a \<chromium>/src/DEPS or \<chromium>/src/patch/ change
|
||||
locally, `git cl upload` can be used in the usual way. Be sure to add
|
||||
`COMMIT=false` to the issue description to avoid accidentally checking it in.
|
||||
|
23
site2/docs/dev/chrome/repo.md
Normal file
23
site2/docs/dev/chrome/repo.md
Normal file
@ -0,0 +1,23 @@
|
||||
|
||||
---
|
||||
title: "Working in a Chromium repo"
|
||||
linkTitle: "Working in a Chromium repo"
|
||||
|
||||
---
|
||||
|
||||
|
||||
To work on Skia inside a Chromium checkout, run the following:
|
||||
|
||||
cd chromium/src/third_party/skia
|
||||
python2 tools/git-sync-deps
|
||||
bin/gn gen out/Debug
|
||||
|
||||
The second command does a minimal "just sync the DEPS" emulation of `gclient
|
||||
sync` for Skia into chromium/src/third_party/skia/third_party. After that,
|
||||
`ninja -C out/Debug dm` in chromium/src/third_party/skia will get you rolling.
|
||||
|
||||
We no longer recommend the .gclient file manipulation to have Chromium DEPS also
|
||||
sync Skia's DEPS. Most of those DEPS are for building and testing only;
|
||||
Chromium doesn't need any of them, and it can be confusing and problematic if
|
||||
they somehow get mixed into the Chromium build.
|
||||
|
67
site2/docs/dev/contrib/_index.md
Normal file
67
site2/docs/dev/contrib/_index.md
Normal file
@ -0,0 +1,67 @@
|
||||
---
|
||||
title: 'Contributing to Skia'
|
||||
linkTitle: 'Contributing'
|
||||
|
||||
weight: 1
|
||||
menu:
|
||||
main:
|
||||
weight: 40
|
||||
---
|
||||
|
||||
Here some ways you can get involved and help us improve Skia.
|
||||
|
||||
## Report Bugs
|
||||
|
||||
Find bugs to fix or report new bugs in the
|
||||
[Skia issue tracker](http://bug.skia.org/). You can also search the
|
||||
[Chromium issue tracker](http://code.google.com/p/chromium/issues/list) for bugs
|
||||
related to graphics or Skia.
|
||||
|
||||
## Test
|
||||
|
||||
Write an application or tool that will exercise the Skia code differently than
|
||||
our current set of tests and verify that Skia works as expected. Draw something
|
||||
interesting and profile it to find ways to speed up Skia's implementation.We
|
||||
cannot always fix issues or support every scenario, but we welcome any bugs
|
||||
found so we can assess and prioritize them. (If you find _and_ fix a bug, even
|
||||
better!)
|
||||
|
||||
## Contribute Code
|
||||
|
||||
Whether you develop a new feature or a fix for an existing bug in the Skia code
|
||||
base, you will need a committer to review and approve the change. There are some
|
||||
steps that can speed up the review process:
|
||||
|
||||
- Keep your code submissions small and targeted.
|
||||
- When possible, have a fellow contributor review your change in advance of
|
||||
submission.
|
||||
- Propose new features to the project leads by opening a feature bug or posting
|
||||
to skia-discuss ahead of development.
|
||||
|
||||
For more information, see [How to submit a patch](/dev/contrib/submit).
|
||||
|
||||
For background on the project and an outline of the types of roles interested
|
||||
parties can take on, see [Project Roles](/roles).
|
||||
|
||||
Anyone contributing code to Skia must sign a Contributor License Agreement and
|
||||
ensure they are listed in the AUTHORS file:
|
||||
|
||||
- Individual contributors can complete the
|
||||
[Individual Contributor License Agreement](https://developers.google.com/open-source/cla/individual)
|
||||
online.
|
||||
- If you are contributing on behalf of a corporation, fill out the
|
||||
[Corporate Contributor License Agreement](https://developers.google.com/open-source/cla/corporate)
|
||||
and send it in as described on that page.
|
||||
|
||||
- If it is your first time submitting code or you have not previously done so,
|
||||
add your (or your organization's) name and contact info to the
|
||||
[AUTHORS file](https://skia.googlesource.com/skia/+/master/AUTHORS) as a part
|
||||
of your CL.
|
||||
|
||||
REVIEWERS: Before you LGTM a change, verify that the contributor is listed in
|
||||
the AUTHORS file.
|
||||
|
||||
If they are not, a Googler must ensure that the individual or their corporation
|
||||
has signed the CLA by searching
|
||||
[go/cla-signers](https://goto.google.com/cla-signers). Then have an entry added
|
||||
to the AUTHORS file with the CL.
|
102
site2/docs/dev/contrib/bazel.md
Normal file
102
site2/docs/dev/contrib/bazel.md
Normal file
@ -0,0 +1,102 @@
|
||||
|
||||
---
|
||||
title: "Notes about Bazel Builds"
|
||||
linkTitle: "Notes about Bazel Builds"
|
||||
|
||||
---
|
||||
|
||||
|
||||
Skia cannot be built with Bazel yet.
|
||||
|
||||
But you should be able to build and run the trivial `tools/bazel_test.cc`:
|
||||
|
||||
$ bazel test ...
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
`WORKSPACE.bazel` acts like `DEPS`, listing external dependencies and how to
|
||||
fetch them. You can call `bazel sync`, or just let `bazel {build,test,run}`
|
||||
handle it as needed on its own. The easiest way to add a new dependency is to
|
||||
start using `tag="..."` or `branch="..."` and then follow the advice of Bazel
|
||||
to pin that to the `commit` and `shallow_since` it suggests.
|
||||
|
||||
We must provide Bazel build configuration for dependencies like `libpng` that
|
||||
don't provide their own. For `libpng` that's `bazel/libpng.bazel`, linked by
|
||||
the `new_git_repository()` `build_file` argument, written relative to that
|
||||
fetched Git repo's root. Its resemblance to `third_party/libpng/BUILD.gn` is
|
||||
no coincidence... it's pretty much a 1:1 translation between GN and Bazel.
|
||||
|
||||
Everything that's checked in builds external dependencies from source. I've
|
||||
not written an integrated system for substituting prebuilt versions of these
|
||||
dependencies (e.g. `/usr/include/png.h` and `/usr/lib/libpng.so`), instead
|
||||
leaving that up to users who want it. The process is not exactly trivial, but
|
||||
closer to tedious than difficult. Here's an example, overriding `libpng` to
|
||||
point to prebuilts from Homebrew in ~/brew:
|
||||
|
||||
Each overridden dependency will need its own directory with a few files.
|
||||
|
||||
$ find overrides
|
||||
overrides
|
||||
overrides/libpng
|
||||
overrides/libpng/include
|
||||
overrides/libpng/WORKSPACE.bazel
|
||||
overrides/libpng/BUILD.bazel
|
||||
|
||||
`WORKSPACE.bazel` must be present, but in this case can be empty.
|
||||
|
||||
$ cat overrides/libpng/WORKSPACE.bazel
|
||||
|
||||
`BUILD.bazel` is where it all happens:
|
||||
|
||||
$ cat overrides/libpng/BUILD.bazel
|
||||
cc_library(
|
||||
name = "libpng",
|
||||
hdrs = ["include/png.h"],
|
||||
srcs = ["include/pngconf.h", "include/pnglibconf.h"],
|
||||
includes = ["include"],
|
||||
linkopts = ["-lpng", "-L/Users/mtklein/brew/lib"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
`include` is a symlink I've made to `~/brew/include` because Bazel doesn't like
|
||||
absolute paths in `hdrs` or `includes`. On the other hand, a symlink to
|
||||
`~/brew/lib` doesn't work here, though `-L/Users/mtklein/brew/lib` works fine.
|
||||
|
||||
$ readlink overrides/libpng/include
|
||||
/Users/mtklein/brew/include/
|
||||
|
||||
Finally, we point Bazel at all that using `--override_repository`:
|
||||
|
||||
$ bazel test ... --override_repository libpng=/Users/mtklein/overrides/libpng
|
||||
|
||||
I expect building from source to be the most common use case, and it's more or
|
||||
less enough to simply know that we can substitute prebuilts this way. The most
|
||||
interesting part to me is that we don't need to provide this mechanism... it's
|
||||
all there in stock Bazel. This plan may all want some rethinking in the future
|
||||
if we want to add the option to trim the dependency entirely and make this
|
||||
tristate (build it, use it prebuilt, or trim).
|
||||
|
||||
.bazelrc
|
||||
--------
|
||||
|
||||
I have not (yet?) checked in a .bazelrc to the Skia repo, but have found it
|
||||
handy to write my own in ~/.bazelrc:
|
||||
|
||||
$ cat ~/.bazelrc
|
||||
# Print more information on failures.
|
||||
build --verbose_failures
|
||||
test --test_output errors
|
||||
|
||||
# Create an ASAN config, try `bazel test --config asan ...`.
|
||||
build:asan --copt -fsanitize=address
|
||||
build:asan --copt -Wno-macro-redefined # (_FORTIFY_SOURCE redefined.)
|
||||
build:asan --linkopt -fsanitize=address
|
||||
|
||||
# Flip on and off prebuilt overrides easily.
|
||||
build --override_repository libpng=/Users/mtklein/overrides/libpng
|
||||
|
||||
I'm impressed by how much you can configure via bazelrc, and I think this
|
||||
should let our Bazel build configuration stay mostly focused on the structure
|
||||
of the project, less cluttered by build settings.
|
||||
|
99
site2/docs/dev/contrib/cqkeywords.md
Normal file
99
site2/docs/dev/contrib/cqkeywords.md
Normal file
@ -0,0 +1,99 @@
|
||||
|
||||
---
|
||||
title: "Commit Queue Keywords"
|
||||
linkTitle: "Commit Queue Keywords"
|
||||
|
||||
---
|
||||
|
||||
|
||||
See [CQ
|
||||
documentation](https://chromium.googlesource.com/chromium/src/+/master/docs/infra/cq.md)
|
||||
for more information.
|
||||
|
||||
Options in the form "Key: Value" must appear in the last paragraph of the
|
||||
commit message to be used.
|
||||
|
||||
|
||||
Commit
|
||||
------
|
||||
|
||||
If you are working on experimental code and do not want to risk accidentally
|
||||
submitting the change via the CQ, then you can mark it with "Commit: false".
|
||||
The CQ will immediately abandon the change if it contains this option.
|
||||
To do a dry run through the CQ please use Gerrit's [CQ Dry
|
||||
Run](https://groups.google.com/a/chromium.org/forum/#!topic/chromium-dev/G5-X0_tfmok)
|
||||
feature.
|
||||
|
||||
Commit: false
|
||||
|
||||
The CQ will run through its list of verifiers (reviewer check, trybots, tree check,
|
||||
presubmit check), and will close the issue instead of committing it.
|
||||
|
||||
|
||||
No-Dependency-Checks
|
||||
--------------------
|
||||
|
||||
No-Dependency-Checks: true
|
||||
|
||||
The CQ rejects patchsets with open dependencies. An open dependency exists when a CL
|
||||
depends on another CL that is not yet closed. You can skip this check with this keyword.
|
||||
|
||||
|
||||
Cq-Include-Trybots
|
||||
------------------
|
||||
|
||||
Allows you to add arbitrary trybots to the CQ's list of default trybots.
|
||||
The CQ will block till these tryjobs pass just like the default list of tryjobs.
|
||||
|
||||
This is the format of the values of this keyword:
|
||||
|
||||
Cq-Include-Trybots: bucket1:bot1,bot2;bucket2:bot3,bot4
|
||||
|
||||
Multiple lines are allowed:
|
||||
|
||||
Cq-Include-Trybots: bucket1:bot1
|
||||
Cq-Include-Trybots: bucket1:bot2
|
||||
Cq-Include-Trybots: bucket2:bot3
|
||||
Cq-Include-Trybots: bucket2:bot4
|
||||
|
||||
Here are some real world examples:
|
||||
|
||||
Cq-Include-Trybots: master.tryserver.chromium.linux:linux_chromium_asan_rel_ng
|
||||
Cq-Include-Trybots: skia.primary:Test-Win10-Clang-ShuttleC-GPU-GTX960-x86_64-Debug-All-ANGLE
|
||||
Cq-Include-Trybots: luci.skia.skia.primary:Build-Debian9-Clang-x86-devrel-Android_SKQP
|
||||
|
||||
FIXME: what bucket are skia bots in now?
|
||||
|
||||
|
||||
No-Tree-Checks
|
||||
--------------
|
||||
|
||||
If you want to skip the tree status checks, to make the CQ commit a CL even if
|
||||
the tree is closed, you can add the following line to the CL description:
|
||||
|
||||
No-Tree-Checks: true
|
||||
|
||||
This is discouraged, since the tree is closed for a reason. However, in rare
|
||||
cases this is acceptable, primarily to fix build breakages (i.e., your CL will
|
||||
help in reopening the tree).
|
||||
|
||||
|
||||
No-Presubmit
|
||||
------------
|
||||
|
||||
If you want to skip the presubmit checks, add the following line to the CL description:
|
||||
|
||||
No-Presubmit: true
|
||||
|
||||
|
||||
No-Try
|
||||
------
|
||||
|
||||
If you cannot wait for the try job results, you can add the following line to
|
||||
the CL description:
|
||||
|
||||
No-Try: true
|
||||
|
||||
The CQ will then not run any try jobs for your change and will commit the CL as
|
||||
soon as the tree is open, assuming the presubmit check passes.
|
||||
|
48
site2/docs/dev/contrib/directory.md
Normal file
48
site2/docs/dev/contrib/directory.md
Normal file
@ -0,0 +1,48 @@
|
||||
|
||||
---
|
||||
title: "The Skia Directory"
|
||||
linkTitle: "The Skia Directory"
|
||||
|
||||
---
|
||||
|
||||
|
||||
* Docs & Bugs
|
||||
- [Skia.org](https://skia.org/)
|
||||
- [Issue Tracker](https://bug.skia.org/)
|
||||
- [Autogenerated API Documentation](https://api.skia.org)
|
||||
|
||||
* Code Repositories
|
||||
- [Git repository](https://skia.googlesource.com/skia/)
|
||||
- [Other Skia project repositories](https://skia.googlesource.com/)
|
||||
- [GitHub Mirror](https://github.com/google/skia)
|
||||
- [Code Search](https://cs.skia.org) based on the version of Skia in the
|
||||
Chromium tree.
|
||||
|
||||
* BuildBot Consoles
|
||||
- [Commit Status](https://status.skia.org/)
|
||||
- [Tree Status](https://tree-status.skia.org/) (requires login)
|
||||
- [BuildBot Console](https://build.chromium.org/p/client.skia/console)
|
||||
- [FYI BuildBot
|
||||
Console](https://build.chromium.org/p/client.skia.fyi/console)
|
||||
- [Android BuildBot
|
||||
Console](https://build.chromium.org/p/client.skia.android/console)
|
||||
- [Compile BuildBot
|
||||
Console](https://build.chromium.org/p/client.skia.compile/console)
|
||||
|
||||
* Other
|
||||
- [Fiddle](https://fiddle.skia.org/) Try out Skia on the web!
|
||||
- [Gold](https://gold.skia.org/) Correctness testing.
|
||||
- [Perf](https://perf.skia.org/) Performance testing.
|
||||
- [Mon](https://mon.skia.org/) Grafana dashboard (requires login).
|
||||
- [Alerts](https://alerts.skia.org/) Monitor testing and bot status.
|
||||
- [BugChomper] (https://bugchomper.skia.org/) Prioritize bugs quickly.
|
||||
- [Code Review](https://skia-review.googlesource.com/)
|
||||
|
||||
* Mailing Lists
|
||||
- [Discussion Mailing List](https://groups.google.com/group/skia-discuss)
|
||||
- [Code Review Announce
|
||||
List](https://groups.google.com/a/skia.org/forum/#!forum/reviews)
|
||||
- [Bug Announce
|
||||
List](https://groups.google.com/a/skia.org/forum/#!forum/bugs)
|
||||
|
||||
|
74
site2/docs/dev/contrib/patch.md
Normal file
74
site2/docs/dev/contrib/patch.md
Normal file
@ -0,0 +1,74 @@
|
||||
---
|
||||
title: 'Applying patches'
|
||||
linkTitle: 'Applying patches'
|
||||
---
|
||||
|
||||
If you are a Skia committer and have been asked to commit an
|
||||
externally-submitted patch, this is how to do it. (This technique is useful in
|
||||
other situations too, like if you just want to try out somebody else's patch
|
||||
locally.)
|
||||
|
||||
Notes:
|
||||
|
||||
- For the examples below, we will assume that this is the change you want to
|
||||
patch into your local checkout: https://codereview.appspot.com/6201055/
|
||||
- These instructions should work on Mac or Linux; Windows is trickier, because
|
||||
there is no standard Windows "patch" tool.
|
||||
|
||||
See also [Contributing Code for The Chromium Projects]
|
||||
(http://dev.chromium.org/developers/contributing-code#TOC-Instructions-for-Reviewer:-Checking-in-the-patch-for-a-non-committer).
|
||||
|
||||
If you use `git cl`, then you should be able to use the shortcut:
|
||||
|
||||
```
|
||||
git cl patch 6201055
|
||||
```
|
||||
|
||||
If you use `gcl`, or the above doesn't work, the following should always work.
|
||||
|
||||
1. Prepare your local workspace to accept the patch.
|
||||
|
||||
- cd into the root directory (usually `trunk/`) of the workspace where you
|
||||
want to apply the patch.
|
||||
- Make sure that the workspace is up-to-date and clean (or "updated and clean
|
||||
enough" for your purposes). If the codereview patch was against an old
|
||||
revision of the repo, you may need to sync your local workspace to that
|
||||
same revision.
|
||||
|
||||
2. Download the raw patch set.
|
||||
|
||||
- Open the codereview web page and look for the "Download raw patch set" link
|
||||
near the upper right-hand corner. Right-click on that link and copy it to
|
||||
the clipboard. (In my case, the link is
|
||||
https://codereview.appspot.com/download/issue6201055_1.diff )
|
||||
- If you are on Linux or Mac and have "curl" or "wget" installed, you can
|
||||
download the patch from the command line:
|
||||
|
||||
```
|
||||
curl https://codereview.appspot.com/download/issue6201055_1.diff
|
||||
--output patch.txt
|
||||
# or...
|
||||
wget https://codereview.appspot.com/download/issue6201055_1.diff
|
||||
--output-document=patch.txt
|
||||
```
|
||||
|
||||
- Otherwise, figure out some other way to download this file and save it as
|
||||
`patch.txt`
|
||||
|
||||
3. Apply this patch to your local checkout.
|
||||
|
||||
- You should still be in the root directory of the workspace where you want
|
||||
to apply the patch.
|
||||
|
||||
```
|
||||
patch -p1 <patch.txt
|
||||
```
|
||||
|
||||
- Then you can run `diff` and visually check the local changes.
|
||||
|
||||
4. Complications: If the patch fails to apply, the following may be happening:
|
||||
|
||||
- Wrong revision. Maybe your local workspace is not up to date? Or maybe the
|
||||
patch was made against an old revision of the repository, and cannot be
|
||||
applied to the latest revision? (In that case, revert any changes and sync
|
||||
your workspace to an older revision, then re-apply the patch.)
|
45
site2/docs/dev/contrib/revert.md
Normal file
45
site2/docs/dev/contrib/revert.md
Normal file
@ -0,0 +1,45 @@
|
||||
|
||||
---
|
||||
title: "How to revert a CL"
|
||||
linkTitle: "How to revert a CL"
|
||||
|
||||
---
|
||||
|
||||
|
||||
Using one-click revert
|
||||
----------------------
|
||||
* Find the codereview issue for the CL you want to revert.
|
||||
* Click the "revert" button.
|
||||
|
||||
Using Git
|
||||
---------
|
||||
|
||||
Update the local repository
|
||||
|
||||
git fetch origin master
|
||||
|
||||
Create a local branch with origin/master as its start point.
|
||||
|
||||
git checkout -b revert$RANDOM origin/master
|
||||
|
||||
Find the SHA1 of the commit you want to revert
|
||||
|
||||
git log origin/master
|
||||
|
||||
Create a revert commit.
|
||||
|
||||
git revert <SHA1>
|
||||
|
||||
Upload it to Gerrit.
|
||||
|
||||
git cl upload
|
||||
|
||||
Land the revert in origin/master.
|
||||
|
||||
git cl land
|
||||
|
||||
Delete the local revert branch.
|
||||
|
||||
git checkout --detach && git branch -D @{-1}
|
||||
|
||||
|
529
site2/docs/dev/contrib/style.md
Normal file
529
site2/docs/dev/contrib/style.md
Normal file
@ -0,0 +1,529 @@
|
||||
---
|
||||
title: 'Coding Style Guidelines'
|
||||
linkTitle: 'Coding Style Guidelines'
|
||||
---
|
||||
|
||||
These conventions have evolved over time. Some of the earlier code in both
|
||||
projects doesn't strictly adhere to the guidelines. However, as the code evolves
|
||||
we hope to make the existing code conform to the guildelines.
|
||||
|
||||
## Files
|
||||
|
||||
We use .cpp and .h as extensions for c++ source and header files.
|
||||
|
||||
Headers that aren't meant for public consumption should be placed in src
|
||||
directories so that they aren't in a client's search path, or in
|
||||
include/private if they need to be used by public headers.
|
||||
|
||||
We prefer to minimize includes. If forward declaring a name in a header is
|
||||
sufficient then that is preferred to an include.
|
||||
|
||||
Forward declarations and file includes should be in alphabetical order.
|
||||
|
||||
<span id="no-define-before-sktypes"></span>
|
||||
Do not use #if/#ifdef before including "SkTypes.h" (directly or indirectly).
|
||||
Most things you'd #if on tend to not yet be decided until SkTypes.h.
|
||||
|
||||
We use 4 spaces, not tabs.
|
||||
|
||||
We use Unix style endlines (LF).
|
||||
|
||||
We prefer no trailing whitespace but aren't very strict about it.
|
||||
|
||||
We wrap lines at 100 columns unless it is excessively ugly (use your judgement).
|
||||
|
||||
## Naming
|
||||
|
||||
Most externally visible types and functions use an Sk- prefix to designate
|
||||
they're part of Skia, but code in Ganesh uses Gr-. Nested types need not be
|
||||
prefixed.
|
||||
|
||||
<!--?prettify?-->
|
||||
|
||||
```
|
||||
class SkClass {
|
||||
public:
|
||||
class HelperClass {
|
||||
...
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
Data fields in structs, classes, and unions that have methods begin with
|
||||
lower-case f and are then camel-capped, to distinguish those fields from other
|
||||
variables. Types that are predominantly meant for direct field access don't
|
||||
need f-decoration.
|
||||
|
||||
<!--?prettify?-->
|
||||
|
||||
```
|
||||
struct GrCar {
|
||||
float milesDriven;
|
||||
Color color;
|
||||
};
|
||||
|
||||
class GrMotorcyle {
|
||||
public:
|
||||
float getMilesDriven() const { return fMilesDriven; }
|
||||
void setMilesDriven(float milesDriven) { fMilesDriven = milesDriven; }
|
||||
|
||||
Color getColor() const { return fColor; }
|
||||
private:
|
||||
float fMilesDriven;
|
||||
Color fColor;
|
||||
};
|
||||
```
|
||||
|
||||
Global variables are similar but prefixed with g and camel-capped.
|
||||
|
||||
<!--?prettify?-->
|
||||
|
||||
```
|
||||
bool gLoggingEnabled;
|
||||
```
|
||||
|
||||
Local variables and arguments are camel-capped with no initial cap.
|
||||
|
||||
<!--?prettify?-->
|
||||
|
||||
```
|
||||
int herdCats(const Array& cats) {
|
||||
int numCats = cats.count();
|
||||
}
|
||||
```
|
||||
|
||||
Variables declared `constexpr` or `const`, and whose value is fixed for the
|
||||
duration of the program, are named with a leading "k" and then camel-capped.
|
||||
|
||||
<!--?prettify?-->
|
||||
|
||||
```
|
||||
int drawPicture() {
|
||||
constexpr SkISize kPictureSize = {100, 100};
|
||||
constexpr float kZoom = 1.0f;
|
||||
}
|
||||
```
|
||||
|
||||
Enum values are also prefixed with k. Unscoped enum values are postfixed with
|
||||
an underscore and singular name of the enum name. The enum itself should be
|
||||
singular for exclusive values or plural for a bitfield. If a count is needed it
|
||||
is `k<singular enum name>Count` and not be a member of the enum (see example),
|
||||
or a kLast member of the enum is fine too.
|
||||
|
||||
<!--?prettify?-->
|
||||
|
||||
```
|
||||
// Enum class does not need suffixes.
|
||||
enum class SkPancakeType {
|
||||
kBlueberry,
|
||||
kPlain,
|
||||
kChocolateChip,
|
||||
};
|
||||
```
|
||||
|
||||
<!--?prettify?-->
|
||||
|
||||
```
|
||||
// Enum should have a suffix after the enum name.
|
||||
enum SkDonutType {
|
||||
kGlazed_DonutType,
|
||||
kSprinkles_DonutType,
|
||||
kChocolate_DonutType,
|
||||
kMaple_DonutType,
|
||||
|
||||
kLast_DonutType = kMaple_DonutType
|
||||
};
|
||||
|
||||
static const SkDonutType kDonutTypeCount = kLast_DonutType + 1;
|
||||
```
|
||||
|
||||
<!--?prettify?-->
|
||||
|
||||
```
|
||||
enum SkSausageIngredientBits {
|
||||
kFennel_SausageIngredientBit = 0x1,
|
||||
kBeef_SausageIngredientBit = 0x2
|
||||
};
|
||||
```
|
||||
|
||||
<!--?prettify?-->
|
||||
|
||||
```
|
||||
enum SkMatrixFlags {
|
||||
kTranslate_MatrixFlag = 0x1,
|
||||
kRotate_MatrixFlag = 0x2
|
||||
};
|
||||
```
|
||||
|
||||
Macros are all caps with underscores between words. Macros that have greater
|
||||
than file scope should be prefixed SK or GR.
|
||||
|
||||
Static non-class functions in implementation files are lower-case with
|
||||
underscores separating words:
|
||||
|
||||
<!--?prettify?-->
|
||||
|
||||
```
|
||||
static inline bool tastes_like_chicken(Food food) {
|
||||
return kIceCream_Food != food;
|
||||
}
|
||||
```
|
||||
|
||||
Externed functions or static class functions are camel-capped with an initial cap:
|
||||
|
||||
<!--?prettify?-->
|
||||
|
||||
```
|
||||
bool SkIsOdd(int n);
|
||||
|
||||
class SkFoo {
|
||||
public:
|
||||
static int FooInstanceCount();
|
||||
|
||||
// Not static.
|
||||
int barBaz();
|
||||
};
|
||||
```
|
||||
|
||||
## Macros
|
||||
|
||||
Ganesh macros that are GL-specific should be prefixed GR_GL.
|
||||
|
||||
<!--?prettify?-->
|
||||
|
||||
```
|
||||
#define GR_GL_TEXTURE0 0xdeadbeef
|
||||
```
|
||||
|
||||
Ganesh prefers that macros are always defined and the use of `#if MACRO` rather than
|
||||
`#ifdef MACRO`.
|
||||
|
||||
<!--?prettify?-->
|
||||
|
||||
```
|
||||
#define GR_GO_SLOWER 0
|
||||
...
|
||||
#if GR_GO_SLOWER
|
||||
Sleep(1000);
|
||||
#endif
|
||||
```
|
||||
|
||||
The rest of Skia tends to use `#ifdef SK_MACRO` for boolean flags.
|
||||
|
||||
## Braces
|
||||
|
||||
Open braces don't get a newline. `else` and `else if` appear on same line as
|
||||
opening and closing braces unless preprocessor conditional compilation
|
||||
interferes. Braces are always used with `if`, `else`, `while`, `for`, and `do`.
|
||||
|
||||
<!--?prettify?-->
|
||||
|
||||
```
|
||||
if (...) {
|
||||
oneOrManyLines;
|
||||
}
|
||||
|
||||
if (...) {
|
||||
oneOrManyLines;
|
||||
} else if (...) {
|
||||
oneOrManyLines;
|
||||
} else {
|
||||
oneOrManyLines;
|
||||
}
|
||||
|
||||
for (...) {
|
||||
oneOrManyLines;
|
||||
}
|
||||
|
||||
while (...) {
|
||||
oneOrManyLines;
|
||||
}
|
||||
|
||||
void function(...) {
|
||||
oneOrManyLines;
|
||||
}
|
||||
|
||||
if (!error) {
|
||||
proceed_as_usual();
|
||||
}
|
||||
#if HANDLE_ERROR
|
||||
else {
|
||||
freak_out();
|
||||
}
|
||||
#endif
|
||||
```
|
||||
|
||||
## Flow Control
|
||||
|
||||
There is a space between flow control words and parentheses, and between
|
||||
parentheses and braces:
|
||||
|
||||
<!--?prettify?-->
|
||||
|
||||
```
|
||||
while (...) {
|
||||
}
|
||||
|
||||
do {
|
||||
} while (...);
|
||||
|
||||
switch (...) {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Cases and default in switch statements are indented from the switch.
|
||||
|
||||
<!--?prettify?-->
|
||||
|
||||
```
|
||||
switch (color) {
|
||||
case kBlue:
|
||||
...
|
||||
break;
|
||||
case kGreen:
|
||||
...
|
||||
break;
|
||||
...
|
||||
default:
|
||||
...
|
||||
break;
|
||||
}
|
||||
```
|
||||
|
||||
Fallthrough from one case to the next is annotated with `[[fallthrough]]`.
|
||||
However, when multiple case statements in a row are used, they do not need the
|
||||
`[[fallthrough]]` annotation.
|
||||
|
||||
<!--?prettify?-->
|
||||
|
||||
```
|
||||
switch (recipe) {
|
||||
...
|
||||
case kSmallCheesePizza_Recipe:
|
||||
case kLargeCheesePizza_Recipe:
|
||||
ingredients |= kCheese_Ingredient | kDough_Ingredient | kSauce_Ingredient;
|
||||
break;
|
||||
case kCheeseOmelette_Recipe:
|
||||
ingredients |= kCheese_Ingredient;
|
||||
[[fallthrough]]
|
||||
case kPlainOmelette_Recipe:
|
||||
ingredients |= (kEgg_Ingredient | kMilk_Ingredient);
|
||||
break;
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
When a block is needed to declare variables within a case follow this pattern:
|
||||
|
||||
<!--?prettify?-->
|
||||
|
||||
```
|
||||
switch (filter) {
|
||||
...
|
||||
case kGaussian_Filter: {
|
||||
Bitmap srcCopy = src->makeCopy();
|
||||
...
|
||||
} break;
|
||||
...
|
||||
};
|
||||
```
|
||||
|
||||
## Classes
|
||||
|
||||
Unless there is a need for forward declaring something, class declarations
|
||||
should be ordered `public`, `protected`, `private`. Each should be preceded by a
|
||||
newline. Within each visibility section (`public`, `private`), fields should not be
|
||||
intermixed with methods. It's nice to keep all data fields together at the end.
|
||||
|
||||
<!--?prettify?-->
|
||||
|
||||
```
|
||||
class SkFoo {
|
||||
|
||||
public:
|
||||
...
|
||||
|
||||
protected:
|
||||
...
|
||||
|
||||
private:
|
||||
void barHelper(...);
|
||||
...
|
||||
|
||||
SkBar fBar;
|
||||
...
|
||||
};
|
||||
```
|
||||
|
||||
Virtual functions that are overridden in derived classes should use override,
|
||||
and the virtual keyword should be omitted.
|
||||
|
||||
<!--?prettify?-->
|
||||
|
||||
```
|
||||
void myVirtual() override {
|
||||
}
|
||||
```
|
||||
|
||||
If you call a method on a parent type that must stand out as specifically the
|
||||
parent's version of that method, we usually privately alias that parent type to
|
||||
`INHERITED` within the class. That lets calls like `INHERITED::onFoo()` stand
|
||||
out visually. No need for `this->` when using `INHERITED::`.
|
||||
|
||||
<!--?prettify?-->
|
||||
|
||||
```
|
||||
class GrDillPickle : public GrPickle {
|
||||
...
|
||||
bool onTasty() const override {
|
||||
return INHERITED::onTasty()
|
||||
&& fFreshDill;
|
||||
}
|
||||
...
|
||||
private:
|
||||
bool fFreshDill;
|
||||
using INHERITED = GrPickle;
|
||||
};
|
||||
```
|
||||
|
||||
Constructor initializers should be one per line, indented, with punctuation
|
||||
placed before the initializer.
|
||||
|
||||
<!--?prettify?-->
|
||||
|
||||
```
|
||||
GrDillPickle::GrDillPickle()
|
||||
: GrPickle()
|
||||
, fSize(kDefaultPickleSize) {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Constructors that take one argument should almost always be explicit, with
|
||||
exceptions made only for the (rare) automatic compatibility class.
|
||||
|
||||
<!--?prettify?-->
|
||||
|
||||
```
|
||||
class Foo {
|
||||
explicit Foo(int x); // Good.
|
||||
Foo(float y); // Spooky implicit conversion from float to Foo. No no no!
|
||||
...
|
||||
};
|
||||
```
|
||||
|
||||
Method calls within method calls should be prefixed with dereference of the
|
||||
'this' pointer. For example:
|
||||
|
||||
<!--?prettify?-->
|
||||
|
||||
```
|
||||
this->method();
|
||||
```
|
||||
|
||||
A common pattern for virtual methods in Skia is to include a public non-virtual
|
||||
(or final) method, paired with a private virtual method named "onMethodName".
|
||||
This ensures that the base-class method is always invoked and gives it control
|
||||
over how the virtual method is used, rather than relying on each subclass to
|
||||
call `INHERITED::onMethodName`. For example:
|
||||
|
||||
<!--?prettify?-->
|
||||
|
||||
```
|
||||
class SkSandwich {
|
||||
public:
|
||||
void assemble() {
|
||||
// All sandwiches must have bread on the top and bottom.
|
||||
this->addIngredient(kBread_Ingredient);
|
||||
this->onAssemble();
|
||||
this->addIngredient(kBread_Ingredient);
|
||||
}
|
||||
bool cook() {
|
||||
return this->onCook();
|
||||
}
|
||||
|
||||
private:
|
||||
// All sandwiches must implement onAssemble.
|
||||
virtual void onAssemble() = 0;
|
||||
// Sandwiches can remain uncooked by default.
|
||||
virtual bool onCook() { return true; }
|
||||
};
|
||||
|
||||
class SkGrilledCheese : public SkSandwich {
|
||||
private:
|
||||
void onAssemble() override {
|
||||
this->addIngredient(kCheese_Ingredient);
|
||||
}
|
||||
bool onCook() override {
|
||||
return this->toastOnGriddle();
|
||||
}
|
||||
};
|
||||
|
||||
class SkPeanutButterAndJelly : public SkSandwich {
|
||||
private:
|
||||
void onAssemble() override {
|
||||
this->addIngredient(kPeanutButter_Ingredient);
|
||||
this->addIngredient(kGrapeJelly_Ingredient);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Integer Types
|
||||
|
||||
We follow the Google C++ guide for ints and are slowly making older code conform to this
|
||||
|
||||
(http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Integer_Types)
|
||||
|
||||
Summary: Use `int` unless you have need a guarantee on the bit count, then use
|
||||
`stdint.h` types (`int32_t`, etc). Assert that counts, etc are not negative instead
|
||||
of using unsigned. Bitfields use `uint32_t` unless they have to be made shorter
|
||||
for packing or performance reasons.
|
||||
|
||||
## Function Parameters
|
||||
|
||||
Mandatory constant object parameters are passed to functions as const references.
|
||||
Optional constant object parameters are passed to functions as const pointers.
|
||||
Mutable object parameters are passed to functions as pointers.
|
||||
We very rarely pass anything by non-const reference.
|
||||
|
||||
<!--?prettify?-->
|
||||
|
||||
```
|
||||
// src and paint are optional
|
||||
void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
|
||||
const SkRect& dst, const SkPaint* paint = nullptr);
|
||||
|
||||
// metrics is mutable (it is changed by the method)
|
||||
SkScalar SkPaint::getFontMetrics(FontMetric* metrics, SkScalar scale) const;
|
||||
|
||||
```
|
||||
|
||||
If function arguments or parameters do not all fit on one line, the overflowing
|
||||
parameters may be lined up with the first parameter on the next line
|
||||
|
||||
<!--?prettify?-->
|
||||
|
||||
```
|
||||
void drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst,
|
||||
const SkPaint* paint = nullptr) {
|
||||
this->drawBitmapRectToRect(bitmap, nullptr, dst, paint,
|
||||
kNone_DrawBitmapRectFlag);
|
||||
}
|
||||
```
|
||||
|
||||
or all parameters placed on the next line and indented eight spaces
|
||||
|
||||
<!--?prettify?-->
|
||||
|
||||
```
|
||||
void drawBitmapRect(
|
||||
const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint = nullptr) {
|
||||
this->drawBitmapRectToRect(
|
||||
bitmap, nullptr, dst, paint, kNone_DrawBitmapRectFlag);
|
||||
}
|
||||
```
|
||||
|
||||
## Python
|
||||
|
||||
Python code follows the [Google Python Style Guide](http://google-styleguide.googlecode.com/svn/trunk/pyguide.html).
|
244
site2/docs/dev/contrib/submit.md
Normal file
244
site2/docs/dev/contrib/submit.md
Normal file
@ -0,0 +1,244 @@
|
||||
|
||||
---
|
||||
title: "How to submit a patch"
|
||||
linkTitle: "How to submit a patch"
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
Configure git
|
||||
-------------
|
||||
|
||||
<!--?prettify lang=sh?-->
|
||||
|
||||
git config --global user.name "Your Name"
|
||||
git config --global user.email you@example.com
|
||||
|
||||
Making changes
|
||||
--------------
|
||||
|
||||
First create a branch for your changes:
|
||||
|
||||
<!--?prettify lang=sh?-->
|
||||
|
||||
git config branch.autosetuprebase always
|
||||
git checkout -b my_feature origin/master
|
||||
|
||||
After making your changes, create a commit
|
||||
|
||||
<!--?prettify lang=sh?-->
|
||||
|
||||
git add [file1] [file2] ...
|
||||
git commit
|
||||
|
||||
If your branch gets out of date, you will need to update it:
|
||||
|
||||
<!--?prettify lang=sh?-->
|
||||
|
||||
git pull
|
||||
python2 tools/git-sync-deps
|
||||
|
||||
Adding a unit test
|
||||
------------------
|
||||
|
||||
If you are willing to change Skia codebase, it's nice to add a test at the same
|
||||
time. Skia has a simple unittest framework so you can add a case to it.
|
||||
|
||||
Test code is located under the 'tests' directory.
|
||||
|
||||
See [Writing Unit and Rendering Tests](../testing/tests) for details.
|
||||
|
||||
Unit tests are best, but if your change touches rendering and you can't think of
|
||||
an automated way to verify the results, consider writing a GM test. Also, if your
|
||||
change is in the GPU code, you may not be able to write it as part of the standard
|
||||
unit test suite, but there are GPU-specific testing paths you can extend.
|
||||
|
||||
Submitting a patch
|
||||
------------------
|
||||
|
||||
For your code to be accepted into the codebase, you must complete the
|
||||
[Individual Contributor License
|
||||
Agreement](http://code.google.com/legal/individual-cla-v1.0.html). You can do
|
||||
this online, and it only takes a minute. If you are contributing on behalf of a
|
||||
corporation, you must fill out the [Corporate Contributor License
|
||||
Agreement](http://code.google.com/legal/corporate-cla-v1.0.html)
|
||||
and send it to us as described on that page. Add your (or your organization's)
|
||||
name and contact info to the AUTHORS file as a part of your CL.
|
||||
|
||||
Now that you've made a change and written a test for it, it's ready for the code
|
||||
review! Submit a patch and getting it reviewed is fairly easy with depot tools.
|
||||
|
||||
Use `git-cl`, which comes with [depot
|
||||
tools](http://sites.google.com/a/chromium.org/dev/developers/how-tos/install-depot-tools).
|
||||
For help, run `git cl help`.
|
||||
Note that in order for `git cl` to work correctly, it needs to run on a clone of
|
||||
<https://skia.googlesource.com/skia>. Using clones of mirrors, including Google's mirror
|
||||
on GitHub, might lead to issues with `git cl` usage.
|
||||
|
||||
### Find a reviewer
|
||||
|
||||
Ideally, the reviewer is someone who is familiar with the area of code you are
|
||||
touching. Look at the git blame for the file to see who else has been editing
|
||||
it. If unsuccessful, another option is to click on the 'Suggested Reviewers' button
|
||||
to add one of the listed Skia contacts. They should be able to add appropriate
|
||||
reviewers for your change. The button is located here:
|
||||
<img src="/dev/contrib/SuggestedReviewers.png" style="display: inline-block; max-width: 75%" />
|
||||
|
||||
|
||||
### Uploading changes for review
|
||||
|
||||
Skia uses the Gerrit code review tool. Skia's instance is [skia-review](http://skia-review.googlesource.com).
|
||||
Use `git cl` to upload your change:
|
||||
|
||||
<!--?prettify lang=sh?-->
|
||||
|
||||
git cl upload
|
||||
|
||||
You may have to enter a Google Account username and password to authenticate
|
||||
yourself to Gerrit. A free gmail account will do fine, or any
|
||||
other type of Google account. It does not have to match the email address you
|
||||
configured using `git config --global user.email` above, but it can.
|
||||
|
||||
The command output should include a URL, similar to
|
||||
(https://skia-review.googlesource.com/c/4559/), indicating where your changelist
|
||||
can be reviewed.
|
||||
|
||||
### Submit try jobs
|
||||
|
||||
Skia's trybots allow testing and verification of changes before they land in the
|
||||
repo. You need to have permission to trigger try jobs; if you need permission,
|
||||
ask a committer. After uploading your CL to [Gerrit](https://skia-review.googlesource.com/),
|
||||
you may trigger a try job for any job listed in tasks.json, either via the
|
||||
Gerrit UI, using `git cl try`, eg.
|
||||
|
||||
git cl try -B skia.primary -b Some-Tryjob-Name
|
||||
|
||||
or using bin/try, a small wrapper for `git cl try` which helps to choose try jobs.
|
||||
From a Skia checkout:
|
||||
|
||||
bin/try --list
|
||||
|
||||
You can also search using regular expressions:
|
||||
|
||||
bin/try "Test.*GTX660.*Release"
|
||||
|
||||
For more information about testing, see [testing infrastructure](https://skia.org/dev/testing/automated_testing).
|
||||
|
||||
### Request review
|
||||
|
||||
Go to the supplied URL or go to the code review page and select the **Your**
|
||||
dropdown and click on **Changes**. Select the change you want to submit for
|
||||
review and click **Reply**. Enter at least one reviewer's email address. Now
|
||||
add any optional notes, and send your change off for review by clicking on
|
||||
**Send**. Unless you send your change to reviewers, no one will know to look
|
||||
at it.
|
||||
|
||||
_Note_: If you don't see editing commands on the review page, click **Sign in**
|
||||
in the upper right. _Hint_: You can add -r reviewer@example.com --send-mail to
|
||||
send the email directly when uploading a change using `git-cl`.
|
||||
|
||||
|
||||
The review process
|
||||
------------------
|
||||
|
||||
If you submit a giant patch, or do a bunch of work without discussing it with
|
||||
the relevant people, you may have a hard time convincing anyone to review it!
|
||||
|
||||
Code reviews are an important part of the engineering process. The reviewer will
|
||||
almost always have suggestions or style fixes for you, and it's important not to
|
||||
take such suggestions personally or as a commentary on your abilities or ideas.
|
||||
This is a process where we work together to make sure that the highest quality
|
||||
code gets submitted!
|
||||
|
||||
You will likely get email back from the reviewer with comments. Fix these and
|
||||
update the patch set in the issue by uploading again. The upload will explain
|
||||
that it is updating the current CL and ask you for a message explaining the
|
||||
change. Be sure to respond to all comments before you request review of an
|
||||
update.
|
||||
|
||||
If you need to update code the code on an already uploaded CL, simply edit the
|
||||
code, commit it again locally, and then run git cl upload again e.g.
|
||||
|
||||
echo "GOATS" > whitespace.txt
|
||||
git add whitespace.txt
|
||||
git commit -m 'add GOATS fix to whitespace.txt'
|
||||
git cl upload
|
||||
|
||||
Once you're ready for another review, use **Reply** again to send another
|
||||
notification (it is helpful to tell the reviewer what you did with respect to
|
||||
each of their comments). When the reviewer is happy with your patch, they will
|
||||
approve your change by setting the Code-Review label to "+1".
|
||||
|
||||
_Note_: As you work through the review process, both you and your reviewers
|
||||
should converse using the code review interface, and send notes.
|
||||
|
||||
Once your change has received an approval, you can click the "Submit to CQ"
|
||||
button on the codereview page and it will be committed on your behalf.
|
||||
|
||||
Once your commit has gone in, you should delete the branch containing your change:
|
||||
|
||||
git checkout -q origin/master
|
||||
git branch -D my_feature
|
||||
|
||||
|
||||
Final Testing
|
||||
-------------
|
||||
|
||||
Skia's principal downstream user is Chromium, and any change to Skia rendering
|
||||
output can break Chromium. If your change alters rendering in any way, you are
|
||||
expected to test for and alleviate this. You may be able to find a Skia team
|
||||
member to help you, but the onus remains on each individual contributor to avoid
|
||||
breaking Chrome.
|
||||
|
||||
### Evaluating Impact on Chromium
|
||||
|
||||
Keep in mind that Skia is rolled daily into Blink and Chromium. Run local tests
|
||||
and watch canary bots for results to ensure no impact. If you are submitting
|
||||
changes that will impact layout tests, follow the guides below and/or work with
|
||||
your friendly Skia-Blink engineer to evaluate, rebaseline, and land your
|
||||
changes.
|
||||
|
||||
Resources:
|
||||
|
||||
[How to land Skia changes that change Blink layout test results](../chrome/layouttest)
|
||||
|
||||
If you're changing the Skia API, you may need to make an associated change in Chromium.
|
||||
If you do, please follow these instructions: [Landing Skia changes which require Chrome changes](../chrome/changes)
|
||||
|
||||
|
||||
Check in your changes
|
||||
---------------------
|
||||
|
||||
### Non-Skia-committers
|
||||
|
||||
If you already have committer rights, you can follow the directions below to
|
||||
commit your change directly to Skia's repository.
|
||||
|
||||
If you don't have committer rights in https://skia.googlesource.com/skia.git ...
|
||||
first of all, thanks for submitting your patch! We really appreciate these
|
||||
submissions. After receiving an approval from a committer, you will be able to
|
||||
click the "Submit to CQ" button and submit your patch via the commit queue.
|
||||
|
||||
In special instances, a Skia committer may assist you in landing the change
|
||||
by uploading a new codereview containing your patch (perhaps with some small
|
||||
adjustments at their discretion). If so, you can mark your change as
|
||||
"Abandoned", and update it with a link to the new codereview.
|
||||
|
||||
### Skia committers
|
||||
* tips on how to apply an externally provided patch are [here](./patch)
|
||||
* when landing externally contributed patches, please note the original
|
||||
contributor's identity (and provide a link to the original codereview) in the commit message
|
||||
|
||||
`git-cl` will squash all your commits into a single one with the description you used when you uploaded your change.
|
||||
|
||||
~~~~
|
||||
git cl land
|
||||
~~~~
|
||||
|
||||
or
|
||||
|
||||
~~~~
|
||||
git cl land -c 'Contributor Name <email@example.com>'
|
||||
~~~~
|
||||
|
12
site2/docs/dev/design/_index.md
Normal file
12
site2/docs/dev/design/_index.md
Normal file
@ -0,0 +1,12 @@
|
||||
|
||||
---
|
||||
title: "Design Documents"
|
||||
linkTitle: "Design Documents"
|
||||
|
||||
weight: 4
|
||||
|
||||
---
|
||||
|
||||
|
||||
Public design documents for major efforts in Skia
|
||||
|
11
site2/docs/dev/design/aaa.md
Normal file
11
site2/docs/dev/design/aaa.md
Normal file
@ -0,0 +1,11 @@
|
||||
|
||||
---
|
||||
title: "Analytic Anti-Alias"
|
||||
linkTitle: "Analytic Anti-Alias"
|
||||
|
||||
---
|
||||
|
||||
|
||||
* [Analytic Anti-Alias Slides](https://docs.google.com/presentation/d/16r9HMS4_UBrcF3HUHscAqbSgkrtIwqaihZNwGP2TL_s/edit?usp=sharing)
|
||||
* [Analytic Anti-Alias Documents](https://docs.google.com/document/d/17Gq-huAf9q7wA4MRfXwpi_bYLrVeteKcSfAep0Am-wA/edit?usp=sharing)
|
||||
|
331
site2/docs/dev/design/conical/_index.md
Normal file
331
site2/docs/dev/design/conical/_index.md
Normal file
@ -0,0 +1,331 @@
|
||||
---
|
||||
title: 'Two-point Conical Gradient'
|
||||
linkTitle: 'Two-point Conical Gradient'
|
||||
---
|
||||
|
||||
<script type="text/x-mathjax-config">
|
||||
MathJax.Hub.Config({
|
||||
tex2jax: {
|
||||
inlineMath: [['$','$'], ['\\(','\\)']]
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<script src='https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/MathJax.js?config=TeX-MML-AM_CHTML'></script>
|
||||
|
||||
(Please refresh the page if you see a lot of dollars instead of math symbols.)
|
||||
|
||||
We present a fast shading algorithm (compared to bruteforcely solving the quadratic equation of
|
||||
gradient $t$) for computing the two-point conical gradient (i.e., `createRadialGradient` in
|
||||
[spec](https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-createradialgradient)).
|
||||
It reduced the number of multiplications per pixel from ~10 down to 3, and brought a speedup of up to
|
||||
26% in our nanobenches.
|
||||
|
||||
This document has 3 parts:
|
||||
|
||||
1. [Problem Statement and Setup](#problem-statement)
|
||||
2. [Algorithm](#algorithm)
|
||||
3. [Appendix](#appendix)
|
||||
|
||||
Part 1 and 2 are self-explanatory. Part 3 shows how to geometrically proves our Theorem 1 in part
|
||||
2; it's more complicated but it gives us a nice picture about what's going on.
|
||||
|
||||
## <span id="problem-statement">Problem Statement and Setup</span>
|
||||
|
||||
Let two circles be $C_0, r_0$ and $C_1, r_1$ where $C$ is the center and $r$ is the radius. For any
|
||||
point $P = (x, y)$ we want the shader to quickly compute a gradient $t \in \mathbb R$ such that $p$
|
||||
is on the linearly interpolated circle with center $C_t = (1-t) \cdot C_0 + t \cdot C_1$ and radius
|
||||
$r_t = (1-t) \cdot r_0 + t \cdot r_1 > 0$ (note that radius $r_t$ has to be _positive_). If
|
||||
there are multiple (at most 2) solutions of $t$, choose the bigger one.
|
||||
|
||||
There are two degenerated cases:
|
||||
|
||||
1. $C_0 = C_1$ so the gradient is essentially a simple radial gradient.
|
||||
2. $r_0 = r_1$ so the gradient is a single strip with bandwidth $2 r_0 = 2 r_1$.
|
||||
|
||||
<!-- TODO maybe add some fiddle or images here to illustrate the two degenerated cases -->
|
||||
|
||||
They are easy to handle so we won't cover them here. From now on, we assume $C_0 \neq C_1$ and $r_0
|
||||
\neq r_1$.
|
||||
|
||||
As $r_0 \neq r_1$, we can find a focal point $C_f = (1-f) \cdot C_0 + f \cdot C_1$ where its
|
||||
corresponding linearly interpolated radius $r_f = (1-f) \cdot r_0 + f \cdot r_1 = 0$.
|
||||
Solving the latter equation gets us $f = r_0 / (r_0 - r_1)$.
|
||||
|
||||
As $C_0 \neq C_1$, focal point $C_f$ is different from $C_1$ unless $r_1 = 0$. If $r_1 = 0$, we can
|
||||
swap $C_0, r_0$ with $C_1, r_1$, compute swapped gradient $t_s$ as if $r_1 \neq 0$, and finally set
|
||||
$t = 1 - t_s$. The only catch here is that with multiple solutions of $t_s$, we shall choose the
|
||||
smaller one (so $t$ could be the bigger one).
|
||||
|
||||
Assuming that we've done swapping if necessary so $C_1 \neq C_f$, we can then do a linear
|
||||
transformation to map $C_f, C_1$ to $(0, 0), (1, 0)$. After the transformation:
|
||||
|
||||
1. All centers $C_t = (x_t, 0)$ must be on the $x$ axis
|
||||
2. The radius $r_t$ is $x_t r_1$.
|
||||
3. Given $x_t$ , we can derive $t = f + (1 - f) x_t$
|
||||
|
||||
From now on, we'll focus on how to quickly computes $x_t$. Note that $r_t > 0$ so we're only
|
||||
interested positive solution $x_t$. Again, if there are multiple $x_t$ solutions, we may want to
|
||||
find the bigger one if $1 - f > 0$, and smaller one if $1 - f < 0$, so the corresponding $t$ is
|
||||
always the bigger one (note that $f \neq 1$, otherwise we'll swap $C_0, r_0$ with $C_1, r_1$).
|
||||
|
||||
## <span id="algorithm">Algorithm</span>
|
||||
|
||||
**Theorem 1.** The solution to $x_t$ is
|
||||
|
||||
1. $\frac{x^2 + y^2}{(1 + r_1) x} = \frac{x^2 + y^2}{2 x}$ if $r_1 = 1$
|
||||
2. $\left(\sqrt{(r_1^2 - 1) y ^2 + r_1^2 x^2} - x\right) / (r_1^2 - 1)$ if $r_1 > 1$
|
||||
3. $\left(\pm \sqrt{(r_1^2 - 1) y ^2 + r_1^2 x^2} - x\right) / (r_1^2 - 1)$ if $r_1 < 1$.
|
||||
|
||||
Case 2 always produces a valid $x_t$. Case 1 and 3 requires $x > 0$ to produce valid $x_t > 0$. Case
|
||||
3 may have no solution at all if $(r_1^2 - 1) y^2 + r_1^2 x^2 < 0$.
|
||||
|
||||
_Proof._ Algebriacally, solving the quadratic equation $(x_t - x)^2 + y^2 = (x_t r_1)^2$ and
|
||||
eliminate negative $x_t$ solutions get us the theorem.
|
||||
|
||||
Alternatively, we can also combine Corollary 2., 3., and Lemma 4. in the Appendix to geometrically
|
||||
prove the theorem. $\square$
|
||||
|
||||
Theorem 1 by itself is not sufficient for our shader algorithm because:
|
||||
|
||||
1. we still need to compute $t$ from $x_t$ (remember that $t = f + (1-f) x_t$);
|
||||
2. we still need to handle cases of choosing the bigger/smaller $x_t$;
|
||||
3. we still need to handle the swapped case (we swap $C_0, r_0$ with $C_1, r_1$ if $r_1 = 0$);
|
||||
4. there are way too many multiplications and divisions in Theorem 1 that would slow our shader.
|
||||
|
||||
Issue 2 and 3 are solved by generating different shader code based on different situations. So they
|
||||
are mainly correctness issues rather than performance issues. Issue 1 and 4 are performance
|
||||
critical, and they will affect how we handle issue 2 and 3.
|
||||
|
||||
The key to handle 1 and 4 efficiently is to fold as many multiplications and divisions into the
|
||||
linear transformation matrix, which the shader has to do anyway (remember our linear transformation
|
||||
to map $C_f, C_1$ to $(0, 0), (1, 0)$).
|
||||
|
||||
For example, let $\hat x, \hat y = |1-f|x, |1-f|y$. Computing $\hat x_t$ with respect to $\hat x,
|
||||
\hat y$ allow us to have $t = f + (1 - f)x_t = f + \text{sign}(1-f) \cdot \hat x_t$. That saves us
|
||||
one multiplication. Applying similar techniques to Theorem 1 gets us:
|
||||
|
||||
1. If $r_1 = 1$, let $x' = x/2,~ y' = y/2$, then $x_t = (x'^2 + y'^2) / x'$.
|
||||
2. If $r_1 > 1$, let $x' = r_1 / (r_1^2 - 1) x,~ y' = \frac{\sqrt{r_1^2 - 1}}{r_1^2 - 1} y$, then
|
||||
$x_t = \sqrt{x'^2 + y'^2} - x' / r_1$
|
||||
3. If $r_1 < 1$, let $x' = r_1 / (r_1^2 - 1) x,~ y' = \frac{\sqrt{1 - r_1^2}}{r_1^2 - 1} y$, then
|
||||
$x_t = \pm\sqrt{x'^2 - y'^2} - x' / r_1$
|
||||
|
||||
Combining it with the swapping, the equation $t = f + (1-f) x_t$, and the fact that we only want
|
||||
positive $x_t > 0$ and bigger $t$, we have our final algorithm:
|
||||
|
||||
**Algorithm 1.**
|
||||
|
||||
1. Let $C'_0, r'_0, C'_1, r'_1 = C_0, r_0, C_1, r_1$ if there is no swapping and $C'_0,
|
||||
r'_0, C'_1, r'_1 = C_1, r_1, C_0, r_0$ if there is swapping.
|
||||
2. Let $f = r'_0 / (r'_0 - r'_1)$ and $1 - f = r'_1 / (r'_1 - r'_0)$
|
||||
3. Let $x' = x/2,~ y' = y/2$ if $r_1 = 1$, and
|
||||
$x' = r_1 / (r_1^2 - 1) x,~ y' = \sqrt{|r_1^2 - 1|} / (r_1^2 - 1) y$ if $r_1 \neq 1$
|
||||
4. Let $\hat x = |1 - f|x', \hat y = |1 - f|y'$
|
||||
5. If $r_1 = 1$, let $\hat x_t = (\hat x^2 + \hat y^2) / \hat x$
|
||||
6. If $r_1 > 1$,
|
||||
let $\hat x_t = \sqrt{\hat x^2 + \hat y^2} - \hat x / r_1$
|
||||
7. If $r_1 < 1$
|
||||
8. return invalid if $\hat x^2 - \hat y^2 < 0$
|
||||
9. let $\hat x_t = -\sqrt{\hat x^2 - \hat y^2} - \hat x / r_1$ if we've swapped $r_0, r_1$,
|
||||
or if $1 - f < 0$
|
||||
|
||||
10. let $\hat x_t = \sqrt{\hat x^2 - \hat y^2} - \hat x / r_1$ otherwise
|
||||
|
||||
11. $t$ is invalid if $\hat x_t < 0$ (this check is unnecessary if $r_1 > 1$)
|
||||
12. Let $t = f + \text{sign}(1 - f) \hat x_t$
|
||||
13. If swapped, let $t = 1 - t$
|
||||
|
||||
In step 7, we try to select either the smaller or bigger $\hat x_t$ based on whether the final $t$
|
||||
has a negative or positive relationship with $\hat x_t$. It's negative if we've swapped, or if
|
||||
$\text{sign}(1 - f)$ is negative (these two cannot both happen).
|
||||
|
||||
Note that all the computations and if decisions not involving $\hat x, \hat y$ can be precomputed
|
||||
before the shading stage. The two if decisions $\hat x^2 - \hat y^2 < 0$ and $\hat x^t < 0$ can
|
||||
also be omitted by precomputing the shading area that never violates those conditions.
|
||||
|
||||
The number of operations per shading is thus:
|
||||
|
||||
- 1 addition, 2 multiplications, and 1 division if $r_1 = 1$
|
||||
- 2 additions, 3 multiplications, and 1 sqrt for $r_1 \neq 1$ (count subtraction as addition;
|
||||
dividing $r_1$ is multiplying $1/r_1$)
|
||||
- 1 more addition operation if $f \neq 0$
|
||||
- 1 more addition operation if swapped.
|
||||
|
||||
In comparison, for $r_1 \neq 1$ case, our current raster pipeline shading algorithm (which shall
|
||||
hopefully soon be upgraded to the algorithm described here) mainly uses formula $$t = 0.5 \cdot
|
||||
(1/a) \cdot \left(-b \pm \sqrt{b^2 - 4ac}\right)$$ It precomputes $a = 1 - (r_1 - r_0)^2, 1/a, r1 -
|
||||
r0$. Number $b = -2 \cdot (x + (r1 - r0) \cdot r0)$ costs 2 multiplications and 1 addition. Number
|
||||
$c = x^2 + y^2 - r_0^2$ costs 3 multiplications and 2 additions. And the final $t$ costs 5 more
|
||||
multiplications, 1 more sqrt, and 2 more additions. That's a total of 5 additions, 10
|
||||
multiplications, and 1 sqrt. (Our algorithm has 2-4 additions, 3 multiplications, and 1 sqrt.) Even
|
||||
if it saves the $0.5 \cdot (1/a), 4a, r_0^2$ and $(r_1 - r_0) r_0$ multiplications, there are still
|
||||
6 multiplications. Moreover, it sends in 4 unitofmrs to the shader while our algorithm only needs 2
|
||||
uniforms ($1/r_1$ and $f$).
|
||||
|
||||
## <span id="appendix">Appendix</span>
|
||||
|
||||
**Lemma 1.** Draw a ray from $C_f = (0, 0)$ to $P = (x, y)$. For every
|
||||
intersection points $P_1$ between that ray and circle $C_1 = (1, 0), r_1$, there exists an $x_t$
|
||||
that equals to the length of segment $C_f P$ over length of segment $C_f P_1$. That is,
|
||||
$x_t = || C_f P || / ||C_f P_1||$
|
||||
|
||||
_Proof._ Draw a line from $P$ that's parallel to $C_1 P_1$. Let it intersect with $x$-axis on point
|
||||
$C = (x', y')$.
|
||||
|
||||
<img src="conical/lemma1.svg"/>
|
||||
|
||||
Triangle $\triangle C_f C P$ is similar to triangle $\triangle C_f C_1 P_1$.
|
||||
Therefore $||P C|| = ||P_1 C_1|| \cdot (||C_f C|| / ||C_f C_1||) = r_1 x'$. Thus $x'$ is a solution
|
||||
to $x_t$. Because triangle $\triangle C_f C P$ and triangle $\triangle C_f C_1 P_1$ are similar, $x'
|
||||
= ||C_f C_1|| \cdot (||C_f P|| / ||C_f P_1||) = ||C_f P|| / ||C_f P_1||$. $\square$
|
||||
|
||||
**Lemma 2.** For every solution $x_t$, if we extend/shrink segment $C_f P$ to $C_f P_1$ with ratio
|
||||
$1 / x_t$ (i.e., find $P_1$ on ray $C_f P$ such that $||C_f P_1|| / ||C_f P|| = 1 / x_t$), then
|
||||
$P_1$ must be on circle $C_1, r_1$.
|
||||
|
||||
_Proof._ Let $C_t = (x_t, 0)$. Triangle $\triangle C_f C_t P$ is similar to $C_f C_1 P_1$. Therefore
|
||||
$||C_1 P_1|| = r_1$ and $P_1$ is on circle $C_1, r_1$. $\square$
|
||||
|
||||
**Corollary 1.** By lemma 1. and 2., we conclude that the number of solutions $x_t$ is equal to the
|
||||
number of intersections between ray $C_f P$ and circle $C_1, r_1$. Therefore
|
||||
|
||||
- when $r_1 > 1$, there's always one unique intersection/solution; we call this "well-behaved"; this
|
||||
was previously known as the "inside" case;
|
||||
- when $r_1 = 1$, there's either one or zero intersection/solution (excluding $C_f$ which is always
|
||||
on the circle); we call this "focal-on-circle"; this was previously known as the "edge" case;
|
||||
|
||||
<img src="conical/corollary2.2.1.svg"/>
|
||||
<img src="conical/corollary2.2.2.svg"/>
|
||||
|
||||
- when $r_1 < 1$, there may be $0, 1$, or $2$ solutions; this was also previously as the "outside"
|
||||
case.
|
||||
|
||||
<img src="conical/corollary2.3.1.svg" width="30%"/>
|
||||
<img src="conical/corollary2.3.2.svg" width="30%"/>
|
||||
<img src="conical/corollary2.3.3.svg" width="30%"/>
|
||||
|
||||
**Lemma 3.** When solution exists, one such solution is
|
||||
|
||||
$$
|
||||
x_t = {|| C_f P || \over ||C_f P_1||} = \frac{x^2 + y^2}{x + \sqrt{(r_1^2 - 1) y^2 + r_1^2 x^2}}
|
||||
$$
|
||||
|
||||
_Proof._ As $C_f = (0, 0), P = (x, y)$, we have $||C_f P|| = \sqrt(x^2 + y^2)$. So we'll mainly
|
||||
focus on how to compute $||C_f P_1||$.
|
||||
|
||||
**When $x \geq 0$:**
|
||||
|
||||
<img src="conical/lemma3.1.svg"/>
|
||||
|
||||
Let $X_P = (x, 0)$ and $H$ be a point on $C_f P_1$ such that $C_1 H$ is perpendicular to $C_1
|
||||
P_1$. Triangle $\triangle C_1 H C_f$ is similar to triangle $\triangle P X_P C_f$. Thus
|
||||
$$||C_f H|| = ||C_f C_1|| \cdot (||C_f X_P|| / ||C_f P||) = x / \sqrt{x^2 + y^2}$$
|
||||
$$||C_1 H|| = ||C_f C_1|| \cdot (||P X_P|| / ||C_f P||) = y / \sqrt{x^2 + y^2}$$
|
||||
|
||||
Triangle $\triangle C_1 H P_1$ is a right triangle with hypotenuse $r_1$. Hence
|
||||
$$ ||H P_1|| = \sqrt{r_1^2 - ||C_1 H||^2} = \sqrt{r_1^2 - y^2 / (x^2 + y^2)} $$
|
||||
|
||||
We have
|
||||
\begin{align}
|
||||
||C_f P_1|| &= ||C_f H|| + ||H P_1|| \\\\\\
|
||||
&= x / \sqrt{x^2 + y^2} + \sqrt{r_1^2 - y^2 / (x^2 + y^2)} \\\\\\
|
||||
&= \frac{x + \sqrt{r_1^2 (x^2 + y^2) - y^2}}{\sqrt{x^2 + y^2}} \\\\\\
|
||||
&= \frac{x + \sqrt{(r_1^2 - 1) y^2 + r_1^2 x^2}}{\sqrt{x^2 + y^2}}
|
||||
\end{align}
|
||||
|
||||
**When $x < 0$:**
|
||||
|
||||
Define $X_P$ and $H$ similarly as before except that now $H$ is on ray $P_1 C_f$ instead of
|
||||
$C_f P_1$.
|
||||
|
||||
<img src="conical/lemma3.2.svg"/>
|
||||
|
||||
As before, triangle $\triangle C_1 H C_f$ is similar to triangle $\triangle P X_P C_f$, and triangle
|
||||
$\triangle C_1 H P_1$ is a right triangle, so we have
|
||||
$$||C_f H|| = ||C_f C_1|| \cdot (||C_f X_P|| / ||C_f P||) = -x / \sqrt{x^2 + y^2}$$
|
||||
$$||C_1 H|| = ||C_f C_1|| \cdot (||P X_P|| / ||C_f P||) = y / \sqrt{x^2 + y^2}$$
|
||||
$$ ||H P_1|| = \sqrt{r_1^2 - ||C_1 H||^2} = \sqrt{r_1^2 - y^2 / (x^2 + y^2)} $$
|
||||
|
||||
Note that the only difference is changing $x$ to $-x$ because $x$ is negative.
|
||||
|
||||
Also note that now $||C_f P_1|| = -||C_f H|| + ||H P_1||$ and we have $-||C_f H||$ instead of
|
||||
$||C_f H||$. That negation cancels out the negation of $-x$ so we get the same equation
|
||||
of $||C_f P_1||$ for both $x \geq 0$ and $x < 0$ cases:
|
||||
|
||||
$$
|
||||
||C_f P_1|| = \frac{x + \sqrt{(r_1^2 - 1) y^2 + r_1^2 x^2}}{\sqrt{x^2 + y^2}}
|
||||
$$
|
||||
|
||||
Finally
|
||||
|
||||
$$
|
||||
x_t = \frac{||C_f P||}{||C_f P_1||} = \frac{\sqrt{x^2 + y^2}}{||C_f P_1||}
|
||||
= \frac{x^2 + y^2}{x + \sqrt{(r_1^2 - 1) y^2 + r_1^2 x^2}}
|
||||
$$ $\square$
|
||||
|
||||
**Corollary 2.** If $r_1 = 1$, then the solution $x_t = \frac{x^2 + y^2}{(1 + r_1) x}$, and
|
||||
it's valid (i.e., $x_t > 0$) iff $x > 0$.
|
||||
|
||||
*Proof.* Simply plug $r_1 = 1$ into the formula of Lemma 3. $\square$
|
||||
|
||||
**Corollary 3.** If $r_1 > 1$, then the unique solution is
|
||||
$x_t = \left(\sqrt{(r_1^2 - 1) y ^2 + r_1^2 x^2} - x\right) / (r_1^2 - 1)$.
|
||||
|
||||
*Proof.* From Lemma 3., we have
|
||||
|
||||
\begin{align}
|
||||
x_t &= \frac{x^2 + y^2}{x + \sqrt{(r_1^2 - 1) y^2 + r_1^2 x^2}} \\\\\\
|
||||
&= {
|
||||
(x^2 + y^2) \left ( -x + \sqrt{(r_1^2 - 1) y^2 + r_1^2 x^2} \right )
|
||||
\over
|
||||
\left (x + \sqrt{(r_1^2 - 1) y^2 + r_1^2 x^2} \right )
|
||||
\left (-x + \sqrt{(r_1^2 - 1) y^2 + r_1^2 x^2} \right )
|
||||
} \\\\\\
|
||||
&= {
|
||||
(x^2 + y^2) \left ( -x + \sqrt{(r_1^2 - 1) y^2 + r_1^2 x^2} \right )
|
||||
\over
|
||||
-x^2 + (r_1^2 - 1) y^2 + r_1^2 x^2
|
||||
} \\\\\\
|
||||
&= {
|
||||
(x^2 + y^2) \left ( -x + \sqrt{(r_1^2 - 1) y^2 + r_1^2 x^2} \right )
|
||||
\over
|
||||
(r_1^2 - 1) (x^2 + y^2)
|
||||
} \\\\\\
|
||||
&= \left(\sqrt{(r_1^2 - 1) y ^2 + r_1^2 x^2} - x\right) / (r_1^2 - 1)
|
||||
\end{align}
|
||||
|
||||
The transformation above (multiplying $-x + \sqrt{(r_1^2 - 1) y^2 + r_1^2 x^2}$ to enumerator and
|
||||
denomenator) is always valid because $r_1 > 1$ and it's the unique solution due to Corollary 1.
|
||||
$\square$
|
||||
|
||||
**Lemma 4.** If $r_1 < 1$, then
|
||||
|
||||
1. there's no solution to $x_t$ if $(r_1^2 - 1) y^2 + r_1^2 x^2 < 0$
|
||||
2. otherwise, the solutions are
|
||||
$x_t = \left(\sqrt{(r_1^2 - 1) y ^2 + r_1^2 x^2} - x\right) / (r_1^2 - 1)$,
|
||||
or
|
||||
$x_t = \left(-\sqrt{(r_1^2 - 1) y ^2 + r_1^2 x^2} - x\right) / (r_1^2 - 1)$.
|
||||
|
||||
(Note that solution $x_t$ still has to be nonnegative to be valid; also note that
|
||||
$x_t > 0 \Leftrightarrow x > 0$ if the solution exists.)
|
||||
|
||||
*Proof.* Case 1 follows naturally from Lemma 3. and Corollary 1.
|
||||
|
||||
<img src="conical/lemma4.svg"/>
|
||||
|
||||
For case 2, we notice that $||C_f P_1||$ could be
|
||||
|
||||
1. either $||C_f H|| + ||H P_1||$ or $||C_f H|| - ||H P_1||$ if $x \geq 0$,
|
||||
2. either $-||C_f H|| + ||H P_1||$ or $-||C_f H|| - ||H P_1||$ if $x < 0$.
|
||||
|
||||
By analysis similar to Lemma 3., the solution to $x_t$ does not depend on the sign of $x$ and
|
||||
they are either $\frac{x^2 + y^2}{x + \sqrt{(r_1^2 - 1) y^2 + r_1^2 x^2}}$
|
||||
or $\frac{x^2 + y^2}{x - \sqrt{(r_1^2 - 1) y^2 + r_1^2 x^2}}$.
|
||||
|
||||
As $r_1 \neq 1$, we can apply the similar transformation in Corollary 3. to get the two
|
||||
formula in the lemma.
|
||||
$\square$
|
||||
|
||||
|
||||
$$
|
453
site2/docs/dev/design/pdftheory.md
Normal file
453
site2/docs/dev/design/pdftheory.md
Normal file
@ -0,0 +1,453 @@
|
||||
---
|
||||
title: 'PDF Theory of Operation'
|
||||
linkTitle: 'PDF Theory of Operation'
|
||||
---
|
||||
|
||||
<!--
|
||||
PRE-GIT DOCUMENT VERSION HISTORY
|
||||
2012-06-25 Steve VanDeBogart
|
||||
* Original version
|
||||
2015-01-14 Hal Canary.
|
||||
* Add section "Using the PDF backend"
|
||||
* Markdown formatting
|
||||
-->
|
||||
|
||||
Internally, SkPDFDocument and SkPDFDevice represents PDF documents and pages.
|
||||
This document describes how the backend operates, but **these interfaces are not
|
||||
part of the public API and are subject to perpetual change.**
|
||||
|
||||
See [Using Skia's PDF Backend](../../user/sample/pdf) to find out how to use
|
||||
SkPDF as a client calling Skia's public API.
|
||||
|
||||
---
|
||||
|
||||
### Contents
|
||||
|
||||
- [Typical usage of the PDF backend](#Typical_usage_of_the_PDF_backend)
|
||||
- [PDF Objects and Document Structure](#PDF_Objects_and_Document_Structure)
|
||||
- [PDF drawing](#PDF_drawing)
|
||||
- [Interned objects](#Interned_objects)
|
||||
- [Graphic States](#Graphic_States)
|
||||
- [Clip and Transform](#Clip_and_Transform)
|
||||
- [Generating a content stream](#Generating_a_content_stream)
|
||||
- [Drawing details](#Drawing_details)
|
||||
- [Layers](#Layers)
|
||||
- [Fonts](#Fonts)
|
||||
- [Shaders](#Shaders)
|
||||
- [Xfer modes](#Xfer_modes)
|
||||
- [Known issues](#Known_issues)
|
||||
|
||||
## <span id="Typical_usage_of_the_PDF_backend">Typical usage of the PDF backend</span>
|
||||
|
||||
SkPDFDevice is the main interface to the PDF backend. This child of SkDevice can
|
||||
be set on an SkCanvas and drawn to. Once drawing to the canvas is complete
|
||||
(SkDocument::onEndPage() is called), the device's content and resources are
|
||||
added to the SkPDFDocument that owns the device. A new SkPDFDevice should be
|
||||
created for each page or layer desired in the document. After all the pages have
|
||||
been added to the document, `SkPDFDocument::onClose()` is called to finish
|
||||
serializing the PDF file.
|
||||
|
||||
## <span id="PDF_Objects_and_Document_Structure">PDF Objects and Document Structure</span>
|
||||
|
||||
![PDF Logical Document Structure](/dev/design/PdfLogicalDocumentStructure.png)
|
||||
|
||||
**Background**: The PDF file format has a header, a set of objects and then a
|
||||
footer that contains a table of contents for all of the objects in the document
|
||||
(the cross-reference table). The table of contents lists the specific byte
|
||||
position for each object. The objects may have references to other objects and
|
||||
the ASCII size of those references is dependent on the object number assigned to
|
||||
the referenced object; therefore we can't calculate the table of contents until
|
||||
the size of objects is known, which requires assignment of object numbers. The
|
||||
document uses SkWStream::bytesWritten() to query the offsets of each object and
|
||||
build the cross-reference table.
|
||||
|
||||
Furthermore, PDF files can support a _linearized_ mode, where objects are in a
|
||||
specific order so that pdf-viewers can more easily retrieve just the objects
|
||||
they need to display a specific page, i.e. by byte-range requests over the web.
|
||||
Linearization also requires that all objects used or referenced on the first
|
||||
page of the PDF have object numbers before the rest of the objects.
|
||||
Consequently, before generating a linearized PDF, all objects, their sizes, and
|
||||
object references must be known. Skia has no plans to implement linearized PDFs.
|
||||
|
||||
%PDF-1.4
|
||||
…objects...
|
||||
xref
|
||||
0 31 % Total number of entries in the table of contents.
|
||||
0000000000 65535 f
|
||||
0000210343 00000 n
|
||||
…
|
||||
0000117055 00000 n
|
||||
trailer
|
||||
<</Size 31 /Root 1 0 R>>
|
||||
startxref
|
||||
210399 % Byte offset to the start of the table of contents.
|
||||
%%EOF
|
||||
|
||||
The the virtual class SkPDFObject are used to manage the needs of the file
|
||||
format. Any object that will represent a PDF object must inherit from
|
||||
SkPDFObject and implement the methods to generate the binary representation and
|
||||
report any other SkPDFObjects used as resources. SkPDFTypes.h defines most of
|
||||
the basic PDF object types: bool, int, scalar, string, name, array, dictionary,
|
||||
and stream. (A stream is a dictionary containing at least a Length entry
|
||||
followed by the data of the stream.)
|
||||
|
||||
Streams are now handled in a slightly different way. The SkPDFStreamOut()
|
||||
function compresses and serializes the binary data immediately instead of
|
||||
creating a new object.
|
||||
|
||||
All of these PDF object types except the stream type can be used in both a
|
||||
direct and an indirect fashion, i.e. an array can have an int or a dictionary as
|
||||
an inline entry, which does not require an object number. The stream type,
|
||||
cannot be inlined and must be referred to with an object reference. Most of the
|
||||
time, other objects types can be referred to with an object reference, but there
|
||||
are specific rules in the PDF specification that requires an inline reference in
|
||||
some place or an indirect reference in other places. All indirect objects must
|
||||
have an object number assigned.
|
||||
|
||||
- **bools**: `true` `false`
|
||||
- **ints**: `42` `0` `-1`
|
||||
- **scalars**: `0.001`
|
||||
- **strings**: `(strings are in parentheses or byte encoded)` `<74657374>`
|
||||
- **name**: `/Name` `/Name#20with#20spaces`
|
||||
- **array**: `[/Foo 42 (arrays can contain multiple types)]`
|
||||
- **dictionary**: `<</Key1 (value1) /key2 42>>`
|
||||
- **indirect object**:
|
||||
`5 0 obj (An indirect string. Indirect objects have an object number and a generation number, Skia always uses generation 0 objects) endobj`
|
||||
- **object reference**: `5 0 R`
|
||||
- **stream**:
|
||||
`<</Length 56>> stream ...stream contents can be arbitrary, including binary... endstream`
|
||||
|
||||
Indirect objects are either:
|
||||
|
||||
- Serialized as soon as they are needed, and a new SkPDFIndirectReference is
|
||||
returned, or
|
||||
|
||||
- Serialized later, but reserve a document-unique SkPDFIndirectReference to
|
||||
allow other objects to refer to it.
|
||||
|
||||
Example document:
|
||||
|
||||
%PDF-1.4
|
||||
2 0 obj <<
|
||||
/Type /Catalog
|
||||
/Pages 1 0 R
|
||||
>>
|
||||
endobj
|
||||
3 0 obj <<
|
||||
/Type /Page
|
||||
/Parent 1 0 R
|
||||
/Resources <>
|
||||
/MediaBox [0 0 612 792]
|
||||
/Contents 4 0 R
|
||||
>>
|
||||
endobj
|
||||
4 0 obj <> stream
|
||||
endstream
|
||||
endobj
|
||||
1 0 obj <<
|
||||
/Type /Pages
|
||||
/Kids [3 0 R]
|
||||
/Count 1
|
||||
>>
|
||||
endobj
|
||||
xref
|
||||
0 5
|
||||
0000000000 65535 f
|
||||
0000000236 00000 n
|
||||
0000000009 00000 n
|
||||
0000000062 00000 n
|
||||
0000000190 00000 n
|
||||
trailer
|
||||
<</Size 5 /Root 2 0 R>>
|
||||
startxref
|
||||
299
|
||||
%%EOF
|
||||
|
||||
## <span id="PDF_drawing">PDF drawing</span>
|
||||
|
||||
Most drawing in PDF is specified by the text of a stream, referred to as a
|
||||
content stream. The syntax of the content stream is different than the syntax of
|
||||
the file format described above and is much closer to PostScript in nature. The
|
||||
commands in the content stream tell the PDF interpreter to draw things, like a
|
||||
rectangle (`x y w h re`), an image, or text, or to do meta operations like set
|
||||
the drawing color, apply a transform to the drawing coordinates, or clip future
|
||||
drawing operations. The page object that references a content stream has a list
|
||||
of resources that can be used in the content stream using the dictionary name to
|
||||
reference the resources. Resources are things like font objects, images objects,
|
||||
graphic state objects (a set of meta operations like miter limit, line width,
|
||||
etc). Because of a mismatch between Skia and PDF’s support for transparency
|
||||
(which will be explained later), SkPDFDevice records each drawing operation into
|
||||
an internal structure (ContentEntry) and only when the content stream is needed
|
||||
does it flatten that list of structures into the final content stream.
|
||||
|
||||
4 0 obj <<
|
||||
/Type /Page
|
||||
/Resources <<
|
||||
/Font <</F1 9 0 R>>
|
||||
/XObject <</Image1 22 0 R /Image2 73 0 R>>
|
||||
>>
|
||||
/Content 5 0 R
|
||||
>> endobj
|
||||
|
||||
5 0 obj <</Length 227>> stream
|
||||
% In the font specified in object 9 and a height
|
||||
% of 12 points, at (72, 96) draw ‘Hello World.’
|
||||
BT
|
||||
/F1 12 Tf
|
||||
72 96 Td
|
||||
(Hello World) Tj
|
||||
ET
|
||||
% Draw a filled rectange.
|
||||
200 96 72 72 re B
|
||||
...
|
||||
endstream
|
||||
endobj
|
||||
|
||||
## <span id="Interned_objects">Interned objects</span>
|
||||
|
||||
There are a number of high level PDF objects (like fonts, graphic states, etc)
|
||||
that are likely to be referenced multiple times in a single PDF. To ensure that
|
||||
there is only one copy of each object, the SkPDFDocument holds on to a mapping
|
||||
from type-specific keys onto the SkPDFIndirectReference for these objects.
|
||||
|
||||
## <span id="Graphic_States">Graphic States</span>
|
||||
|
||||
PDF has a number of parameters that affect how things are drawn. The ones that
|
||||
correspond to drawing options in Skia are: color, alpha, line cap, line join
|
||||
type, line width, miter limit, and xfer/blend mode (see later section for xfer
|
||||
modes). With the exception of color, these can all be specified in a single pdf
|
||||
object, represented by the SkPDFGraphicState class. A simple command in the
|
||||
content stream can then set the drawing parameters to the values specified in
|
||||
that graphic state object. PDF does not allow specifying color in the graphic
|
||||
state object, instead it must be specified directly in the content stream.
|
||||
Similarly the current font and font size are set directly in the content stream.
|
||||
|
||||
6 0 obj <<
|
||||
/Type /ExtGState
|
||||
/CA 1 % Opaque - alpha = 1
|
||||
/LC 0 % Butt linecap
|
||||
/LJ 0 % Miter line-join
|
||||
/LW 2 % Line width of 2
|
||||
/ML 6 % Miter limit of 6
|
||||
/BM /Normal % Blend mode is normal i.e. source over
|
||||
>>
|
||||
endobj
|
||||
|
||||
## <span id="Clip_and_Transform">Clip and Transform</span>
|
||||
|
||||
Similar to Skia, PDF allows drawing to be clipped or transformed. However, there
|
||||
are a few caveats that affect the design of the PDF backend. PDF does not
|
||||
support perspective transforms (perspective transform are treated as identity
|
||||
transforms). Clips, however, have more issues to cotend with. PDF clips cannot
|
||||
be directly unapplied or expanded. i.e. once an area has been clipped off, there
|
||||
is no way to draw to it. However, PDF provides a limited depth stack for the PDF
|
||||
graphic state (which includes the drawing parameters mentioned above in the
|
||||
Graphic States section as well as the clip and transform). Therefore to undo a
|
||||
clip, the PDF graphic state must be pushed before the clip is applied, then
|
||||
popped to revert to the state of the graphic state before the clip was applied.
|
||||
|
||||
As the canvas makes drawing calls into SkPDFDevice, the active transform, clip
|
||||
region, and clip stack are stored in a ContentEntry structure. Later, when the
|
||||
ContentEntry structures are flattened into a valid PDF content stream, the
|
||||
transforms and clips are compared to decide on an efficient set of operations to
|
||||
transition between the states needed. Currently, a local optimization is used,
|
||||
to figure out the best transition from one state to the next. A global
|
||||
optimization could improve things by more effectively using the graphics state
|
||||
stack provided in the PDF format.
|
||||
|
||||
## <span id="Generating_a_content_stream">Generating a content stream</span>
|
||||
|
||||
For each draw call on an SkPDFDevice, a new ContentEntry is created, which
|
||||
stores the matrix, clip region, and clip stack as well as the paint parameters.
|
||||
Most of the paint parameters are bundled into an SkPDFGraphicState (interned)
|
||||
with the rest (color, font size, etc) explicitly stored in the ContentEntry.
|
||||
After populating the ContentEntry with all the relevant context, it is compared
|
||||
to the the most recently used ContentEntry. If the context matches, then the
|
||||
previous one is appended to instead of using the new one. In either case, with
|
||||
the context populated into the ContentEntry, the appropriate draw call is
|
||||
allowed to append to the content stream snippet in the ContentEntry to affect
|
||||
the core of the drawing call, i.e. drawing a shape, an image, text, etc.
|
||||
|
||||
When all drawing is complete, SkPDFDocument::onEndPage() will call
|
||||
SkPDFDevice::content() to request the complete content stream for the page. The
|
||||
first thing done is to apply the initial transform specified in part in the
|
||||
constructor, this transform takes care of changing the coordinate space from an
|
||||
origin in the lower left (PDF default) to the upper left (Skia default) as well
|
||||
as any translation or scaling requested by the user (i.e. to achieve a margin or
|
||||
scale the canvas). Next (well almost next, see the next section), a clip is
|
||||
applied to restrict drawing to the content area (the part of the page inside the
|
||||
margins) of the page. Then, each ContentEntry is applied to the content stream
|
||||
with the help of a helper class, GraphicStackState, which tracks the state of
|
||||
the PDF graphics stack and optimizes the output. For each ContentEntry, commands
|
||||
are emitted to the final content entry to update the clip from its current state
|
||||
to the state specified in the ContentEntry, similarly the Matrix and drawing
|
||||
state (color, line joins, etc) are updated, then the content entry fragment (the
|
||||
actual drawing operation) is appended.
|
||||
|
||||
## <span id="Drawing_details">Drawing details</span>
|
||||
|
||||
Certain objects have specific properties that need to be dealt with. Images,
|
||||
layers (see below), and fonts assume the standard PDF coordinate system, so we
|
||||
have to undo any flip to the Skia coordinate system before drawing these
|
||||
entities. We don't currently support inverted paths, so filling an inverted path
|
||||
will give the wrong result ([issue 241](https://bug.skia.org/241)). PDF doesn't
|
||||
draw zero length lines that have butt of square caps, so that is emulated.
|
||||
|
||||
### <span id="Layers">Layers</span>
|
||||
|
||||
PDF has a higher level object called a form x-object (form external object) that
|
||||
is basically a PDF page, with resources and a content stream, but can be
|
||||
transformed and drawn on an existing page. This is used to implement layers.
|
||||
SkPDFDevice has a method, makeFormXObjectFromDevice(), which uses the
|
||||
SkPDFDevice::content() method to construct a form x-object from the the device.
|
||||
SkPDFDevice::drawDevice() works by creating a form x-object of the passed device
|
||||
and then drawing that form x-object in the root device. There are a couple
|
||||
things to be aware of in this process. As noted previously, we have to be aware
|
||||
of any flip to the coordinate system - flipping it an even number of times will
|
||||
lead to the wrong result unless it is corrected for. The SkClipStack passed to
|
||||
drawing commands includes the entire clip stack, including the clipping
|
||||
operations done on the base layer. Since the form x-object will be drawn as a
|
||||
single operation onto the base layer, we can assume that all of those clips are
|
||||
in effect and need not apply them within the layer.
|
||||
|
||||
### <span id="Fonts">Fonts</span>
|
||||
|
||||
There are many details for dealing with fonts, so this document will only talk
|
||||
about some of the more important ones. A couple short details:
|
||||
|
||||
- We can't assume that an arbitrary font will be available at PDF view time, so
|
||||
we embed all fonts in accordance with modern PDF guidelines.
|
||||
- Most fonts these days are TrueType fonts, so this is where most of the effort
|
||||
has been concentrated.
|
||||
- Because Skia may only be given a glyph-id encoding of the text to render and
|
||||
there is no perfect way to reverse the encoding, the PDF backend always uses
|
||||
the glyph-id encoding of the text.
|
||||
|
||||
#### _Type1/Type3 fonts_
|
||||
|
||||
Linux supports Type1 fonts, but Windows and Mac seem to lack the functionality
|
||||
required to extract the required information from the font without parsing the
|
||||
font file. When a non TrueType font is used any any platform (except for Type1
|
||||
on Linux), it is encoded as a Type3 font. In this context, a Type3 font is an
|
||||
array of form x-objects (content streams) that draw each glyph of the font. No
|
||||
hinting or kerning information is included in a Type3 font, just the shape of
|
||||
each glyph. Any font that has the do-not embed copy protection bit set will also
|
||||
get embedded as a Type3 font. From what I understand, shapes are not
|
||||
copyrightable, but programs are, so by stripping all the programmatic
|
||||
information and only embedding the shape of the glyphs we are honoring the
|
||||
do-not embed bit as much as required by law.
|
||||
|
||||
PDF only supports an 8-bit encoding for Type1 or Type3 fonts. However, they can
|
||||
contain more than 256 glyphs. The PDF backend handles this by segmenting the
|
||||
glyphs into groups of 255 (glyph id 0 is always the unknown glyph) and
|
||||
presenting the font as multiple fonts, each with up to 255 glyphs.
|
||||
|
||||
#### _Font subsetting_
|
||||
|
||||
Many fonts, especially fonts with CJK support are fairly large, so it is
|
||||
desirable to subset them. Chrome uses the SFNTLY package to provide subsetting
|
||||
support to Skia for TrueType fonts.
|
||||
|
||||
### <span id="Shaders">Shaders</span>
|
||||
|
||||
Skia has two types of predefined shaders, image shaders and gradient shaders. In
|
||||
both cases, shaders are effectively positioned absolutely, so the initial
|
||||
position and bounds of where they are visible is part of the immutable state of
|
||||
the shader object. Each of the Skia's tile modes needs to be considered and
|
||||
handled explicitly. The image shader we generate will be tiled, so tiling is
|
||||
handled by default. To support mirroring, we draw the image, reversed, on the
|
||||
appropriate axis, or on both axes plus a fourth in the vacant quadrant. For
|
||||
clamp mode, we extract the pixels along the appropriate edge and stretch the
|
||||
single pixel wide/long image to fill the bounds. For both x and y in clamp mode,
|
||||
we fill the corners with a rectangle of the appropriate color. The composed
|
||||
shader is then rotated or scaled as appropriate for the request.
|
||||
|
||||
Gradient shaders are handled purely mathematically. First, the matrix is
|
||||
transformed so that specific points in the requested gradient are at pre-defined
|
||||
locations, for example, the linear distance of the gradient is always normalized
|
||||
to one. Then, a type 4 PDF function is created that achieves the desired
|
||||
gradient. A type 4 function is a function defined by a resticted postscript
|
||||
language. The generated functions clamp at the edges so if the desired tiling
|
||||
mode is tile or mirror, we hav to add a bit more postscript code to map any
|
||||
input parameter into the 0-1 range appropriately. The code to generate the
|
||||
postscript code is somewhat obtuse, since it is trying to generate optimized
|
||||
(for space) postscript code, but there is a significant number of comments to
|
||||
explain the intent.
|
||||
|
||||
### <span id="Xfer_modes">Xfer modes</span>
|
||||
|
||||
PDF supports some of the xfer modes used in Skia directly. For those, it is
|
||||
simply a matter of setting the blend mode in the graphic state to the
|
||||
appropriate value (Normal/SrcOver, Multiply, Screen, Overlay, Darken, Lighten,
|
||||
!ColorDOdge, ColorBurn, HardLight, SoftLight, Difference, Exclusion). Aside from
|
||||
the standard SrcOver mode, PDF does not directly support the porter-duff xfer
|
||||
modes though. Most of them (Clear, SrcMode, DstMode, DstOver, SrcIn, DstIn,
|
||||
SrcOut, DstOut) can be emulated by various means, mostly by creating form
|
||||
x-objects out of part of the content and drawing it with a another form x-object
|
||||
as a mask. I have not figured out how to emulate the following modes: SrcATop,
|
||||
DstATop, Xor, Plus.
|
||||
|
||||
At the time of writing [2012-06-25], I have a
|
||||
[CL outstanding to fix a misunderstanding I had about the meaning of some of the emulated modes](https://codereview.appspot.com/4631078/).
|
||||
I will describe the system with this change applied.
|
||||
|
||||
First, a bit of terminology and definition. When drawing something with an
|
||||
emulated xfer mode, what's already drawn to the device is called the destination
|
||||
or Dst, and what's about to be drawn is the source or Src. Src (and Dst) can
|
||||
have regions where it is transparent (alpha equals zero), but it also has an
|
||||
inherent shape. For most kinds of drawn objects, the shape is the same as where
|
||||
alpha is not zero. However, for things like images and layers, the shape is the
|
||||
bounds of the item, not where the alpha is non-zero. For example, a 10x10 image,
|
||||
that is transparent except for a 1x1 dot in the center has a shape that is
|
||||
10x10. The xfermodes gm test demonstrates the interaction between shape and
|
||||
alpha in combination with the port-duff xfer modes.
|
||||
|
||||
The clear xfer mode removes any part of Dst that is within Src's shape. This is
|
||||
accomplished by bundling the current content of the device (Dst) into a single
|
||||
entity and then drawing that with the inverse of Src's shape used as a mask (we
|
||||
want Dst where Src isn't). The implementation of that takes a couple more steps.
|
||||
You may have to refer back to
|
||||
[the content stream section](#Generating_a_content_stream). For any draw call, a
|
||||
ContentEntry is created through a method called
|
||||
SkPDFDevice::setUpContentEntry(). This method examines the xfer modes in effect
|
||||
for that drawing operation and if it is an xfer mode that needs emulation, it
|
||||
creates a form x-object from the device, i.e. creates Dst, and stores it away
|
||||
for later use. This also clears all of that existing ContentEntry's on that
|
||||
device. The drawing operation is then allowed to proceed as normal (in most
|
||||
cases, see note about shape below), but into the now empty device. Then, when
|
||||
the drawing operation in done, a complementary method is
|
||||
called,SkPDFDevice::finishContentEntry(), which takes action if the current xfer
|
||||
mode is emulated. In the case of Clear, it packages what was just drawn into
|
||||
another form x-object, and then uses the Src form x-object, an invert function,
|
||||
and the Dst form x-object to draw Dst with the inverse shape of Src as a mask.
|
||||
This works well when the shape of Src is the same as the opaque part of the
|
||||
drawing, since PDF uses the alpha channel of the mask form x-object to do
|
||||
masking. When shape doesn't match the alpha channel, additional action is
|
||||
required. The drawing routines where shape and alpha don't match, set state to
|
||||
indicate the shape (always rectangular), which finishContentEntry uses. The
|
||||
clear xfer mode is a special case; if shape is needed, then Src isn't used, so
|
||||
there is code to not bother drawing Src if shape is required and the xfer mode
|
||||
is clear.
|
||||
|
||||
SrcMode is clear plus Src being drawn afterward. DstMode simply omits drawing
|
||||
Src. DstOver is the same as SrcOver with Src and Dst swapped - this is
|
||||
accomplished by inserting the new ContentEntry at the beginning of the list of
|
||||
ContentEntry's in setUpContentEntry instead of at the end. SrcIn, SrcOut, DstIn,
|
||||
DstOut are similar to each, the difference being an inverted or non-inverted
|
||||
mask and swapping Src and Dst (or not). SrcIn is SrcMode with Src drawn with Dst
|
||||
as a mask. SrcOut is like SrcMode, but with Src drawn with an inverted Dst as a
|
||||
mask. DstIn is SrcMode with Dst drawn with Src as a mask. Finally, DstOut is
|
||||
SrcMode with Dst draw with an inverted Src as a mask.
|
||||
|
||||
## <span id="Known_issues">Known issues</span>
|
||||
|
||||
- [issue 249](https://bug.skia.org/249) SrcAtop Xor, and Plus xfer modes are not
|
||||
supported.
|
||||
- [issue 240](https://bug.skia.org/240) drawVerticies is not implemented.
|
||||
- [issue 244](https://bug.skia.org/244) Mostly, only TTF fonts are _directly_
|
||||
supported. (User metrics show that almost all fonts are truetype.)
|
||||
- [issue 260](https://bug.skia.org/260) Page rotation is accomplished by
|
||||
specifying a different size page instead of including the appropriate rotation
|
||||
annotation.
|
||||
|
||||
---
|
204
site2/docs/dev/design/raster_tragedy/_index.md
Normal file
204
site2/docs/dev/design/raster_tragedy/_index.md
Normal file
@ -0,0 +1,204 @@
|
||||
|
||||
---
|
||||
title: "The Raster Tragedy in Skia"
|
||||
linkTitle: "The Raster Tragedy in Skia"
|
||||
|
||||
---
|
||||
|
||||
|
||||
This is an extension of [The Raster Tragedy at Low-Resolution Revisited](http://rastertragedy.com)
|
||||
as it applies to Skia. The Raster Tragedy describes a number of issues with typeface rasterization
|
||||
with a particular emphasis on proper hinting to overcome these issues. Since not all fonts
|
||||
are nicely hinted and sometimes hinting is not desired, there are additional hacks which may
|
||||
be applied. Generally, one wants to hint purely informational text laid out for a particular
|
||||
device, but not hint text which is art. Unless, of course, the hinting is part of the art like
|
||||
Shift_JIS art.
|
||||
|
||||
|
||||
|
||||
The Gamma Hack
|
||||
--------------
|
||||
|
||||
First, one should be aware of transfer functions (of which 'gamma' is an
|
||||
example). A good introduction can be had at [What Every Coder Should Know About
|
||||
Gamma](https://blog.johnnovak.net/2016/09/21/what-every-coder-should-know-about-gamma/).
|
||||
|
||||
In Skia, all color sources are converted into the destination color space and the blending is done
|
||||
in the destination color space by applying the linear blend function. Skia does not convert into
|
||||
a linear space, apply the linear blend, and convert back to the encoded space. If the destination
|
||||
color space does not have a linear encoding this will lead to 'incorrect' blending. The idea is
|
||||
that there are essentially two kinds of users of Skia. First there are existing systems which
|
||||
are already using a non-linear encoding with a linear blend function. While the blend isn't
|
||||
correct, these users generally don't want anything to change due to expectations. Second there
|
||||
are those who want everything done correctly and they are willing to pay for a linearly encoded
|
||||
destination in which the linear blend function is correct.
|
||||
|
||||
For bi-level glyph rendering a pixel is either covered or not, so there are no coverage blending
|
||||
issues.
|
||||
|
||||
For regular full pixel partial coverage (anti-aliased) glyph rendering the user may or may not
|
||||
want correct linear blending. In most non-linear encodings, using the linear blend function
|
||||
tends to make black on white look slightly heavier, using the pixel grid as a kind of contrast
|
||||
and optical sizing enhancement. It does the opposite for white on black, often making such
|
||||
glyphs a bit under-covered. However, this fights the common issue of blooming where light on
|
||||
dark on many displays tends to appear thicker than dark on light. (The black not being fully
|
||||
black also contributes.) If the pixels are small enough and there is proper optical sizing and
|
||||
perhaps anti-aliased drop out control (these latter two achieved either manually with proper
|
||||
font selection or 'opsz', automatically, or through hinting) then correct linear blending tends
|
||||
to look great. Otherwise black on white text tends to (correctly) get really anemic looking at
|
||||
small sizes. So correct blending of glyph masks here should be left up to the user of Skia. If
|
||||
they're really sophisticated and already tackled these issues then they may want linear blending
|
||||
of the glyphs for best effect. Otherwise the glyphs should just keep looking like they used to
|
||||
look due to expectations.
|
||||
|
||||
For subpixel partial coverage (subpixel anti-aliased) glyph masks linear blending in a
|
||||
linear encoding is more or less required to avoid color fringing effects. The intensity of
|
||||
the subpixels is being directly exploited so needs to be carefully controlled. The subpixels
|
||||
tend to alleviate the issues with no full coverage (though still problematic if blitting text
|
||||
in one of the display's primaries). One will still want optical sizing since the glyphs will
|
||||
still look somewhat too light when scaled down linearly.
|
||||
|
||||
So, if subpixel anti-aliased glyph masks (and sometimes full pixel anti-aliased glyph masks)
|
||||
need a correct blit how are they to be used with non-linearly encoded destinations?
|
||||
|
||||
One possible solution is to special case these blits. If blitting on the CPU it's often fast and
|
||||
close enough to take the square root of the source and destination values, do the linear blend
|
||||
function, then square the result (approximating the destination encoding as if its transfer
|
||||
function is square). Many GPUs have a mode where they can blend in sRGB, though unfortunately
|
||||
this generally applies to the whole framebuffer, not just individual draws. For various reasons,
|
||||
Skia has avoided special casing these blends.
|
||||
|
||||
What Skia currently does is the gamma hack. When creating the glyph mask one usually knows
|
||||
the approximate color which is going to be drawn, the transfer function, and that a linear
|
||||
source-over blend is going to be used. The destination color is then guessed to be a contrasting
|
||||
color (if there isn't any contrast the drawing won't be able to be seen anyway) so assume that the
|
||||
destination color will be the perceptually opposite color in the destination color space. One can
|
||||
now determine the desired value by converting the perceptual source and guessed destination into
|
||||
a linear encoding, do the linear source-over blend, and convert to the destination encoding. The
|
||||
coverage is then adjusted so that the result of a linear source-over blend on the non-linear
|
||||
encoded values will be as close as possible to this desired value.
|
||||
|
||||
This works, but makes many assumptions. The first is the guess at the destination. A perceptual
|
||||
middle gray could equally well (or poorly) contrast with black or white so the best guess
|
||||
is drawing it on top of itself. Subpixel anti-aliased glyph masks drawn with this guess will
|
||||
be drawn without any adjustment at all, leaving them color fringy. On macOS Skia tweaks the
|
||||
destination guess to reduce the correction when using bright sources. This helps reducing
|
||||
'blooming' issues with light on dark (aka dark mode) and better matches how CoreText seems
|
||||
to do blending. The second is that a src-over blend is assumed, but users generally aren't as
|
||||
discriminating of the results of other blends.
|
||||
|
||||
The gamma hack works best with subpixel anti-aliasing since the adjustment can be made per-channel
|
||||
instead of full pixel. If this hack is applied to full pixel anti-aliased masks everything is
|
||||
essentially being done in the nearest gray instead of the nearest color (since there is only
|
||||
one channel through which to communicate), leading to poor results. Most users will not want
|
||||
the gamma hack applied to full pixel anti-aliased glyphs.
|
||||
|
||||
Since the gamma hack is logically part of the blend, it must always be the very last adjustment
|
||||
made to the coverage mask before being used by the blitter. The inputs are the destination
|
||||
transfer function and the current color (in the destination color space). In Skia these come
|
||||
from the color space on the SkSurface and the color on the SkPaint.
|
||||
|
||||
|
||||
Optical Sizing Hack
|
||||
-------------------
|
||||
|
||||
In metal type a type designer will draw out on paper the design for the faces of the final
|
||||
sorts. Often there would be different drawings for different target sizes. Sometimes these
|
||||
different sizes look quite different (like Display and Text faces of the same family). Then a
|
||||
punch cutter takes these drawings and creates a piece of metal called a punch which can stamp
|
||||
out these faces. The punch is used to create a negative in a softer piece of metal forming a
|
||||
strike. This strike is made the correct width and called a matrix. This matrix is used as a mold
|
||||
to cast individual sorts. The sorts are collected into a box called a type case. A typesetter
|
||||
would take sorts from a type case and set them into a form. The printer would then ink the
|
||||
sorts in the form and press the paper. (Note that the terms 'typeface' and 'font' aren't used
|
||||
in this description, there is a lot of disagreement on what they apply to.)
|
||||
|
||||
Every step of this process is now automated in some way. Unfortunately, knowledge embedded in
|
||||
the manual process has not always been replicated in the automation. This can be for a wide
|
||||
variety of reasons, such as being overlooked or being difficult to emulate. One of these areas
|
||||
is the art of optical sizing and managing thin features.
|
||||
|
||||
In general smaller type should be relatively heavier in weight than would be expected if taking
|
||||
a larger size and linearly scaling it down. The type designer will draw out the faces with this
|
||||
in mind, potentially with an eye toward how it would vary with size and with potential need
|
||||
for ink traps. The punch cutter would then cut the punches at a given size with this in mind,
|
||||
adjusting until the soot proofs looked good. There may even be slight adjustments to the matrix
|
||||
itself if something seemed off. The typesetter would often know which specific cases contained
|
||||
slightly heavier or lighter sorts. The printer would then adjust the ink and pressure to get
|
||||
a good looking print.
|
||||
|
||||
Popular digital font formats didn't really support this until recently with the variable font
|
||||
'opsz' axis. The presence of an 'opsz' axis is like the type designer giving really good
|
||||
instructions about how the faces should look at various sizes. However, not all fonts have or
|
||||
will have a 'opsz' axis. Since we don't always have an 'opsz' what can be done to prevent small
|
||||
glyphs from looking all washed out?
|
||||
|
||||
One way the type designer could influence the optical size is through hinting. Any manual
|
||||
hinting is done by someone looking at the result at small sizes. Tweaks are made until it
|
||||
'looks good'. This often unconsciously bakes in optical sizing. Even autohinters eventually
|
||||
end up emulating this, generally making the text more contrasty and somewhat heavier at small
|
||||
pixel sizes (this depends on the target pixel size not the nominal requested size).
|
||||
|
||||
An alternate way the designer could provide optical sized fonts is using multiple font files
|
||||
for different optical sizes and expect the user to select the right one (like Display and
|
||||
Text variants). In theory the right one might also be selected automatically based on some
|
||||
criteria. Switching font files because of nominal size changes may seem drastic, but this is
|
||||
how the system font on macOS worked for a while.
|
||||
|
||||
One automatic way to do optical sizing is to artificially embolden the outline itself at small
|
||||
nominal sizes. This is the approach taken by CoreText. This is a lot like the dreaded fake-bold,
|
||||
but doesn't have the issue of the automatic fake bolding being heavier than ultra bold, since
|
||||
this emboldening is applied uniformally based on requested nominal size.
|
||||
|
||||
Another automatic way to do optical sizing is to over-cover all the edges when doing
|
||||
anti-aliasing. The pixels are a fixed physical size so affect small features more than large
|
||||
features. Skia currently has rudimentary support for this in its 'contrast hack' which is
|
||||
described more later.
|
||||
|
||||
As a note on optical sizing, this is one place where the nominal or optical size of the font is
|
||||
treated differently from the final pixel size of the font. A SkCanvas may have a scale transform
|
||||
active. Any hinting will be done at the final pixel size (the font size mapped through the
|
||||
current transformation matrix), but the optical size is not affected by the transform since
|
||||
it's actually a parameter for the SkTypeface (and maybe the SkFont).
|
||||
|
||||
|
||||
The Contrast Hack
|
||||
-----------------
|
||||
|
||||
Consider the example of [a pixel wide stroke at half-pixel
|
||||
boundaries](http://rastertragedy.com/RTRCh3.htm#Sec1). As stated in section 3.1.3 "if we were
|
||||
to choose the stroke positions to render the most faithful proportions and advance width, we
|
||||
may have to compromise the rendering contrast." If the stroke lands on a pixel boundary all
|
||||
is well. If the stoke lands at a half pixel boundary and correct linear blending is used then
|
||||
the same number of photons are reaching the eye, but the reduction in photons is spread out
|
||||
over twice the area. If the pixels were small enough then this wouldn't be an issue. Back of
|
||||
the envelope suggests an 8K 20inch desktop monitor or ~400ppi being the minimum. Note that RGB
|
||||
subpixel anti-aliasing brings a 100dpi display up to 300dpi, which is close, but still a bit
|
||||
short. Exploiting the RGB subpixels also doesn't really increase the resolution as much when
|
||||
getting close to the RGB primaries for the fill color.
|
||||
|
||||
One way to try to compensate for this is to cover some of these partially covered pixels more
|
||||
until it visually looks better. Note that this depends on a lot of factors like the pixel density
|
||||
of the display, the visual acuity of the user, the distance of the user from the display, and the
|
||||
user's sensitivity to the potential variations in stem darkness which may result. Automatically
|
||||
taking all these factors into account would be quite difficult. The correct function is generally
|
||||
determined by having the user look at the result of applying various amounts of extra coverage
|
||||
and having them to pick a setting that looks the least bad to them.
|
||||
|
||||
This specific form of over-covering is a form of drop out control for anti-aliasing and could
|
||||
be implemented in a similar way, detecting when a stem comes on in one pixel and goes out in
|
||||
the next and mark that for additional coverage. At raster time a cruder approximation could be
|
||||
made by doing a pass in each of the horizontal and vertical directions and finding runs of more
|
||||
than one non-fully-covered pixel and increasing their coverage.
|
||||
|
||||
If instead of doing these computationally expensive passes all coverage is boosted then in
|
||||
addition to the smeared stems the entire outside edge of the glyph will also be bolded. Making
|
||||
these outside edges heavier is a crude approximation of outsetting the initial path in a
|
||||
rather complicated way and amounts to an optical sizing tweak. Just as hinting can be used to
|
||||
approximate optical sizing if the user's perception of the pixel sizes is known in advance,
|
||||
this is a pixel level tweak tied to a specific user and display combination.
|
||||
|
||||
Much like the gamma hack can be modified to reduce the correction for light on dark to
|
||||
fight blooming, the contrast hack can be reduced when the color being drawn is known to be
|
||||
light. Generally the contrast correction goes to zero as one approaches white.
|
||||
|
29
site2/docs/dev/flutter/_index.md
Normal file
29
site2/docs/dev/flutter/_index.md
Normal file
@ -0,0 +1,29 @@
|
||||
|
||||
---
|
||||
title: "Skia in Flutter & Fuchsia"
|
||||
linkTitle: "Skia in Flutter & Fuchsia"
|
||||
|
||||
---
|
||||
|
||||
|
||||
Skia is used by both [Flutter](https://flutter.io/) and [Fuchsia](https://fuchsia.googlesource.com/docs/+/master/README.md).
|
||||
|
||||
Fuchsia has a [roller](https://fuchsia-roll.skia.org/) that will continuously roll latest Skia into that project. Fuchsia uses an XML [manifest](https://fuchsia.googlesource.com/manifest/+/master/userspace) to specify the Skia revision (as well as other third party libraries).
|
||||
|
||||
Flutter does not (yet) have a roller, so developers must manually perform rolls. Flutter uses [DEPS](https://github.com/flutter/engine/blob/master/DEPS) to specify third party dependencies.
|
||||
|
||||
|
||||
Although each project is (almost always) building at a different revision of Skia, Fuchsia itself always builds the latest revision of Flutter as one of its components. Thus, the versions of Skia being used by Flutter and Fuchsia must be "source compatible" -- Flutter must be capable of compiling against either revision without any change to Flutter itself.
|
||||
|
||||
Making API Changes
|
||||
------------------
|
||||
|
||||
If you need to make a breaking API change, the basic approach is:
|
||||
|
||||
* Add new code to Skia, leaving the old code in place.
|
||||
* Deprecate the old code path so that it must be enabled with a flag such as 'SK_SUPPORT_LEGACY_XXX'.
|
||||
* Add that same flag to [flutter\_defines.gni](https://skia.googlesource.com/skia/+/master/gn/flutter_defines.gni) in Skia.
|
||||
* Both Flutter and Fuchsia build Skia with a GN argument that enables all the defines listed in that file.
|
||||
* Land the Skia change, and test the new API in both Flutter and Fuchsia.
|
||||
* Remove the flag and code when the legacy code path is no longer in use.
|
||||
|
191
site2/docs/dev/gardening/_index.md
Normal file
191
site2/docs/dev/gardening/_index.md
Normal file
@ -0,0 +1,191 @@
|
||||
|
||||
---
|
||||
title: "Skia Gardener Documentation"
|
||||
linkTitle: "Skia Gardener Documentation"
|
||||
|
||||
weight: 8
|
||||
|
||||
---
|
||||
|
||||
|
||||
### Contents ###
|
||||
|
||||
* [What does a Skia Gardener do?](#what_is_a_skia_gardener)
|
||||
+ [Skia tree](#skia_tree)
|
||||
+ [AutoRollers](#autorollers)
|
||||
+ [Gold and Perf](#gold_and_perf)
|
||||
+ [Documentation](#skia_gardener_doc)
|
||||
* [View current and upcoming rotations](#view_current_upcoming_rotations)
|
||||
* [How to swap rotation shifts](#how_to_swap)
|
||||
* [Tips for Skia Gardeners](#tips)
|
||||
+ [When to file bugs](#when_to_file_bugs)
|
||||
+ [How to close or re-open the tree](#how_close_tree)
|
||||
+ [How to revert a CL](#how_to_revert)
|
||||
+ [What to do if DEPS roll fails to land](#deps_roll_failures)
|
||||
+ [How to rebaseline](#how_to_rebaseline)
|
||||
|
||||
|
||||
<a name="what_is_a_skia_gardener"></a>
|
||||
What does a Skia Gardener do?
|
||||
-----------------------------
|
||||
|
||||
A Skia Gardener keeps an eye on the tree, DEPS rolls, Gold tool, the Perf tool, and triages Chrome bugs.
|
||||
|
||||
Below is a brief summary of what the gardener does for each task:
|
||||
|
||||
<a name="skia_tree"></a>
|
||||
### Skia tree
|
||||
* Understand the [testing infrastructure](https://skia.org/dev/testing/automated_testing).
|
||||
* Start watching the [status page](https://status.skia.org) for bot breakages.
|
||||
* Track down people responsible for breakages and revert broken changes if there is no easy fix. You can use [blamer](#blamer) to help track down such changes.
|
||||
* Close and open the [tree](http://tree-status.skia.org).
|
||||
* Keep the builder comments on the [status page](https://status.skia.org) up to date.
|
||||
* File or follow up with [BreakingTheBuildbots bugs](https://bugs.chromium.org/p/skia/issues/list?q=label:BreakingTheBuildbots). See the tip on [when to file bugs](#when_to_file_bugs).
|
||||
* Read and update the [Ongoing Issues section](https://docs.google.com/document/d/1y2jUf4vXI0fwhu2TiCLVIfWC1JOxFcHXGw39y7i-y_I/edit#heading=h.tpualuc3p7z0) in the handoff doc.
|
||||
* (Optional) Document significant events that occurred during your shift in the [Weekly
|
||||
Handoff Notes section](https://docs.google.com/document/d/1y2jUf4vXI0fwhu2TiCLVIfWC1JOxFcHXGw39y7i-y_I/edit#heading=h.y49irwbutzr) in the handoff doc.
|
||||
|
||||
<a name="triage"></a>
|
||||
### Triage
|
||||
You should triage Chromium and Skia bugs that show up under "Untriaged Bugs" on the [status page](https://status.skia.org).
|
||||
The Android Gardener will triage the untriaged Android Bugs.
|
||||
For a more detailed view of bugs see [Skia Bugs Central](https://bugs-central.skia.org/).
|
||||
|
||||
<a name="blamer"></a>
|
||||
### Blamer
|
||||
If you have Go installed, a command-line tool is available to search through
|
||||
git history and do text searches on the full patch text and the commit
|
||||
message. To install blamer run:
|
||||
|
||||
go get go.skia.org/infra/blamer/go/blamer
|
||||
|
||||
Then run blamer from within a Skia checkout. For example, to search if the
|
||||
string "SkDevice" has appeared in the last 10 commits:
|
||||
|
||||
$ $GOPATH/bin/blamer --match SkDevice --num 10
|
||||
|
||||
commit ea70c4bb22394c8dcc29a369d3422a2b8f3b3e80
|
||||
Author: robertphillips <robertphillips@google.com>
|
||||
Date: Wed Jul 20 08:54:31 2016 -0700
|
||||
|
||||
Remove SkDevice::accessRenderTarget virtual
|
||||
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2167723002
|
||||
|
||||
Review-Url: https://codereview.chromium.org/2167723002
|
||||
|
||||
<a name="autorollers"></a>
|
||||
### Autorollers
|
||||
* Ensure that all AutoRollers listed on the [status page](https://status.skia.org) are successfully landing.
|
||||
|
||||
<a name="gold_and_perf"></a>
|
||||
### Gold and Perf
|
||||
* Pay attention for new [Perf](https://perf.skia.org/) and [Gold](https://gold.skia.org/) alerts (by clicking on the bell at the top right of the [status page](https://status.skia.org)).
|
||||
* The gardener's duty here is to make sure that when developers introduce new images or new perf regressions, that they are aware of what happened, and they use these tools to take appropriate action.
|
||||
|
||||
<a name="skia_gardener_doc"></a>
|
||||
### Documentation
|
||||
* Improve/update this documentation page for future gardeners, especially the [Tips section](#tips).
|
||||
|
||||
In general, gardeners should have a strong bias towards actions that keep the tree green and then open; if a simple revert can fix the problem, the gardener <b>should revert first and ask questions later</b>.
|
||||
|
||||
|
||||
<a name="view_current_upcoming_rotations"></a>
|
||||
View current and upcoming rotations
|
||||
-----------------------------------
|
||||
|
||||
The list of Skia Gardeners is specified [here](https://rotations.corp.google.com/rotation/4699606003744768).
|
||||
The gardeners widget on the [status page](https://status.skia.org) also displays the current gardeners.
|
||||
|
||||
|
||||
<a name="how_to_swap"></a>
|
||||
How to swap rotation shifts
|
||||
---------------------------
|
||||
|
||||
If you need to swap shifts with someone (because you are out sick or on vacation), please get approval from the person you want to swap with and directly make the swap via the [rotations page](https://rotations.corp.google.com/rotation/4699606003744768).
|
||||
|
||||
|
||||
<a name="tips"></a>
|
||||
Tips for Skia Gardeners
|
||||
-----------------
|
||||
|
||||
<a name="when_to_file_bugs"></a>
|
||||
### When to file bugs
|
||||
|
||||
Pay close attention to the "Failures" view in the [status page](https://status.skia.org).
|
||||
Look at all existing [BreakingTheBuildbots bugs](https://bug.skia.org/?q=label:BreakingTheBuildbots). If the list is kept up to date then it should accurately represent everything that is causing failures. If it does not, then please file/update bugs accordingly.
|
||||
|
||||
|
||||
<a name="how_close_tree"></a>
|
||||
### How to close or re-open the tree
|
||||
|
||||
1. Go to [tree-status.skia.org](https://tree-status.skia.org).
|
||||
2. Change the status.
|
||||
* To close the tree, include the word "closed" in the status.
|
||||
* To open the tree, include the word "open" in the status.
|
||||
* To caution the tree, include the word "caution" in the status.
|
||||
|
||||
|
||||
<a name="how_to_submit_when_tree_closed"></a>
|
||||
### How to submit when the tree is closed
|
||||
|
||||
* Submit manually using the "git cl land" with the --bypass-hooks flag.
|
||||
* Add "No-Tree-Checks: true" to your CL description and use the CQ as usual.
|
||||
|
||||
|
||||
<a name="how_to_revert"></a>
|
||||
### How to revert a CL
|
||||
|
||||
See the revert documentation [here](https://skia.org/dev/contrib/revert).
|
||||
|
||||
|
||||
<a name="deps_roll_failures"></a>
|
||||
### What to do if DEPS roll fails to land
|
||||
|
||||
A common cause of DEPS roll failures are layout tests. Find the offending Skia CL by examining the commit hash range in the DEPS roll and revert (or talk to the commit author if they are available). If you do revert then keep an eye on the next DEPS roll to make sure it succeeds.
|
||||
|
||||
If a Skia CL changes layout tests, but the new images look good, the tests need to be rebaselined. See [Rebaseline Layout Tests](#how_to_rebaseline).
|
||||
|
||||
<a name="how_to_rebaseline"></a>
|
||||
### Rebaseline Layout Tests (i.e., add suppressions)
|
||||
|
||||
* First create a Chromium bug:
|
||||
* goto [crbug.com](https://crbug.com)
|
||||
* Make sure you're logged in with your Chromium credentials
|
||||
* Click “New Issue”
|
||||
* Summary: “Skia image rebaseline”
|
||||
* Description:
|
||||
* DEPS roll #,
|
||||
* Helpful message about what went wrong (e.g., “Changes to how lighting is scaled in Skia r#### changed the following images:”)
|
||||
* Layout tests affected
|
||||
* You should copy the list of affected from stdio of the failing bot
|
||||
* Status: Assigned
|
||||
* Owner: yourself
|
||||
* cc: reed@, bsalomon@, robertphillips@ & developer responsible for changes
|
||||
* Labels: OS-All & Cr-Blink-LayoutTests
|
||||
* If it is filter related, cc senorblanco@
|
||||
|
||||
* (Dispreferred but faster) Edit [skia/skia_test_expectations.txt](https://chromium.googlesource.com/chromium/src/+/master/skia/skia_test_expectations.txt)
|
||||
* Add # comment about what has changed (I usually paraphrase the crbug text)
|
||||
* Add line(s) like the following after the comment:
|
||||
* crbug.com/<bug#youjustcreated> foo/bar/test-name.html [ ImageOnlyFailure ]
|
||||
* Note: this change is usually done in the DEPS roll patch itself
|
||||
|
||||
* (Preferred but slower) Make a separate Blink patch by editing LayoutTests/TestExpectations
|
||||
* Add # comment about what has changed (I usually paraphrase the crbug text)
|
||||
* Add line(s) like the following after the comment:
|
||||
* crbug.com/<bug#youjustcreated> foo/bar/test-name.html [ Skip ] # needs rebaseline
|
||||
* Commit the patch you created and wait until it lands and rolls into Chrome
|
||||
|
||||
* Retry the DEPS roll (for the 1st/dispreferred option this usually means just retrying the layout bots)
|
||||
* Make a Blink patch by editing LayoutTests/TestExpectations
|
||||
* Add # comment about what has changed
|
||||
* Add line(s) like the following after the comment:
|
||||
* crbug.com/<bug#youjustcreated> foo/bar/test-name.html [ Skip ] # needs rebaseline
|
||||
* (if you took the second option above you can just edit the existing line(s))
|
||||
|
||||
* If you took the first/dispreferred option above:
|
||||
* Wait for the Blink patch to roll into Chrome
|
||||
* Create a Chrome patch that removes your suppressions from skia/skia_test_expectations.txt
|
||||
|
||||
|
||||
|
70
site2/docs/dev/gardening/android.md
Normal file
70
site2/docs/dev/gardening/android.md
Normal file
@ -0,0 +1,70 @@
|
||||
|
||||
---
|
||||
title: "Android Gardener Documentation"
|
||||
linkTitle: "Android Gardener Documentation"
|
||||
|
||||
---
|
||||
|
||||
|
||||
### Contents ###
|
||||
|
||||
* [What does a Android Gardener do?](#what_is_a_android_gardener)
|
||||
* [Android Autorollers](#autoroller_doc)
|
||||
* [View current and upcoming rotations](#view_current_upcoming_rotations)
|
||||
* [How to swap rotation shifts](#how_to_swap)
|
||||
|
||||
|
||||
<a name="what_is_a_android_gardener"></a>
|
||||
What does a Android Gardener do?
|
||||
--------------------------------
|
||||
|
||||
The Android Gardener has two primary jobs:
|
||||
|
||||
1) Monitor and approve the semi-autonomous [git merges](https://googleplex-android-review.git.corp.google.com/#/q/owner:31977622648%2540project.gserviceaccount.com+status:open) from Skia's repository into the Android source tree. See autoroller documentation <a href="#autoroller_doc">here</a> for details on how to interact with it.
|
||||
|
||||
2) Stay on top of incoming Android-related bugs in both the [Skia](https://bugs.chromium.org/p/skia/issues/list?can=2&q=OpSys%3DAndroid&sort=-id&colspec=ID+Type+Status+Priority+Owner+Summary&cells=tiles) and [Android](https://buganizer.corp.google.com/issues?q=assignee:skia-android-triage%20status:open) bug trackers. For Skia bugs, this means triaging and assigning all Android bugs that are currently unassigned. For Android, this means following the [Android guidelines](go/android-buganizer) to verifying that all Skia bugs are TL-triaged (if not reach out to djsollen@).
|
||||
|
||||
The Android Gardener's job is NOT to address issues in Perf and Gold. You'll get your chance when you are the general Skia Gardener.
|
||||
|
||||
|
||||
<a name="autoroller_doc"></a>
|
||||
Android Autorollers
|
||||
-------------------
|
||||
|
||||
The Android autoroller into the master branch runs on [https://android-master-roll.skia.org](android-master-roll.skia.org) and is accessible only to Googlers.<br/>
|
||||
The autoroller's status is displayed on Skia's [status page](https://status.skia.org/).
|
||||
|
||||
You can send the autoroller into dry run mode via the UI. The uploaded change will not autosubmit when it is in dry run mode.
|
||||
|
||||
You can also stop the autoroller via the UI. This is useful in cases where a failure needs to be investigated and you do not want to waste TH resources by running unnecessary tests.
|
||||
|
||||
If the autoroller displays an error in the UI then look for more detail in it's [cloud logs](https://pantheon.corp.google.com/logs/viewer?project=google.com:skia-buildbots&resource=logging_log%2Fname%2Fandroid-master-autoroll&logName=projects%2Fgoogle.com:skia-buildbots%2Flogs%2Fautoroll).
|
||||
|
||||
If you need any more information about the autoroller please look at [skia:5538](https://bugs.chromium.org/p/skia/issues/detail?id=5538) or ask rmistry@ / skiabot@.
|
||||
|
||||
We also have autorollers into release branches (also restricted only to Googlers):
|
||||
|
||||
* [https://android-o-roll.skia.org](https://android-o-roll.skia.org) ([cloud logs](https://pantheon.corp.google.com/logs/viewer?project=google.com:skia-buildbots&resource=logging_log%2Fname%2Fandroid-o-autoroll&logName=projects%2Fgoogle.com:skia-buildbots%2Flogs%2Fautoroll)).
|
||||
|
||||
Changes created by these rollers need to be manually approved.<br/>
|
||||
The changes created by the release rollers:
|
||||
|
||||
* Include all authors of merged changes so that they can watch the roll.
|
||||
* Extracts all buganizer bugs of the form 'BUG=b/123' or 'Bug: b/456' and creates a single line in the merge change 'Bug: 123, 456'.
|
||||
* Collects all 'Test: ' lines and carries them over to the merge change.
|
||||
|
||||
|
||||
<a name="view_current_upcoming_rotations"></a>
|
||||
View current and upcoming rotations
|
||||
-----------------------------------
|
||||
|
||||
The list of Android Gardeners is specified [here](https://rotations.corp.google.com/rotation/5296436538245120).
|
||||
The gardeners widget on the [status page](https://status.skia.org) also displays the current gardeners.
|
||||
|
||||
|
||||
<a name="how_to_swap"></a>
|
||||
How to swap rotation shifts
|
||||
--------------------------
|
||||
|
||||
If you need to swap shifts with someone (because you are out sick or on vacation), please get approval from the person you want to swap with and directly make the swap via the [rotations page](https://rotations.corp.google.com/rotation/5296436538245120).
|
||||
|
65
site2/docs/dev/gardening/gpu.md
Normal file
65
site2/docs/dev/gardening/gpu.md
Normal file
@ -0,0 +1,65 @@
|
||||
|
||||
---
|
||||
title: "GPU Gardener Documentation"
|
||||
linkTitle: "GPU Gardener Documentation"
|
||||
|
||||
---
|
||||
|
||||
|
||||
### Contents ###
|
||||
|
||||
* [What does a GPU Gardener do?](#what_is_a_gpu_gardener)
|
||||
* [Tracking GPU Gardener Work](#tracking)
|
||||
* [View current and upcoming rotations](#view_current_upcoming_rotations)
|
||||
* [How to swap rotation shifts](#how_to_swap)
|
||||
* [Tips for GPU Gardeners](#tips)
|
||||
|
||||
|
||||
<a name="what_is_a_gpu_gardener"></a>
|
||||
What does a GPU Gardener do?
|
||||
----------------------------
|
||||
|
||||
The GPU Gardener has three main jobs:
|
||||
|
||||
1) Stay on top of incoming GPU-related bugs from clients in various bug trackers. This means triaging and assigning bugs that have a clear owner and investigating and possibly fixing bugs that don't.
|
||||
|
||||
|
||||
2) Improve the reliability of the GPU bots. This includes dealing with flaky images, crashing bots, etc. We have a never ending set of machine or driver specific issues to deal with. We often brush them under the rug so that we have time for the "real work." When you're gardener this is "real work."
|
||||
|
||||
|
||||
3) Improve our tooling. This includes writing new tools and improving existing test tools. Expected results are faster bot run times, more accurate testing, faster testing, surfacing new useful data, and improving debuggability.
|
||||
|
||||
|
||||
The GPU Gardener should always prioritize dealing with incoming bugs. The balance of a gardener's time should be spent divided as seen fit between 2) and 3). It is expected that as much as possible a gardener puts normal work on pause and focuses on gardener tasks for the full week. It is ok (and encouraged) to take a deep dive on one particular facet of the gardener duties and drive it as far as possible during gardener week (while staying on top of incoming bugs).
|
||||
|
||||
Note that the GPU Gardener's job is NOT to spend an abnormal amount of time triaging images, filing bugs for failing bots, or shepherding DEPS rolls. You'll get your chance when you are the general Skia Gardener.
|
||||
|
||||
<a name="tracking"></a>
|
||||
Tracking GPU Gardener Work
|
||||
--------------------------
|
||||
Outside of bug reports, a GPU Gardener should track their progress so that a future gardener can pick up any batons left shy of the finish line at week's end.
|
||||
|
||||
Also, whenever a gardener figures out how to accomplish a gardenly task (e.g. run a set of Chromium tests that aren't well documented or a cool OpenGL trick used to debug a gnarly issue) the tips section of this doc should be updated to assist future gardeners.
|
||||
|
||||
|
||||
<a name="view_current_upcoming_rotations"></a>
|
||||
View current and upcoming rotations
|
||||
-----------------------------------
|
||||
|
||||
The list of GPU Gardeners is specified [here](https://rotations.corp.google.com/rotation/6176639586140160).
|
||||
The gardeners widget on the [status page](https://status.skia.org) also displays the current gardeners.
|
||||
|
||||
|
||||
<a name="how_to_swap"></a>
|
||||
How to swap rotation shifts
|
||||
---------------------------
|
||||
|
||||
If you need to swap shifts with someone (because you are out sick or on vacation), please get approval from the person you want to swap with and directly make the swap via the [rotations page](https://rotations.corp.google.com/rotation/6176639586140160).
|
||||
|
||||
|
||||
<a name="tips"></a>
|
||||
Tips for GPU Gardeners
|
||||
----------------------
|
||||
|
||||
Please see [this](https://docs.google.com/a/google.com/document/d/1Q1A5T5js4MdqvD0EKjCgNbUBJfRBMPKR3OZAkc-2Tvc/edit?usp=sharing) doc.
|
||||
|
11
site2/docs/dev/gardening/infra.md
Normal file
11
site2/docs/dev/gardening/infra.md
Normal file
@ -0,0 +1,11 @@
|
||||
|
||||
---
|
||||
title: "Infra Gardener Documentation"
|
||||
linkTitle: "Infra Gardener Documentation"
|
||||
|
||||
---
|
||||
|
||||
|
||||
The Infra Gardener handles problems with Skia's build and test infrastructure.
|
||||
Documentation for Infra Gardeners is found at [go/skia-infra-gardener](http://go/skia-infra-gardener) (Googler's only).
|
||||
|
42
site2/docs/dev/internal/_index.md
Normal file
42
site2/docs/dev/internal/_index.md
Normal file
@ -0,0 +1,42 @@
|
||||
---
|
||||
title: 'Internal Links'
|
||||
linkTitle: 'Internal Links'
|
||||
---
|
||||
|
||||
Index of links to resources that are internal to the Google/Skia core team, for
|
||||
easy reference.
|
||||
|
||||
## Skia team information
|
||||
|
||||
- [Granting access to Skia](https://sites.google.com/a/google.com/skia/key-resources/access)
|
||||
- [Project communications](https://sites.google.com/a/google.com/skia/key-resources)
|
||||
|
||||
## Skia project information
|
||||
|
||||
- [Milestones and schedule](https://sites.google.com/a/google.com/skia/milestones)
|
||||
- [Status](https://sites.google.com/a/google.com/skia/status) from weekly,
|
||||
monthly updates and OKRs
|
||||
- [Skia branch process](https://docs.google.com/a/google.com/document/d/1Xn24lTMlmUgdP8bp-iHOeGKAOp8L5uCxg12lw49Jlpg/edit?usp=sharing)
|
||||
- [Design documents](https://sites.google.com/a/google.com/skia/design-documents)
|
||||
|
||||
## Infrastructure related
|
||||
|
||||
- [iOS tools](https://sites.google.com/a/google.com/skia/key-resources/ios-provisioning)
|
||||
for machine setup and provisioning
|
||||
- [SKP Playback](https://docs.google.com/a/google.com/document/d/1oJpuY8XKc212RsfUm6oEH2tp26Veb-Gez3clBuqapE4/edit?usp=sharing)
|
||||
for downloading buildbot SKPs
|
||||
|
||||
## Chrome related
|
||||
|
||||
- [Development on a Chrome branch](https://sites.google.com/a/google.com/skia/development-on-a-chrome-branch)
|
||||
- [Cherrypick instructions](https://sites.google.com/a/google.com/skia/development-on-a-chrome-branch#TOC-How-to-cherrypick-a-Skia-fix-into-a-Chrome-branch)
|
||||
|
||||
## Android related
|
||||
|
||||
- Skia on Android
|
||||
[development guide](https://sites.google.com/a/google.com/skia/android)
|
||||
|
||||
## Google3 related
|
||||
|
||||
- [Google3-Autoroller](https://sites.google.com/a/google.com/skia-infrastructure/docs/google3-autoroller)
|
||||
-- How to handle failures and how to test CLs.
|
12
site2/docs/dev/present/_index.md
Normal file
12
site2/docs/dev/present/_index.md
Normal file
@ -0,0 +1,12 @@
|
||||
|
||||
---
|
||||
title: "Presentations"
|
||||
linkTitle: "Presentations"
|
||||
|
||||
weight: 5
|
||||
|
||||
---
|
||||
|
||||
|
||||
Resources providing technical overview of various aspects of the Skia library
|
||||
|
33
site2/docs/dev/present/pathops.md
Normal file
33
site2/docs/dev/present/pathops.md
Normal file
@ -0,0 +1,33 @@
|
||||
|
||||
---
|
||||
title: "Path Ops"
|
||||
linkTitle: "Path Ops"
|
||||
|
||||
---
|
||||
|
||||
|
||||
View the PathOps presentations with speaker notes enabled for full content.
|
||||
|
||||
2013 Path Ops Presentation
|
||||
------------
|
||||
<iframe
|
||||
src="https://docs.google.com/presentation/d/1iEjbQV4o40hoooB9DiAHjH9P9Q5CrVUUnbYdQtQB6_A/embed?start=false&loop=false&delayms=3000"
|
||||
frameborder="0" width="480" height="299" allowfullscreen="true"
|
||||
mozallowfullscreen="true" webkitallowfullscreen="true"></iframe>
|
||||
|
||||
2014 Update
|
||||
------------
|
||||
<iframe
|
||||
src="https://docs.google.com/presentation/d/1NbmG5W6VW9h5HtjpCVLx4h6SXW0qW7HIwmSfiwzFbrI/embed?start=false&loop=false&delayms=3000"
|
||||
frameborder="0" width="480" height="299" allowfullscreen="true"
|
||||
mozallowfullscreen="true" webkitallowfullscreen="true"></iframe>
|
||||
|
||||
2015 Update
|
||||
------------
|
||||
<iframe
|
||||
src="https://docs.google.com/presentation/d/1PoZdIx4DqdIvs7ybv-L3EvtxQE2qXuzeOZpSkFJjfhg/embed?start=false&loop=false&delayms=3000"
|
||||
frameborder="0" width="480" height="299" allowfullscreen="true"
|
||||
mozallowfullscreen="true" webkitallowfullscreen="true"></iframe>
|
||||
|
||||
## [Path Ops Inverse Fill Illustration](https://drive.google.com/file/d/0BwoLUwz9PYkHLWpsaXd0UDdaN00/view?usp=sharing)
|
||||
|
23
site2/docs/dev/testing/_index.md
Normal file
23
site2/docs/dev/testing/_index.md
Normal file
@ -0,0 +1,23 @@
|
||||
|
||||
---
|
||||
title: "Testing"
|
||||
linkTitle: "Testing"
|
||||
|
||||
weight: 3
|
||||
|
||||
---
|
||||
|
||||
|
||||
Skia relies heavily on our suite of unit and GM tests, which are served by our
|
||||
DM test tool, for correctness testing. Tests are executed by our trybots, for
|
||||
every commit, across most of our supported platforms and configurations.
|
||||
Skia [Gold](https://gold.skia.org) is a web interface for triaging these results.
|
||||
|
||||
We also have a robust set of performance tests, served by the nanobench tool and
|
||||
accessible via the Skia [Perf](https://perf.skia.org) web interface.
|
||||
|
||||
Cluster Telemetry is a powerful framework that helps us capture and benchmark
|
||||
SKP files, a binary format for draw commands, across up to one million websites.
|
||||
|
||||
See the individual subpages for more details on our various test tools.
|
||||
|
193
site2/docs/dev/testing/automated_testing.md
Normal file
193
site2/docs/dev/testing/automated_testing.md
Normal file
@ -0,0 +1,193 @@
|
||||
|
||||
---
|
||||
title: "Skia Automated Testing"
|
||||
linkTitle: "Skia Automated Testing"
|
||||
|
||||
---
|
||||
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
Skia uses [Swarming](https://github.com/luci/luci-py/blob/master/appengine/swarming/doc/Design.md)
|
||||
to do the heavy lifting for our automated testing. It farms out tasks, which may
|
||||
consist of compiling code, running tests, or any number of other things, to our
|
||||
bots, which are virtual or real machines living in our local lab, Chrome Infra's
|
||||
lab, or in GCE.
|
||||
|
||||
The [Skia Task Scheduler](http://go/skia-task-scheduler) determines what tasks
|
||||
should run on what bots at what time. See the link for a detailed explanation of
|
||||
how relative task priorities are derived. A *task* corresponds to a single
|
||||
Swarming task. A *job* is composed of a directed acyclic graph of one or more
|
||||
*tasks*. The job is complete when all of its component tasks have succeeded
|
||||
or is considered a failure when any of its component tasks fails. The scheduler
|
||||
may automatically retry tasks within its set limits. Jobs are not retried.
|
||||
Multiple jobs may share the same task, for example, tests on two different
|
||||
Android devices which use the same compiled code.
|
||||
|
||||
Each Skia repository has an `infra/bots/tasks.json` file which defines the jobs
|
||||
and tasks for the repo. Most jobs will run at every commit, but it is possible
|
||||
to specify nightly and weekly jobs as well. For convenience, most repos also
|
||||
have a `gen_tasks.go` which will generate `tasks.json`. You will need to
|
||||
[install Go](https://golang.org/doc/install). From the repository root:
|
||||
|
||||
$ go run infra/bots/gen_tasks.go
|
||||
|
||||
It is necessary to run `gen_tasks.go` every time it is changed or every time an
|
||||
[asset](https://skia.googlesource.com/skia/+/master/infra/bots/assets/README.md)
|
||||
has changed. There is also a test mode which simply verifies that the `tasks.json`
|
||||
file is up to date:
|
||||
|
||||
$ go run infra/bots/gen_tasks.go --test
|
||||
|
||||
|
||||
|
||||
Try Jobs
|
||||
--------
|
||||
|
||||
Skia's trybots allow testing and verification of changes before they land in the
|
||||
repo. You need to have permission to trigger try jobs; if you need permission,
|
||||
ask a committer. After uploading your CL to [Gerrit](https://skia-review.googlesource.com/),
|
||||
you may trigger a try job for any job listed in `tasks.json`, either via the
|
||||
Gerrit UI, using `git cl try`, eg.
|
||||
|
||||
git cl try -B skia.primary -b Some-Tryjob-Name
|
||||
|
||||
or using `bin/try`, a small wrapper for `git cl try` which helps to choose try jobs.
|
||||
From a Skia checkout:
|
||||
|
||||
bin/try --list
|
||||
|
||||
You can also search using regular expressions:
|
||||
|
||||
bin/try "Test.*GTX660.*Release"
|
||||
|
||||
|
||||
Status View
|
||||
------------
|
||||
|
||||
The status view shows a table with tasks, grouped by test type and platform,
|
||||
on the X-axis and commits on the Y-axis. The cells are colored according to
|
||||
the status of the task for each commit:
|
||||
|
||||
* green: success
|
||||
* orange: failure
|
||||
* purple: mishap (infrastructure issue)
|
||||
* black border, no fill: task in progress
|
||||
* blank: no task has started yet for a given revision
|
||||
|
||||
Commits are listed by author, and the branch on which the commit was made is
|
||||
shown on the very left. A purple result will override an orange result.
|
||||
|
||||
For more detail, you can click on an individual cell to get a summary of the
|
||||
task. You can also click one of the white bars at the top of each column to see
|
||||
a summary of recent tasks with the same name.
|
||||
|
||||
The status page has several filters which can be used to show only a subset of
|
||||
task specs:
|
||||
|
||||
* Interesting: Task specs which have both successes and failures within the
|
||||
visible commit window.
|
||||
* Failures: Task specs which have failures within the visible commit window.
|
||||
* Comments: Task specs which have comments.
|
||||
* Failing w/o comment: task specs which have failures within the visible commit
|
||||
window but have no comments.
|
||||
* All: Display all tasks.
|
||||
* Search: Enter a search string. Substrings and regular expressions may be
|
||||
used, per the Javascript String Match() rules:
|
||||
http://www.w3schools.com/jsref/jsref_match.asp
|
||||
|
||||
<a name="adding-new-jobs"></a>
|
||||
Adding new jobs
|
||||
---------------
|
||||
|
||||
If you would like to add jobs to build or test new configurations, please file a
|
||||
[New Bot Request][new bot request].
|
||||
|
||||
If you know that the new jobs will need new hardware or you aren't sure which
|
||||
existing bots should run the new jobs, assign to jcgregorio. Once the Infra team
|
||||
has allocated the hardware, we will assign back to you to complete the process.
|
||||
|
||||
Generally it's possible to copy an existing job and make changes to accomplish
|
||||
what you want. You will need to add the new job to
|
||||
[infra/bots/jobs.json][jobs json]. In some cases, you will need to make changes
|
||||
to recipes:
|
||||
|
||||
* If there are new GN flags or compiler options:
|
||||
[infra/bots/recipe_modules/build][build recipe module], probably default.py.
|
||||
* If there are modifications to dm flags: [infra/bots/recipes/test.py][test py]
|
||||
* If there are modifications to nanobench flags:
|
||||
[infra/bots/recipes/perf.py][perf py]
|
||||
|
||||
After modifying any of the above files, run `make train` in the infra/bots
|
||||
directory to update generated files. Upload the CL, then run `git cl try -B
|
||||
skia.primary -b <job name>` to run the new job. (After commit, the new job will
|
||||
appear in the PolyGerrit UI after the next successful run of the
|
||||
Housekeeper-Nightly-UpdateMetaConfig task.)
|
||||
|
||||
[new bot request]:
|
||||
https://bugs.chromium.org/p/skia/issues/entry?template=New+Bot+Request
|
||||
[jobs json]: https://skia.googlesource.com/skia/+/master/infra/bots/jobs.json
|
||||
[build recipe module]:
|
||||
https://skia.googlesource.com/skia/+/refs/heads/master/infra/bots/recipe_modules/build/
|
||||
[test py]:
|
||||
https://skia.googlesource.com/skia/+/master/infra/bots/recipes/test.py
|
||||
[perf py]:
|
||||
https://skia.googlesource.com/skia/+/master/infra/bots/recipes/perf.py
|
||||
|
||||
|
||||
Detail on Skia Tasks
|
||||
--------------------
|
||||
|
||||
[infra/bots/gen_tasks.go][gen_tasks] reads config files:
|
||||
|
||||
* [infra/bots/jobs.json][jobs json]
|
||||
* [infra/bots/cfg.json][cfg json]
|
||||
* [infra/bots/recipe_modules/builder_name_schema/builder_name_schema.json][builder_name_schema]
|
||||
|
||||
Based on each job name in jobs.json, gen_tasks decides which tasks to generate (process
|
||||
function). Various helper functions return task name of the direct dependencies of the job.
|
||||
|
||||
In gen_tasks, tasks are specified with a TaskSpec. A TaskSpec specifies how to generate and trigger
|
||||
a Swarming task.
|
||||
|
||||
Most Skia tasks run a recipe with Kitchen. The arguments to the kitchenTask function specify the
|
||||
most common parameters for a TaskSpec that will run a recipe. More info on recipes at
|
||||
[infra/bots/recipes/README.md][recipes README] and
|
||||
[infra/bots/recipe_modules/README.md][recipe_modules README].
|
||||
|
||||
The Swarming task is generated based on several parameters of the TaskSpec:
|
||||
|
||||
* Isolate: specifies the isolate file. The isolate file specifies the files from the repo to place
|
||||
on the bot before running the task. (For non-Kitchen tasks, the isolate also specifies the command
|
||||
to run.) [More info][isolate user guide].
|
||||
* Command: the command to run, if not specified in the Isolate. (Generally this is a boilerplate
|
||||
Kitchen command that runs a recipe; see below.)
|
||||
* CipdPackages: specifies the IDs of CIPD packages that will be placed on the bot before running the
|
||||
task. See infra/bots/assets/README.md for more info.
|
||||
* Dependencies: specifies the names of other tasks that this task depends upon. The outputs of those
|
||||
tasks will be placed on the bot before running this task.
|
||||
* Dimensions: specifies what kind of bot should run this task. Ask Infra team for how to set this.
|
||||
* ExecutionTimeout: total time the task is allowed to run before it is killed.
|
||||
* IoTimeout: amount of time the task can run without printing something to stdout/stderr before it
|
||||
is killed.
|
||||
* Expiration: Mostly ignored. If the task happens to be scheduled when there are no bots that can
|
||||
run it, it will remain pending for this long before being canceled.
|
||||
|
||||
If you need to do something more complicated, or if you are not sure how to add
|
||||
and configure the new jobs, please ask for help from borenet, benjaminwagner, or
|
||||
mtklein.
|
||||
|
||||
[gen_tasks]:
|
||||
https://skia.googlesource.com/skia/+/master/infra/bots/gen_tasks.go
|
||||
[cfg json]:
|
||||
https://skia.googlesource.com/skia/+/master/infra/bots/cfg.json
|
||||
[builder_name_schema]:
|
||||
https://skia.googlesource.com/skia/+/master/infra/bots/recipe_modules/builder_name_schema/builder_name_schema.json
|
||||
[recipes README]:
|
||||
https://skia.googlesource.com/skia/+/master/infra/bots/recipes/README.md
|
||||
[recipe_modules README]:
|
||||
https://skia.googlesource.com/skia/+/master/infra/bots/recipe_modules/README.md
|
||||
[isolate user guide]:
|
||||
https://chromium.googlesource.com/infra/luci/luci-py/+/master/appengine/isolate/doc/client/Isolate-User-Guide.md
|
||||
|
38
site2/docs/dev/testing/download.md
Normal file
38
site2/docs/dev/testing/download.md
Normal file
@ -0,0 +1,38 @@
|
||||
|
||||
---
|
||||
title: "Downloading Isolates"
|
||||
linkTitle: "Downloading Isolates"
|
||||
|
||||
---
|
||||
|
||||
|
||||
The intermediate and final build products from running tests are all stored in
|
||||
[Isolate](https://github.com/luci/luci-py/blob/master/appengine/isolate/doc/Design.md),
|
||||
and can be downloaded to the desktop for inspection and debugging.
|
||||
|
||||
First install the client:
|
||||
|
||||
git clone https://github.com/luci/client-py.git
|
||||
|
||||
Add the checkout location to your $PATH.
|
||||
|
||||
To download the isolated files for a test first visit
|
||||
the build status page and find the "isolated output" link:
|
||||
|
||||
<img src="Status.png" style="margin-left:30px" width=576 height=271 >
|
||||
|
||||
|
||||
Follow that link to find the hash of the isolated outputs:
|
||||
|
||||
|
||||
<img src="Isolate.png" style="margin-left:30px" width=451 height=301 >
|
||||
|
||||
Then run `isolateserver.py` with --isolated set to that hash:
|
||||
|
||||
$ isolateserver.py \
|
||||
download \
|
||||
--isolate-server=https://isolateserver.appspot.com \
|
||||
--isolated=5b85b7c382ee2a34530e33c7db20a07515ff9481 \
|
||||
--target=./download/
|
||||
|
||||
|
40
site2/docs/dev/testing/fonts.md
Normal file
40
site2/docs/dev/testing/fonts.md
Normal file
@ -0,0 +1,40 @@
|
||||
|
||||
---
|
||||
title: "Fonts and GM Tests"
|
||||
linkTitle: "Fonts and GM Tests"
|
||||
|
||||
---
|
||||
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
Each test in the gm directory draws a reference image. Their primary purpose is
|
||||
to detect when images change unexpectedly, indicating that a rendering bug has
|
||||
been introduced.
|
||||
|
||||
The gm tests have a secondary purpose: they detect when rendering is different
|
||||
across platforms and configurations.
|
||||
|
||||
GM font selection
|
||||
-----------------
|
||||
|
||||
Each gm specifies the typeface to use when drawing text. For now, to set the
|
||||
portable typeface on the paint, call:
|
||||
|
||||
~~~~
|
||||
ToolUtils::set_portable_typeface(SkPaint* , const char* name = nullptr,
|
||||
SkFontStyle style = SkFontStyle());
|
||||
~~~~
|
||||
|
||||
To create a portable typeface, use:
|
||||
|
||||
~~~~
|
||||
SkTypeface* typeface = ToolUtils::create_portable_typeface(const char* name,
|
||||
SkFontStyle style);
|
||||
~~~~
|
||||
|
||||
Eventually, both `set_portable_typeface()` and `create_portable_typeface()` will be
|
||||
removed. Instead, a test-wide `SkFontMgr` will be selected to choose portable
|
||||
fonts or resource fonts.
|
||||
|
93
site2/docs/dev/testing/fuzz.md
Normal file
93
site2/docs/dev/testing/fuzz.md
Normal file
@ -0,0 +1,93 @@
|
||||
|
||||
---
|
||||
title: "Fuzzing"
|
||||
linkTitle: "Fuzzing"
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reproducing using `fuzz`
|
||||
------------------------
|
||||
|
||||
We assume that you can [build Skia](/user/build). Many fuzzes only reproduce
|
||||
when building with ASAN or MSAN; see [those instructions for more details](./xsan).
|
||||
|
||||
When building, you should add the following args to BUILD.gn to make reproducing
|
||||
less machine- and platform- dependent:
|
||||
|
||||
skia_use_fontconfig=false
|
||||
skia_use_freetype=true
|
||||
skia_use_system_freetype2=false
|
||||
skia_use_wuffs=true
|
||||
skia_enable_skottie=true
|
||||
skia_enable_fontmgr_custom_directory=false
|
||||
skia_enable_fontmgr_custom_embedded=false
|
||||
skia_enable_fontmgr_custom_empty=true
|
||||
|
||||
All that is needed to reproduce a fuzz downloaded from ClusterFuzz or oss-fuzz is to
|
||||
run something like:
|
||||
|
||||
out/ASAN/fuzz -b /path/to/downloaded/testcase
|
||||
|
||||
The fuzz binary will try its best to guess what the type/name should be based on
|
||||
the name of the testcase. Manually providing type and name is also supported, like:
|
||||
|
||||
out/ASAN/fuzz -t filter_fuzz -b /path/to/downloaded/testcase
|
||||
out/ASAN/fuzz -t api -n RasterN32Canvas -b /path/to/downloaded/testcase
|
||||
|
||||
To enumerate all supported types and names, run the following:
|
||||
|
||||
out/ASAN/fuzz --help # will list all types
|
||||
out/ASAN/fuzz -t api # will list all names
|
||||
|
||||
If the crash does not show up, try to add the flag --loops:
|
||||
|
||||
out/ASAN/fuzz -b /path/to/downloaded/testcase --loops <times-to-run>
|
||||
|
||||
Writing fuzzers with libfuzzer
|
||||
------------------------------
|
||||
|
||||
libfuzzer is an easy way to write new fuzzers, and how we run them on oss-fuzz.
|
||||
Your fuzzer entry point should implement this API:
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t*, size_t);
|
||||
|
||||
First install Clang and libfuzzer, e.g.
|
||||
|
||||
sudo apt install clang-10 libc++-10-dev libfuzzer-10-dev
|
||||
|
||||
You should now be able to use `-fsanitize=fuzzer` with Clang.
|
||||
|
||||
Set up GN args to use libfuzzer:
|
||||
|
||||
cc = "clang-10"
|
||||
cxx = "clang++-10"
|
||||
sanitize = "fuzzer"
|
||||
extra_cflags = [ "-O1" ] # Or whatever you want.
|
||||
...
|
||||
|
||||
Build Skia and your fuzzer entry point:
|
||||
|
||||
ninja -C out/libfuzzer skia
|
||||
clang++-10 -I. -O1 -fsanitize=fuzzer fuzz/oss_fuzz/whatever.cpp out/libfuzzer/libskia.a
|
||||
|
||||
Run your new fuzzer binary
|
||||
|
||||
./a.out
|
||||
|
||||
|
||||
Fuzzing Defines
|
||||
---------------
|
||||
There are some defines that can help guide a fuzzer to be more productive (e.g. avoid OOMs, avoid
|
||||
unnecessarily slow code).
|
||||
|
||||
// Required for fuzzing with afl-fuzz to prevent OOMs from adding noise.
|
||||
SK_BUILD_FOR_AFL_FUZZ
|
||||
|
||||
// Required for fuzzing with libfuzzer
|
||||
SK_BUILD_FOR_LIBFUZZER
|
||||
|
||||
// This define adds in guards to abort when we think some code path will take a long time or
|
||||
// use a lot of RAM. It is set by default when either of the above defines are set.
|
||||
SK_BUILD_FOR_FUZZER
|
||||
|
48
site2/docs/dev/testing/ios.md
Normal file
48
site2/docs/dev/testing/ios.md
Normal file
@ -0,0 +1,48 @@
|
||||
|
||||
---
|
||||
title: "Testing on iOS"
|
||||
linkTitle: "Testing on iOS"
|
||||
|
||||
---
|
||||
|
||||
Before setting Skia up for automated testing from the command line, please
|
||||
follow the instructions to run Skia tests (*dm*, *nano-bench*) with the
|
||||
mainstream iOS tool chain. See the [quick start guide for ios](../../user/quick/ios).
|
||||
|
||||
iOS doesn't lend itself well to compiling and running from the command line.
|
||||
Below are instructions on how to install a set of tools that make this possible.
|
||||
To see how they are used in automated testing please see the bash scripts
|
||||
used by the buildbot recipes: <https://github.com/google/skia/tree/master/platform_tools/ios/bin>.
|
||||
|
||||
Installation
|
||||
------------
|
||||
The key tools are
|
||||
|
||||
* libimobiledevice <http://www.libimobiledevice.org/>, <https://github.com/libimobiledevice/libimobiledevice>
|
||||
|
||||
* ios-deploy <https://github.com/phonegap/ios-deploy>
|
||||
|
||||
Follow these steps to install them:
|
||||
|
||||
* Install Brew at <http://brew.sh/>
|
||||
* Install *libimobiledevice*
|
||||
(Note: All these are part of the *libimobiledevice* project but packaged/developed
|
||||
under different names. The *cask* extension to *brew* is necessary to install
|
||||
*osxfuse* and *ifuse*, which allows to mount the application directory on an iOS device).
|
||||
|
||||
```
|
||||
brew install libimobiledevice
|
||||
brew install ideviceinstaller
|
||||
brew install caskroom/cask/brew-cask
|
||||
brew install Caskroom/cask/osxfuse
|
||||
brew install ifuse
|
||||
```
|
||||
|
||||
* Install node.js and ios-deploy
|
||||
|
||||
```
|
||||
$ brew update
|
||||
$ brew install node
|
||||
$ npm install ios-deploy
|
||||
```
|
||||
|
185
site2/docs/dev/testing/skiagold.md
Normal file
185
site2/docs/dev/testing/skiagold.md
Normal file
@ -0,0 +1,185 @@
|
||||
---
|
||||
title: 'Skia Gold'
|
||||
linkTitle: 'Skia Gold'
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Gold is a web application that compares the images produced by our bots against
|
||||
known baseline images.
|
||||
|
||||
Key features:
|
||||
|
||||
- Baselines are managed in Gold outside of Git, but in lockstep with Git
|
||||
commits.
|
||||
- Each commit creates >500k images.
|
||||
- Deviations from the baseline are triaged after a CL lands and images are
|
||||
triaged as either `positive` or `negative`. 'Positive' means the diff is
|
||||
considered acceptable. 'Negative' means the diff is considered unacceptable
|
||||
and requires a fix. If a CL causes Skia to break it is reverted or an
|
||||
additional CL is landed to fix the problem.
|
||||
- We test across a range of dimensions, e.g.:
|
||||
|
||||
- OS (Windows, Linux, Mac, Android, iOS)
|
||||
- Architectures (Intel, ARM)
|
||||
- Backends (CPU, OpenGL, Vulkan etc.)
|
||||
- etc.
|
||||
|
||||
- Written in Go, Polymer and deployed on the Google Cloud. The code is in the
|
||||
[Skia Infra Repository](https://github.com/google/skia-buildbot).
|
||||
|
||||
## Recommended Workflows
|
||||
|
||||
### How to best use Gold for commonly faced problems
|
||||
|
||||
These instructions will refer to various views which are accessible via the left
|
||||
navigation on [gold.skia.org](https://gold.skia.org/).
|
||||
|
||||
View access is public, triage access is granted to Skia contributors. You must
|
||||
be logged in to triage.
|
||||
|
||||
## Problem #1: As Skia Gardener, I need to triage and “assign” many incoming new images.
|
||||
|
||||
Solution today:
|
||||
|
||||
- Access the By Blame view to see digests needing triage and associated
|
||||
owners/CLs
|
||||
- Only untriaged digests will be shown by default
|
||||
- Blame is not sorted in any particular order
|
||||
- Digests are clustered by runs and the most minimal set of blame
|
||||
|
||||
<img src=BlameView.png style="margin-left:30px" align="left" width="800"/>
|
||||
<br clear="left">
|
||||
|
||||
- Select digests for triage
|
||||
- Digests will be listed in order with largest difference first
|
||||
- Click to open the digest view with detailed information
|
||||
|
||||
<img src=Digests.png style="margin-left:40px" align="left" width="780"/>
|
||||
<br clear="left">
|
||||
|
||||
- Open bugs for identified owner(s)
|
||||
- The digest detail view has a link to open a bug from the UI
|
||||
- Via the Gold UI or when manually entering a bug, copy the full URL of single
|
||||
digest into a bug report
|
||||
- The URL reference to the digest in Issue Tracker will link the bug to the
|
||||
digest in Gold
|
||||
|
||||
<img src="IssueHighlight.png" style="margin-left:60px" align="left" width="720" border=1/>
|
||||
<br clear="left">
|
||||
|
||||
<br>
|
||||
|
||||
Future improvements:
|
||||
|
||||
- Smarter, more granular blamelist
|
||||
|
||||
<br>
|
||||
|
||||
## Problem #2: As a developer, I need to land a CL that may change many images.
|
||||
|
||||
To find your results:
|
||||
|
||||
- Immediately following commit, access the By Blame view to find untriaged
|
||||
digest groupings associated with your ID
|
||||
- Click on one of the clusters including your CL to triage
|
||||
- Return to the By Blame view to walk through all untriaged digests involving
|
||||
your change
|
||||
- Note: It is not yet implemented in the UI but possible to filter the view by
|
||||
CL. Delete hashes in the URL to only include the hash for your CL.
|
||||
|
||||
<img src=BlameView.png style="margin-left:30px" align="left" width="800"/>
|
||||
<br clear="left">
|
||||
|
||||
To rebaseline images:
|
||||
|
||||
- Access the Ignores view and create a new, short-interval (hours) ignore for
|
||||
the most affected configuration(s)
|
||||
|
||||
<img src=Ignores.png style="margin-left:30px" align="left" width="800"/>
|
||||
<br clear="left">
|
||||
|
||||
- Click on the Ignore to bring up a search view filtered by the affected
|
||||
configuration(s)
|
||||
- Mark untriaged images as positive (or negative if appropriate)
|
||||
- Follow one of two options for handling former positives:
|
||||
- Leave former positives as-is and let them fall off with time if there is low
|
||||
risk of recurrence
|
||||
- Mark former positives as negative if needed to verify the change moving
|
||||
forward
|
||||
|
||||
Future improvements:
|
||||
|
||||
- Trybot support prior to commit, with view limited to your CL
|
||||
- Pre-triage prior to commit that will persist when the CL lands
|
||||
|
||||
<br>
|
||||
|
||||
## Problem #3: As a developer or infrastructure engineer, I need to add a new or updated config.
|
||||
|
||||
(ie: new bot, test mode, environment change)
|
||||
|
||||
Solution today:
|
||||
|
||||
- Follow the process for rebaselining images:
|
||||
- Wait for the bot/test/config to be committed and show up in the Gold UI
|
||||
- Access the Ignores view and create a short-interval ignore for the
|
||||
configuration(s)
|
||||
- Triage the ignores for that config to identify positive images
|
||||
- Delete the ignore
|
||||
|
||||
Future improvements:
|
||||
|
||||
- Introduction of a new or updated test can make use of try jobs and pre-triage.
|
||||
- New configs may be able to use these features as well.
|
||||
|
||||
<br>
|
||||
|
||||
## Problem #4: As a developer, I need to analyze the details of a particular image digest.
|
||||
|
||||
Solution:
|
||||
|
||||
- Access the By Test view
|
||||
|
||||
<img src=ByTest.png style="margin-left:30px" align="left" width="800"/>
|
||||
<br clear="left">
|
||||
|
||||
- Click the magnifier to filter by configuration
|
||||
- Access the Cluster view to see the distribution of digest results
|
||||
- Use control-click to select and do a direct compare between data points
|
||||
- Click on configurations under “parameters” to highlight data points and
|
||||
compare
|
||||
|
||||
<img src=ClusterConfig.png style="margin-left:30px" align="left" width="800"/>
|
||||
<br clear="left">
|
||||
|
||||
- Access the Grid view to see NxN diffs
|
||||
|
||||
<img src=Grid.png style="margin-left:30px" align="left" width="800"/>
|
||||
<br clear="left">
|
||||
|
||||
- Access the Dot diagram to see history of commits for the trace
|
||||
- Each dot represents a commit
|
||||
- Each line represents a configuration
|
||||
- Dot colors distinguish between digests
|
||||
|
||||
<img src=DotDiagram.png style="margin-left:30px" align="left" width="800"/>
|
||||
<br clear="left">
|
||||
|
||||
<br>
|
||||
|
||||
Future improvements:
|
||||
|
||||
- Large diff display of image vs image
|
||||
|
||||
<br>
|
||||
|
||||
## Problem #5: As a developer, I need to find results for a particular configuration.
|
||||
|
||||
Solution:
|
||||
|
||||
- Access the Search view
|
||||
- Select any parameters desired to search across tests
|
||||
|
||||
<img src=Search.png style="margin-left:30px" align="left" width="800"/>
|
||||
<br clear="left">
|
45
site2/docs/dev/testing/skiaperf.md
Normal file
45
site2/docs/dev/testing/skiaperf.md
Normal file
@ -0,0 +1,45 @@
|
||||
|
||||
---
|
||||
title: "Skia Perf"
|
||||
linkTitle: "Skia Perf"
|
||||
|
||||
---
|
||||
|
||||
|
||||
[Skia Perf](https://perf.skia.org) is a web application for analyzing and
|
||||
viewing performance metrics produced by Skia's testing infrastructure.
|
||||
|
||||
<img src=Perf.png style="margin-left:30px" align="left" width="800"/> <br clear="left">
|
||||
|
||||
Skia tests across a large number of platforms and configurations, and each
|
||||
commit to Skia generates more than 400,000 individual values that are sent to
|
||||
Perf, consisting mostly of performance benchmark results, but also including
|
||||
memory and coverage data.
|
||||
|
||||
Perf offers clustering, which is a tool to pick out trends and patterns in large sets of traces.
|
||||
|
||||
<img src=Cluster.png style="margin-left:30px" align="left" width="400"/> <br clear="left">
|
||||
|
||||
And can generate alerts when those trends spot a regression:
|
||||
|
||||
<img src=Regression.png style="margin-left:30px" align="left" width="800"/> <br clear="left">
|
||||
|
||||
|
||||
## Calculations
|
||||
|
||||
Skia Perf has the ability to perform calculations over the test data
|
||||
allowing you to build up interesting queries.
|
||||
|
||||
This query displays the ratio of playback time in ms to the number of ops for desk\_wowwiki.skp:
|
||||
|
||||
ratio(
|
||||
ave(fill(filter("name=desk_wowwiki.skp&sub_result=min_ms"))),
|
||||
ave(fill(filter("name=desk_wowwiki.skp&sub_result=ops")))
|
||||
)
|
||||
|
||||
You can also use the data to answer questions like how many tests were run per commit.
|
||||
|
||||
count(filter(""))
|
||||
|
||||
See Skia Perf for the [full list of functions available](https://perf.skia.org/help/).
|
||||
|
56
site2/docs/dev/testing/skqp.md
Normal file
56
site2/docs/dev/testing/skqp.md
Normal file
@ -0,0 +1,56 @@
|
||||
|
||||
---
|
||||
title: "SkQP"
|
||||
linkTitle: "SkQP"
|
||||
|
||||
---
|
||||
|
||||
|
||||
Development APKs of SkQP are kept in Google storage. Each file in named
|
||||
with a abbreviated Git hash that points at the commit in the Skia repository it
|
||||
was built with.
|
||||
|
||||
These are universal APKs that contain native libraries for armeabi-v7a,
|
||||
arm64-v8a, x86, and x86\_64 architectures. The most recent is listed first.
|
||||
|
||||
The listing can be found here:
|
||||
[https://storage.googleapis.com/skia-skqp/apklist](https://storage.googleapis.com/skia-skqp/apklist)
|
||||
|
||||
If you are looking at Android CTS failures, use the most recent commit on the
|
||||
`origin/skqp/release` branch.
|
||||
|
||||
To run tests:
|
||||
|
||||
adb install -r skqp-universal-{APK_SHA_HERE}.apk
|
||||
adb logcat -c
|
||||
adb shell am instrument -w org.skia.skqp
|
||||
|
||||
Monitor the output with:
|
||||
|
||||
adb logcat TestRunner org.skia.skqp skia DEBUG "*:S"
|
||||
|
||||
Note the test's output path on the device. It will look something like this:
|
||||
|
||||
01-23 15:22:12.688 27158 27173 I org.skia.skqp:
|
||||
output written to "/storage/emulated/0/Android/data/org.skia.skqp/files/skqp_report_2019-02-28T102058"
|
||||
|
||||
Retrieve and view the report with:
|
||||
|
||||
OUTPUT_LOCATION="/storage/emulated/0/Android/data/org.skia.skqp/files/skqp_report_2019-02-28T102058"
|
||||
adb pull "$OUTPUT_LOCATION" /tmp/
|
||||
|
||||
(Your value of `$OUTPUT_LOCATION` will differ from mine.
|
||||
|
||||
Open the file `/tmp/output/skqp_report_2019-02-28T102058/report.html` .
|
||||
|
||||
**Zip up that directory to attach to a bug report:**
|
||||
|
||||
cd /tmp
|
||||
zip -r skqp_report_2019-02-28T102058.zip skqp_report_2019-02-28T102058
|
||||
ls -l skqp_report_2019-02-28T102058.zip
|
||||
|
||||
* * *
|
||||
|
||||
For more information about building your own APK, refer to
|
||||
https://skia.googlesource.com/skia/+/master/tools/skqp/README.md
|
||||
|
94
site2/docs/dev/testing/swarmingbots.md
Normal file
94
site2/docs/dev/testing/swarmingbots.md
Normal file
@ -0,0 +1,94 @@
|
||||
|
||||
---
|
||||
title: "Skia Swarming Bots"
|
||||
linkTitle: "Skia Swarming Bots"
|
||||
|
||||
---
|
||||
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
Skia's Swarming bots are hosted in three places:
|
||||
|
||||
* Google Compute Engine. This is the preferred location for bots which don't need to run on physical
|
||||
hardware, ie. anything that doesn't require a GPU or a specific hardware configuration. Most of
|
||||
our compile bots live here, along with some non-GPU test bots on Linux and Windows. We get
|
||||
surprisingly stable performance numbers from GCE, despite very few guarantees about the physical
|
||||
hardware.
|
||||
* Chrome Golo. This is the preferred location for bots which require specific hardware or OS
|
||||
configurations that are not supported by GCE. We have several Mac, Linux, and Windows bots in the
|
||||
Golo.
|
||||
* The Skolo (local Skia lab in Chapel Hill). Anything we can't get in GCE or the Golo lives
|
||||
here. This includes a wider variety of GPUs and all Android, ChromeOS, iOS, and other devices.
|
||||
|
||||
[go/skbl](https://goto.google.com/skbl) lists all Skia Swarming bots.
|
||||
|
||||
|
||||
<a name="connecting-to-swarming-bots"></a>
|
||||
Connecting to Swarming Bots
|
||||
---------------------------
|
||||
|
||||
If you need to make changes on a bot/device, please check with the Infra Gardener or another Infra team member. Most
|
||||
bots/devices can be flashed/imaged back to a clean state, but others can not.
|
||||
|
||||
- Machine name like “skia-e-gce-NNN”, “skia-ct-gce-NNN”, “skia-i-gce-NNN”, “ct-gce-NNN”, “ct-xxx-builder-NNN” -> GCE
|
||||
* First determine the project for the bot:
|
||||
+ skia-e-gce-NNN, skia-ct-gce-NNN: [skia-swarming-bots](https://console.cloud.google.com/compute/instances?project=skia-swarming-bots)
|
||||
+ skia-i-gce-NNN: [google.com:skia-buildbots](https://console.cloud.google.com/compute/instances?project=google.com:skia-buildbots)
|
||||
+ ct-gce-NNN, ct-xxx-builder-NNN: [ct-swarming-bots](https://console.cloud.google.com/compute/instances?project=ct-swarming-bots)
|
||||
* To log in to a Linux bot in GCE, use `gcloud compute ssh --project <project> default@<machine name>`. Choose the zone listed on the VM's detail page (see links above). You may also specify the zone using the `--zone` command-line flag.
|
||||
* To log in to a Windows bot in GCE, first go to the VM's detail page and click the "Set Windows password"
|
||||
button. (Alternatively, ask the Infra Team how to log in as chrome-bot.) There are two options to connect:
|
||||
+ SSH: Follow the instructions for Linux using your username rather than `default`.
|
||||
+ RDP: On the VM's detail page, click the "RDP" button. (You will be instructed to install the Chrome RDP Extension
|
||||
for GCP if it hasn't already been installed.)
|
||||
|
||||
- Machine name ends with “a9”, “m3”, "m5" -> Chrome Golo/Labs
|
||||
* To log in to Golo bots, see [go/chrome-infra-build-access](https://goto.google.com/chrome-infra-build-access).
|
||||
|
||||
- Machine name starts with “skia-e-”, “skia-i-” (other than “skia-i-gce-NNN”), “skia-rpi-” -> Chapel Hill lab (aka Skolo)<br/>
|
||||
To log in to Skolo bots, see the [Skolo maintenance doc][remote access] remote access section. See the following for OS specific instructions:<br/>
|
||||
* [Remotely debug an Android device in Skolo][remotely debug android]
|
||||
* [VNC to Skolo Windows bots][vnc to skolo windows]
|
||||
* [ChromeOS Debugging][chromeos debugging]
|
||||
|
||||
|
||||
Debugging
|
||||
---------
|
||||
|
||||
If you need to run code on a specific machine/device to debug an issue, the simplest option is to
|
||||
run tryjobs (after adding debugging output to the relevant code). In some cases you may also need to
|
||||
[create or modify tryjobs](automated_testing#adding-new-jobs).
|
||||
|
||||
For Googlers: If you need more control (e.g. to run GDB) and need to run directly on a swarming bot then you can use [leasing.skia.org](https://leasing.skia.org).<br/>
|
||||
If that does not work then the [current infra gardener][current infra gardener] can help you bring the device back to your desk and connect
|
||||
it to GoogleGuest Wifi or the [Google Test Network](http://go/gtn-criteria).
|
||||
|
||||
If you need to make changes on a bot/device, please check with the Infra Gardener or another Infra team member. Most
|
||||
bots/devices can be flashed/imaged back to a clean state, but others can not.
|
||||
|
||||
If a permanent change needs to be made on the machine (such as an OS or driver update), please [file
|
||||
a bug][infra bug] and assign to jcgregorio for reassignment.
|
||||
|
||||
For your convenience, the machine skolo-builder is available for checking out and compiling code within the Skolo. See
|
||||
more info in the [Skolo maintenance doc][remote access] remote access section.
|
||||
|
||||
[current infra gardener]: https://rotations.corp.google.com/rotation/4617277386260480
|
||||
[remote access]:
|
||||
https://docs.google.com/document/d/1zTR1YtrIFBo-fRWgbUgvJNVJ-s_4_sNjTrHIoX2vulo/edit#heading=h.v77cmwbwc5la
|
||||
[infra bug]: https://bugs.chromium.org/p/skia/issues/entry?template=Infrastructure+Bug
|
||||
[remotely debug android]: https://docs.google.com/document/d/1nxn7TobfaLNNfhSTiwstOnjV0jCxYUI1uwW0T_V7BYg/
|
||||
[vnc to skolo windows]:
|
||||
https://docs.google.com/document/d/1zTR1YtrIFBo-fRWgbUgvJNVJ-s_4_sNjTrHIoX2vulo/edit#heading=h.7cqd856ft0s
|
||||
[chromeos debugging]:
|
||||
https://docs.google.com/document/d/1yJ2LLfLzV6pXKjiameid1LHEz1mj71Ob4wySIYxlBdw/edit#heading=h.9arg79l59xrf
|
||||
|
||||
Maintenance Tasks
|
||||
-----------------
|
||||
|
||||
See the [Skolo maintenance doc][skolo maintenance].
|
||||
|
||||
[skolo maintenance]:
|
||||
https://docs.google.com/document/d/1zTR1YtrIFBo-fRWgbUgvJNVJ-s_4_sNjTrHIoX2vulo/edit
|
||||
|
201
site2/docs/dev/testing/testing.md
Normal file
201
site2/docs/dev/testing/testing.md
Normal file
@ -0,0 +1,201 @@
|
||||
---
|
||||
title: 'Correctness Testing'
|
||||
linkTitle: 'Correctness Testing'
|
||||
---
|
||||
|
||||
Skia correctness testing is primarily served by a tool named DM. This is a
|
||||
quickstart to building and running DM.
|
||||
|
||||
<!--?prettify lang=sh?-->
|
||||
|
||||
python2 tools/git-sync-deps
|
||||
bin/gn gen out/Debug
|
||||
ninja -C out/Debug dm
|
||||
out/Debug/dm -v -w dm_output
|
||||
|
||||
When you run this, you may notice your CPU peg to 100% for a while, then taper
|
||||
off to 1 or 2 active cores as the run finishes. This is intentional. DM is very
|
||||
multithreaded, but some of the work, particularly GPU-backed work, is still
|
||||
forced to run on a single thread. You can use `--threads N` to limit DM to N
|
||||
threads if you like. This can sometimes be helpful on machines that have
|
||||
relatively more CPU available than RAM.
|
||||
|
||||
As DM runs, you ought to see a giant spew of output that looks something like
|
||||
this.
|
||||
|
||||
```
|
||||
Skipping nonrendering: Don't understand 'nonrendering'.
|
||||
Skipping angle: Don't understand 'angle'.
|
||||
Skipping nvprmsaa4: Could not create a surface.
|
||||
492 srcs * 3 sinks + 382 tests == 1858 tasks
|
||||
|
||||
( 25MB 1857) 1.36ms 8888 image mandrill_132x132_12x12.astc-5-subsets
|
||||
( 25MB 1856) 1.41ms 8888 image mandrill_132x132_6x6.astc-5-subsets
|
||||
( 25MB 1855) 1.35ms 8888 image mandrill_132x130_6x5.astc-5-subsets
|
||||
( 25MB 1854) 1.41ms 8888 image mandrill_132x130_12x10.astc-5-subsets
|
||||
( 25MB 1853) 151µs 8888 image mandrill_130x132_10x6.astc-5-subsets
|
||||
( 25MB 1852) 154µs 8888 image mandrill_130x130_5x5.astc-5-subsets
|
||||
...
|
||||
( 748MB 5) 9.43ms unit test GLInterfaceValidation
|
||||
( 748MB 4) 30.3ms unit test HalfFloatTextureTest
|
||||
( 748MB 3) 31.2ms unit test FloatingPointTextureTest
|
||||
( 748MB 2) 32.9ms unit test DeferredCanvas_GPU
|
||||
( 748MB 1) 49.4ms unit test ClipCache
|
||||
( 748MB 0) 37.2ms unit test Blur
|
||||
```
|
||||
|
||||
Do not panic.
|
||||
|
||||
As you become more familiar with DM, this spew may be a bit annoying. If you
|
||||
remove -v from the command line, DM will spin its progress on a single line
|
||||
rather than print a new line for each status update.
|
||||
|
||||
Don't worry about the "Skipping something: Here's why." lines at startup. DM
|
||||
supports many test configurations, which are not all appropriate for all
|
||||
machines. These lines are a sort of FYI, mostly in case DM can't run some
|
||||
configuration you might be expecting it to run.
|
||||
|
||||
Don't worry about the "skps: Couldn't read skps." messages either, you won't
|
||||
have those by default and can do without them. If you wish to test with them
|
||||
too, you can download them separately.
|
||||
|
||||
The next line is an overview of the work DM is about to do.
|
||||
|
||||
```
|
||||
492 srcs * 3 sinks + 382 tests == 1858 tasks
|
||||
```
|
||||
|
||||
DM has found 382 unit tests (code linked in from tests/), and 492 other drawing
|
||||
sources. These drawing sources may be GM integration tests (code linked in from
|
||||
gm/), image files (from `--images`, which defaults to "resources") or .skp files
|
||||
(from `--skps`, which defaults to "skps"). You can control the types of sources
|
||||
DM will use with `--src` (default, "tests gm image skp").
|
||||
|
||||
DM has found 3 usable ways to draw those 492 sources. This is controlled by
|
||||
`--config`. The defaults are operating system dependent. On Linux they are "8888
|
||||
gl nonrendering". DM has skipped nonrendering leaving two usable configs: 8888
|
||||
and gl. These two name different ways to draw using Skia:
|
||||
|
||||
- 8888: draw using the software backend into a 32-bit RGBA bitmap
|
||||
- gl: draw using the OpenGL backend (Ganesh) into a 32-bit RGBA bitmap
|
||||
|
||||
Sometimes DM calls these configs, sometimes sinks. Sorry. There are many
|
||||
possible configs but generally we pay most attention to 8888 and gl.
|
||||
|
||||
DM always tries to draw all sources into all sinks, which is why we multiply 492
|
||||
by 3. The unit tests don't really fit into this source-sink model, so they stand
|
||||
alone. A couple thousand tasks is pretty normal. Let's look at the status line
|
||||
for one of those tasks.
|
||||
|
||||
```
|
||||
( 25MB 1857) 1.36ms 8888 image mandrill_132x132_12x12.astc-5-subsets
|
||||
[1] [2] [3] [4]
|
||||
```
|
||||
|
||||
This status line tells us several things.
|
||||
|
||||
1. The maximum amount of memory DM had ever used was 25MB. Note this is a high
|
||||
water mark, not the current memory usage. This is mostly useful for us to
|
||||
track on our buildbots, some of which run perilously close to the system
|
||||
memory limit.
|
||||
|
||||
2. The number of unfinished tasks, in this example there are 1857, either
|
||||
currently running or waiting to run. We generally run one task per hardware
|
||||
thread available, so on a typical laptop there are probably 4 or 8 running at
|
||||
once. Sometimes the counts appear to show up out of order, particularly at DM
|
||||
startup; it's harmless, and doesn't affect the correctness of the run.
|
||||
|
||||
3. Next, we see this task took 1.36 milliseconds to run. Generally, the
|
||||
precision of this timer is around 1 microsecond. The time is purely there for
|
||||
informational purposes, to make it easier for us to find slow tests.
|
||||
|
||||
4. The configuration and name of the test we ran. We drew the test
|
||||
"mandrill_132x132_12x12.astc-5-subsets", which is an "image" source, into an
|
||||
"8888" sink.
|
||||
|
||||
When DM finishes running, you should find a directory with file named `dm.json`,
|
||||
and some nested directories filled with lots of images.
|
||||
|
||||
```
|
||||
$ ls dm_output
|
||||
8888 dm.json gl
|
||||
|
||||
$ find dm_output -name '*.png'
|
||||
dm_output/8888/gm/3x3bitmaprect.png
|
||||
dm_output/8888/gm/aaclip.png
|
||||
dm_output/8888/gm/aarectmodes.png
|
||||
dm_output/8888/gm/alphagradients.png
|
||||
dm_output/8888/gm/arcofzorro.png
|
||||
dm_output/8888/gm/arithmode.png
|
||||
dm_output/8888/gm/astcbitmap.png
|
||||
dm_output/8888/gm/bezier_conic_effects.png
|
||||
dm_output/8888/gm/bezier_cubic_effects.png
|
||||
dm_output/8888/gm/bezier_quad_effects.png
|
||||
...
|
||||
```
|
||||
|
||||
The directories are nested first by sink type (`--config`), then by source type
|
||||
(`--src`). The image from the task we just looked at, "8888 image
|
||||
mandrill_132x132_12x12.astc-5-subsets", can be found at
|
||||
`dm_output/8888/image/mandrill_132x132_12x12.astc-5-subsets.png`.
|
||||
|
||||
`dm.json` is used by our automated testing system, so you can ignore it if you
|
||||
like. It contains a listing of each test run and a checksum of the image
|
||||
generated for that run.
|
||||
|
||||
### Detail <a name="digests"></a>
|
||||
|
||||
Boring technical detail: The checksum is not a checksum of the .png file, but
|
||||
rather a checksum of the raw pixels used to create that .png. That means it is
|
||||
possible for two different configurations to produce the same exact .png, but
|
||||
have their checksums differ.
|
||||
|
||||
Unit tests don't generally output anything but a status update when they pass.
|
||||
If a test fails, DM will print out its assertion failures, both at the time they
|
||||
happen and then again all together after everything is done running. These
|
||||
failures are also included in the `dm.json` file.
|
||||
|
||||
DM has a simple facility to compare against the results of a previous run:
|
||||
|
||||
<!--?prettify lang=sh?-->
|
||||
|
||||
ninja -C out/Debug dm
|
||||
out/Debug/dm -w good
|
||||
|
||||
# do some work
|
||||
|
||||
ninja -C out/Debug dm
|
||||
out/Debug/dm -r good -w bad
|
||||
|
||||
When using `-r`, DM will display a failure for any test that didn't produce the
|
||||
same image as the `good` run.
|
||||
|
||||
For anything fancier, I suggest using skdiff:
|
||||
|
||||
<!--?prettify lang=sh?-->
|
||||
|
||||
ninja -C out/Debug dm
|
||||
out/Debug/dm -w good
|
||||
|
||||
# do some work
|
||||
|
||||
ninja -C out/Debug dm
|
||||
out/Debug/dm -w bad
|
||||
|
||||
ninja -C out/Debug skdiff
|
||||
mkdir diff
|
||||
out/Debug/skdiff good bad diff
|
||||
|
||||
# open diff/index.html in your web browser
|
||||
|
||||
That's the basics of DM. DM supports many other modes and flags. Here are a few
|
||||
examples you might find handy.
|
||||
|
||||
<!--?prettify lang=sh?-->
|
||||
|
||||
out/Debug/dm --help # Print all flags, their defaults, and a brief explanation of each.
|
||||
out/Debug/dm --src tests # Run only unit tests.
|
||||
out/Debug/dm --nocpu # Test only GPU-backed work.
|
||||
out/Debug/dm --nogpu # Test only CPU-backed work.
|
||||
out/Debug/dm --match blur # Run only work with "blur" in its name.
|
||||
out/Debug/dm --dryRun # Don't really do anything, just print out what we'd do.
|
129
site2/docs/dev/testing/tests.md
Normal file
129
site2/docs/dev/testing/tests.md
Normal file
@ -0,0 +1,129 @@
|
||||
---
|
||||
title: 'Writing Skia Tests'
|
||||
linkTitle: 'Writing Skia Tests'
|
||||
---
|
||||
|
||||
- [Unit Tests](#test)
|
||||
- [Rendering Tests](#gm)
|
||||
- [Benchmark Tests](#bench)
|
||||
|
||||
We assume you have already synced Skia's dependencies and set up Skia's build
|
||||
system.
|
||||
|
||||
<!--?prettify lang=sh?-->
|
||||
|
||||
python2 tools/git-sync-deps
|
||||
bin/gn gen out/Debug
|
||||
bin/gn gen out/Release --args='is_debug=false'
|
||||
|
||||
<span id="test"></span>
|
||||
|
||||
## Writing a Unit Test
|
||||
|
||||
1. Add a file `tests/NewUnitTest.cpp`:
|
||||
|
||||
<!--?prettify lang=cc?-->
|
||||
|
||||
/*
|
||||
* Copyright ........
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
#include "Test.h"
|
||||
DEF_TEST(NewUnitTest, reporter) {
|
||||
if (1 + 1 != 2) {
|
||||
ERRORF(reporter, "%d + %d != %d", 1, 1, 2);
|
||||
}
|
||||
bool lifeIsGood = true;
|
||||
REPORTER_ASSERT(reporter, lifeIsGood);
|
||||
}
|
||||
|
||||
2. Add `NewUnitTest.cpp` to `gn/tests.gni`.
|
||||
|
||||
3. Recompile and run test:
|
||||
|
||||
<!--?prettify lang=sh?-->
|
||||
|
||||
ninja -C out/Debug dm
|
||||
out/Debug/dm --match NewUnitTest
|
||||
|
||||
<span id="gm"></span>
|
||||
|
||||
## Writing a Rendering Test
|
||||
|
||||
1. Add a file `gm/newgmtest.cpp`:
|
||||
|
||||
<!--?prettify lang=cc?-->
|
||||
|
||||
/*
|
||||
* Copyright ........
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
#include "gm.h"
|
||||
DEF_SIMPLE_GM(newgmtest, canvas, 128, 128) {
|
||||
canvas->clear(SK_ColorWHITE);
|
||||
SkPaint p;
|
||||
p.setStrokeWidth(2);
|
||||
canvas->drawLine(16, 16, 112, 112, p);
|
||||
}
|
||||
|
||||
2. Add `newgmtest.cpp` to `gn/gm.gni`.
|
||||
|
||||
3. Recompile and run test:
|
||||
|
||||
<!--?prettify lang=sh?-->
|
||||
|
||||
ninja -C out/Debug dm
|
||||
out/Debug/dm --match newgmtest
|
||||
|
||||
4. Run the GM inside Viewer:
|
||||
|
||||
<!--?prettify lang=sh?-->
|
||||
|
||||
ninja -C out/Debug viewer
|
||||
out/Debug/viewer --slide GM_newgmtest
|
||||
|
||||
<span id="bench"></span>
|
||||
|
||||
## Writing a Benchmark Test
|
||||
|
||||
1. Add a file `bench/FooBench.cpp`:
|
||||
|
||||
<!--?prettify lang=cc?-->
|
||||
|
||||
/*
|
||||
* Copyright ........
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
#include "Benchmark.h"
|
||||
#include "SkCanvas.h"
|
||||
namespace {
|
||||
class FooBench : public Benchmark {
|
||||
public:
|
||||
FooBench() {}
|
||||
virtual ~FooBench() {}
|
||||
protected:
|
||||
const char* onGetName() override { return "Foo"; }
|
||||
SkIPoint onGetSize() override { return SkIPoint{100, 100}; }
|
||||
void onDraw(int loops, SkCanvas* canvas) override {
|
||||
while (loops-- > 0) {
|
||||
canvas->drawLine(0.0f, 0.0f, 100.0f, 100.0f, SkPaint());
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
DEF_BENCH(return new FooBench;)
|
||||
|
||||
2. Add `FooBench.cpp` to `gn/bench.gni`.
|
||||
|
||||
3. Recompile and run nanobench:
|
||||
|
||||
<!--?prettify lang=sh?-->
|
||||
|
||||
ninja -C out/Release nanobench
|
||||
out/Release/nanobench --match Foo
|
104
site2/docs/dev/testing/xsan.md
Normal file
104
site2/docs/dev/testing/xsan.md
Normal file
@ -0,0 +1,104 @@
|
||||
|
||||
---
|
||||
title: "MSAN, ASAN, & TSAN"
|
||||
linkTitle: "MSAN, ASAN, & TSAN"
|
||||
|
||||
---
|
||||
|
||||
|
||||
*Testing Skia with memory, address, and thread santizers.*
|
||||
|
||||
Compiling Skia with ASAN, UBSAN, or TSAN can be done with the latest version of Clang.
|
||||
|
||||
- UBSAN works on Linux, Mac, Android, and Windows, though some checks are platform-specific.
|
||||
- ASAN works on Linux, Mac, Android, and Windows.
|
||||
- TSAN works on Linux and Mac.
|
||||
- MSAN works on Linux[1].
|
||||
|
||||
We find that testing sanitizer builds with libc++ uncovers more issues than
|
||||
with the system-provided C++ standard library, which is usually libstdc++.
|
||||
libc++ proactively hooks into sanitizers to help their analyses.
|
||||
We ship a copy of libc++ with our Linux toolchain in /lib.
|
||||
|
||||
[1]To compile and run with MSAN, an MSAN-instrumented version of libc++ is needed.
|
||||
It's generally easiest to run one of the following 2 steps to build/download a recent version
|
||||
of Clang and the instrumented libc++, located in /msan.
|
||||
|
||||
Downloading Clang binaries (Googlers Only)
|
||||
------------------------------------------
|
||||
This requires gsutil, part of the [gcloud sdk](https://cloud.google.com/sdk/downloads).
|
||||
|
||||
<!--?prettify lang=sh?-->
|
||||
|
||||
CLANGDIR="${HOME}/clang"
|
||||
python2 infra/bots/assets/clang_linux/download.py -t $CLANGDIR
|
||||
|
||||
Building Clang binaries from scratch (Other users)
|
||||
---------------------------
|
||||
|
||||
<!--?prettify lang=sh?-->
|
||||
|
||||
CLANGDIR="${HOME}/clang"
|
||||
|
||||
python2 tools/git-sync-deps
|
||||
CC= CXX= infra/bots/assets/clang_linux/create.py -t "$CLANGDIR"
|
||||
|
||||
Configure and Compile Skia with MSAN
|
||||
------------------------------------
|
||||
|
||||
<!--?prettify lang=sh?-->
|
||||
|
||||
CLANGDIR="${HOME}/clang"
|
||||
mkdir -p out/msan
|
||||
cat > out/msan/args.gn <<- EOF
|
||||
cc = "${CLANGDIR}/bin/clang"
|
||||
cxx = "${CLANGDIR}/bin/clang++"
|
||||
extra_cflags = [ "-B${CLANGDIR}/bin" ]
|
||||
extra_ldflags = [
|
||||
"-B${CLANGDIR}/bin",
|
||||
"-fuse-ld=lld",
|
||||
"-L${CLANGDIR}/msan",
|
||||
"-Wl,-rpath,${CLANGDIR}/msan" ]
|
||||
sanitize = "MSAN"
|
||||
skia_use_fontconfig = false
|
||||
EOF
|
||||
python2 tools/git-sync-deps
|
||||
bin/gn gen out/msan
|
||||
ninja -C out/msan
|
||||
|
||||
Configure and Compile Skia with ASAN
|
||||
------------------------------------
|
||||
|
||||
<!--?prettify lang=sh?-->
|
||||
|
||||
CLANGDIR="${HOME}/clang"
|
||||
mkdir -p out/asan
|
||||
cat > out/asan/args.gn <<- EOF
|
||||
cc = "${CLANGDIR}/bin/clang"
|
||||
cxx = "${CLANGDIR}/bin/clang++"
|
||||
sanitize = "ASAN"
|
||||
extra_ldflags = [ "-fuse-ld=lld", "-Wl,-rpath,${CLANGDIR}/lib" ]
|
||||
EOF
|
||||
python2 tools/git-sync-deps
|
||||
bin/gn gen out/asan
|
||||
ninja -C out/asan
|
||||
|
||||
Configure and Compile Skia with TSAN
|
||||
------------------------------------
|
||||
|
||||
<!--?prettify lang=sh?-->
|
||||
|
||||
CLANGDIR="${HOME}/clang"
|
||||
mkdir -p out/tsan
|
||||
cat > out/tsan/args.gn <<- EOF
|
||||
cc = "${CLANGDIR}/bin/clang"
|
||||
cxx = "${CLANGDIR}/bin/clang++"
|
||||
sanitize = "TSAN"
|
||||
is_debug = false
|
||||
extra_ldflags = [ "-Wl,-rpath,${CLANGDIR}/lib" ]
|
||||
EOF
|
||||
python2 tools/git-sync-deps
|
||||
bin/gn gen out/tsan
|
||||
ninja -C out/tsan
|
||||
|
||||
|
12
site2/docs/dev/tools/_index.md
Normal file
12
site2/docs/dev/tools/_index.md
Normal file
@ -0,0 +1,12 @@
|
||||
|
||||
---
|
||||
title: "Tools"
|
||||
linkTitle: "Tools"
|
||||
|
||||
weight: 2
|
||||
|
||||
---
|
||||
|
||||
|
||||
Developer tools for working in Skia.
|
||||
|
56
site2/docs/dev/tools/codesearch.md
Normal file
56
site2/docs/dev/tools/codesearch.md
Normal file
@ -0,0 +1,56 @@
|
||||
|
||||
---
|
||||
title: "Code Search"
|
||||
linkTitle: "Code Search"
|
||||
|
||||
---
|
||||
|
||||
|
||||
There are a number of ways to search the Skia codebase, each with advantages and
|
||||
disadvantages.
|
||||
|
||||
[cs.skia.org](http://cs.skia.org) redirects to
|
||||
[Chromium code search](https://code.google.com/p/chromium/codesearch) restricted
|
||||
to the Skia portion of the Chromium tree. You can add a query after the slash;
|
||||
e.g. [cs.skia.org/foo](http://cs.skia.org/foo) will search for "foo" within the
|
||||
Skia tree. Chromium code search provides cross-references.
|
||||
|
||||
For Googlers, there is also the option of [the skia depot](http://cs/#skia/) in
|
||||
internal Code Search. In addition to the
|
||||
main [skia](http://cs/#skia/skia/) repo, internal Code Search indexes the
|
||||
[buildbot](http://cs/#skia/buildbot/), [common](http://cs/#skia/common/),
|
||||
and [skia_internal](https://cs/#skia/skia_internal/) repos. However,
|
||||
cross-references and code analysis are not available.
|
||||
|
||||
The GitHub mirrors of the [skia](https://github.com/google/skia) and
|
||||
[skia-buildbot](https://github.com/google/skia-buildbot) repos are useful for
|
||||
investigating history and blame, or for exploring release branches or other
|
||||
branches. However, the search functionality is fairly limited, cross-references
|
||||
are not available, and in history the original committer's username is replaced
|
||||
with that person's GitHub username.
|
||||
|
||||
You can also navigate through the
|
||||
[Skia repos on googlesource.com](https://skia.googlesource.com/). All commits
|
||||
appear here first.
|
||||
|
||||
Code search option |Search |XRef |History |Repos |Branches |Freshness
|
||||
--------------------|-------|-----|--------|------------------------------|---------|---------------
|
||||
[cs.skia.org][1] |regexp | yes |yes |skia [buildbot][5] |master |last DEPS roll
|
||||
[Internal][2] |regexp | no |yes |skia buildbot common internal |master |hours
|
||||
[GitHub][3] |basic | no |yes |skia buildbot |all |hour
|
||||
[googlesource][4] |none | no |yes |all |all |N/A
|
||||
|
||||
[1]: http://cs.skia.org/ "Chromium code search"
|
||||
[2]: http://cs/#skia/ "Internal Code Search"
|
||||
[3]: https://github.com/google/skia "GitHub mirror of skia"
|
||||
[4]: https://skia.googlesource.com/ "Primary Skia repos on googlesource.com"
|
||||
[5]: https://cs.chromium.org/chromium/skia/buildbot/
|
||||
|
||||
Client code search
|
||||
------------------
|
||||
|
||||
There is an [internal tool for
|
||||
Googlers](https://goto.google.com/skia-client-search) to make it easier to
|
||||
search the repos of Skia clients, e.g. Chromium, Android, and Mozilla. If you
|
||||
use it and have suggestions, please let brianosman know.
|
||||
|
192
site2/docs/dev/tools/debugger.md
Normal file
192
site2/docs/dev/tools/debugger.md
Normal file
@ -0,0 +1,192 @@
|
||||
---
|
||||
title: 'Skia Debugger'
|
||||
linkTitle: 'Skia Debugger'
|
||||
---
|
||||
|
||||
## Introduction
|
||||
|
||||
The Skia Debugger is a graphical tool used to step through and analyze the
|
||||
contents of the Skia picture format. The tool is available online at
|
||||
[https://debugger.skia.org](https://debugger.skia.org/) or can be run locally.
|
||||
|
||||
Features:
|
||||
|
||||
- Draw command and multiple frame playback
|
||||
- Shows the current clip and matrix at any step
|
||||
- Zoomed viewer with crosshair for selecting pixels.
|
||||
- Breakpoints that stop playback when a pixel's color changes.
|
||||
- GPU or CPU backed execution.
|
||||
- GPU op bounds visualization
|
||||
- Android offscreen layer visualization
|
||||
- Shared resource viewer
|
||||
|
||||
<img src="/dev/tools/onlinedebugger.png" style="display: inline-block;" />
|
||||
|
||||
## User Guide
|
||||
|
||||
SKP files can contain a single frame or multiple frames. Single frame files have
|
||||
the .skp extension and Multi-frame files have the .mskp extension. In the online
|
||||
debugger linked above, Open a [sample mskp file](/dev/tools/calendar.mskp) or
|
||||
capture one from an android device using the
|
||||
[instructions here](https://sites.google.com/a/google.com/skia/android/skp-from-framework).
|
||||
|
||||
### Command Playback and Filters
|
||||
|
||||
Try playing back the commands within the current frame using the lower play
|
||||
button <img src="/dev/tools/playcommands.png" style="display: inline-block;" />,
|
||||
(the one not in a circle) You should see the image built up one draw at a time.
|
||||
|
||||
Many commands manipulate the matrix or clip but don't make any visible change
|
||||
when run. Try filtering these out by pasting
|
||||
`!drawannotation save restore concat setmatrix cliprect` in to the filter
|
||||
textbox just below the command playback controls. Press enter to apply the
|
||||
filter, and resume playback if it was paused. This will have the effect of
|
||||
making the playback appear to be much faster as there are only 29 commands in
|
||||
the first frame of the sample file that pass this filter.
|
||||
|
||||
Try pausing command playback and stepping forward and back through the commands
|
||||
using `,` (comma) and `.` (period).
|
||||
|
||||
> Filters are case insensitive, and the only supported logical operator is !
|
||||
> (not) which applies to the entire filter and is only recognised when it occurs
|
||||
> at the beginning.
|
||||
|
||||
Any command can be expanded using the
|
||||
<img src="/dev/tools/expand.png" style="display: inline-block;" /> icon to see
|
||||
all of the parameters that were recorded with that command.
|
||||
|
||||
Commands can be disabled or enabled with the checkbox that becomes available
|
||||
after expanding the command's detail view.
|
||||
|
||||
Jog the command playhead to the end of the list with the
|
||||
<img src="/dev/tools/end.png" style="display: inline-block;" /> button.
|
||||
|
||||
### Frame playback
|
||||
|
||||
<img src="/dev/tools/frameplayback.png" style="display: inline-block;" />
|
||||
|
||||
The sample file contains multiple frames. Use the encircled play button to play
|
||||
back the frames. The current frame is indictated by the slider position, and the
|
||||
slider can be set manually. Frames can be stepped through with `w` (back) and
|
||||
`s` forward. `p` pauses or unpauses the frame playback.
|
||||
|
||||
Not all frames in a file will have the same number of commands. When the command
|
||||
playhead is left at the end of the list the debugger will play every frame to
|
||||
the end of its list. If the command playhead is somewhere in the middle, say
|
||||
155, the debugger will try to play every frame to its 155th command.
|
||||
|
||||
### Resources Tab
|
||||
|
||||
<img src="/dev/tools/resources.png" style="display: inline-block;" />
|
||||
|
||||
Any resources that were referenced by commands in the file appear here. As of
|
||||
Dec 2019, this only shows images.
|
||||
|
||||
Any resource can be selected and viewed. You may find it helpful to toggle the
|
||||
Light/Dark setting if you cannot see an image.
|
||||
|
||||
Images' names are formatted as `7 @24205864 (99, 99)` where `7` is the index of
|
||||
the image as it was saved in the file, `@24205864` is it's address in wasm
|
||||
memory, for cross referencing with DrawImage\* commands in the command list
|
||||
which also show this address, and `(99, 99)` is the size of the image.
|
||||
|
||||
The resource viewer allows a user to determine if an image was not effectively
|
||||
shared between frames or draw commands. If it occurs more than once in the
|
||||
resource tab, then there were multiple copies of it with different generation
|
||||
ids in the process that recorded the SKP.
|
||||
|
||||
### Android Layers
|
||||
|
||||
<img src="/dev/tools/layers.png" style="display: inline-block;" />
|
||||
|
||||
When MSKPs are recorded in Android, Extra information about offscreen hardware
|
||||
layers is recorded. The sample google calendar mskp linked above contains this
|
||||
information. You will find two layers on frame 3.
|
||||
|
||||
There are two kinds of events relevant to recorded android layer use.
|
||||
|
||||
1. Draw Events - points when an offscreen surface was drawn to. They may be
|
||||
complete, meaning the clip equalled the surface's size, or partial, meaning
|
||||
the clip was smaller.
|
||||
2. Use events - points when the offscreen surface was used as an SkImage in the
|
||||
main surface.
|
||||
|
||||
Layers are shown as boxes in the bottom right of the interface when viewing a
|
||||
frame where a layer draw event occurred. Each Layer box has two buttons:
|
||||
`Show Use` will cycle through use events for that layer in the current frame if
|
||||
there are any, and `Inspector` will open the draw event as if it were a single
|
||||
frame SKP. you can play back it's commands, enable or disabled them, inspect GPU
|
||||
op bounds or anything else you could do with an ordinary SKP. Exit the inspector
|
||||
by clicking the `Exit` button on the layer box.
|
||||
|
||||
### Crosshair and Breakpoints
|
||||
|
||||
<img src="/dev/tools/crosshair.png" style="display: inline-block;" />
|
||||
|
||||
Clicking any point in the main view will toggle a red crosshair for selecting
|
||||
pixels. the selected pixel's color is shown in several formats on the right
|
||||
pane. A zoomed view centered on the selected pixel is shown below it. The
|
||||
position can be moved precicely by either clicking neighboring pixels in the
|
||||
zoom view, or using `H` (left) `L` (right) `J` (down) `K` (up).
|
||||
|
||||
When "Break on change" is selected, command playback will pause on any command
|
||||
which changes the color of the selected pixel. this can be used to find the
|
||||
command that draws something you see in the viewer.
|
||||
|
||||
### GPU Op Bounds and Other settings
|
||||
|
||||
<img src="/dev/tools/settings.png" style="display: inline-block;" />
|
||||
|
||||
Each of the filtered commands from above has a colored number to its right
|
||||
<img src="/dev/tools/gpuop.png" style="display: inline-block;" />. This is the
|
||||
GPU operation id. When multiple commands share a GPU op id, this indicates that
|
||||
they were batched together when sent to the GPU. In the WASM debugger, this goes
|
||||
though WebGL.
|
||||
|
||||
There is a "Display GPU Op Bounds" toggle in the upper right of the interface.
|
||||
Turning this on will show a colored rectangle to represent the bounds of the GPU
|
||||
op of the currently selected command.
|
||||
|
||||
GPU - Controls which backend Skia uses to draw to the screen. GPU in the online
|
||||
wasm debugger means WebGL. CPU means skia draws into a surface in memory which
|
||||
is copied into an HTML canvas without using the GPU.
|
||||
|
||||
Light/Dark - this toggle changes the appearance of the checkerboard behind the
|
||||
main view and zoom views to assist in viewing content with transparency.
|
||||
|
||||
Display Overdraw Viz - This vizualization shows a red overlay that is darker in
|
||||
propertion to how much overdraw has occurred on a pixel. Overdraw meaning that
|
||||
the pixel was drawn to more than once.
|
||||
|
||||
- As of Dec 2019, this feature may not be working correctly.
|
||||
|
||||
### Image fit and download buttons.
|
||||
|
||||
<img src="/dev/tools/settings.png" style="display: inline-block;" />
|
||||
|
||||
These buttons resize the main view. they are, from left to right:
|
||||
|
||||
Original size - the natural size of the canvas, when it was recorded. Fit to
|
||||
page - shrink enough that the whole canvas fits in the center pane. Fit to page
|
||||
width - make the canvas fit horizontally but allow scrolling vertically Fit to
|
||||
page height - make the canvas fit vertically but allow scrolling horizontally.
|
||||
|
||||
next to these is a 5th, unrelated download button which saves the current canvas
|
||||
as a PNG file.
|
||||
|
||||
## Building and running locally
|
||||
|
||||
Begin by following the instructions to
|
||||
[download and build Skia](../../user/quick), then simply build and run the
|
||||
`skiaserve` tool:
|
||||
|
||||
<!--?prettify lang=sh?-->
|
||||
|
||||
# Build.
|
||||
ninja -C out/Release skiaserve
|
||||
|
||||
# Run the debugger locally
|
||||
out/Release/skiaserve
|
||||
|
||||
After running `skiaserve`, follow the instructions to open the debugger in your
|
||||
local browser. By default the address will be `http://127.0.0.1:8888`.
|
18
site2/docs/dev/tools/debugvis.md
Normal file
18
site2/docs/dev/tools/debugvis.md
Normal file
@ -0,0 +1,18 @@
|
||||
|
||||
---
|
||||
title: "Debug Visualization"
|
||||
linkTitle: "Debug Visualization"
|
||||
|
||||
---
|
||||
|
||||
|
||||
Skia uses custom container types, such as `SkString` and `SkTArray<>`, which can
|
||||
be inconvenient to view in a debugger.
|
||||
|
||||
If you frequently debug code that uses Skia types, consider installing a debug
|
||||
visualizer. Skia offers debugger visualization support for the following
|
||||
platforms:
|
||||
|
||||
- [Visual Studio and VS Code](https://skia.googlesource.com/skia/+/refs/heads/master/platform_tools/debugging/vs/Skia.natvis)
|
||||
- [LLDB and Xcode](https://skia.googlesource.com/skia/+/refs/heads/master/platform_tools/debugging/lldb/skia.py)
|
||||
|
52
site2/docs/dev/tools/markdown.md
Normal file
52
site2/docs/dev/tools/markdown.md
Normal file
@ -0,0 +1,52 @@
|
||||
---
|
||||
title: 'Markdown'
|
||||
linkTitle: 'Markdown'
|
||||
---
|
||||
|
||||
This site is build with [Hugo](https://gohugo.io/) and
|
||||
[Docsy](https://www.docsy.dev/).
|
||||
|
||||
Any file you put under `/site/` that has the extension `.md` will be processed
|
||||
as Markdown. All other files will be served directly. For example, images can be
|
||||
added and they will be served correctly and referenced from within Markdown
|
||||
files.
|
||||
|
||||
When preparing for a code review of site docs you can get a preview of how the
|
||||
page will render by visiting the skia.org site and add a query parameter `cl`
|
||||
with the value of the Reitveld issue id:
|
||||
|
||||
https://skia.org/path/to/markdown-file?cl=REITVELD_ISSUE_NUMBER
|
||||
|
||||
See the [Docsy documentation](https://www.docsy.dev/docs/) for more details on
|
||||
how to configure and use docsy. For example the
|
||||
[Navigation](https://www.docsy.dev/docs/adding-content/navigation/) section
|
||||
explains what frontmatter needs to be added to a page to get it to appear in the
|
||||
top navigation bar.
|
||||
|
||||
## Frontmatter
|
||||
|
||||
Each page needs a frontmatter section that provides information on that page.
|
||||
For example:
|
||||
|
||||
```
|
||||
---
|
||||
title: 'Markdown'
|
||||
linkTitle: 'Markdown'
|
||||
---
|
||||
```
|
||||
|
||||
This is true for both Markdown and HTML pages. See
|
||||
[the Docsy documentation on frontmatter](https://www.docsy.dev/docs/adding-content/content/#page-frontmatter)
|
||||
for more details.
|
||||
|
||||
## Styling And Icons
|
||||
|
||||
Docsy supports both
|
||||
[Bootstrap](https://getbootstrap.com/docs/5.0/getting-started/introduction/) and
|
||||
[Font-Awesome](https://fontawesome.com/). Check out their documentation for what
|
||||
they offer.
|
||||
|
||||
## Configuration
|
||||
|
||||
The Hugo configuration file is [config.toml](../../../config.toml) in the site
|
||||
directory.
|
115
site2/docs/dev/tools/tracing.md
Normal file
115
site2/docs/dev/tools/tracing.md
Normal file
@ -0,0 +1,115 @@
|
||||
|
||||
---
|
||||
title: "Tracing Skia Execution"
|
||||
linkTitle: "Tracing Skia Execution"
|
||||
|
||||
---
|
||||
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
Skia is instrumented to provide execution traces in several ways. Within Chrome, Skia is traced
|
||||
with the standard [tracing interface](chrome://tracing), along with the rest of Chromium. In
|
||||
the Android framework, Skia's tracing is integrated into
|
||||
[atrace](https://source.android.com/devices/tech/debug/ftrace).
|
||||
|
||||
For standalone builds, Skia's tools (DM, nanobench, and Viewer) are capable of tracing execution
|
||||
in three ways, controlled by the `--trace` command line argument.
|
||||
|
||||
Standalone Tracing
|
||||
------------------
|
||||
|
||||
Most arguments to `--trace` will be interpreted as a filename (the two exceptions are described
|
||||
below), and trace events will be written to that file in JSON format, suitable for viewing with
|
||||
[chrome://tracing](chrome://tracing).
|
||||
|
||||
<!--?prettify lang=sh?-->
|
||||
|
||||
# Run DM on several GMs to get tracing data
|
||||
out/Release/dm --config gl --match bleed --trace gl_bleed_gms.json
|
||||
|
||||
This creates a file `gl_bleed_gms.json` in the current directory. There are limitations in Chrome's
|
||||
tracing tool that prevent loading a file larger than 256 MB. To stay under that limit (and avoid
|
||||
clutter and slowdown in the interface), it's best to run a small number of tests/benchmarks when
|
||||
tracing. Once you have generated a file in this way, go to
|
||||
[chrome://tracing](chrome://tracing), click Load:
|
||||
|
||||
![Load Button](tracing_load.png)
|
||||
|
||||
... then select the JSON file. The data will be loaded and can be navigated/inspected using the
|
||||
tracing tools. Tip: press '?' for a help screen explaining the available keyboard and mouse
|
||||
controls.
|
||||
|
||||
![Tracing interface](tracing.png)
|
||||
|
||||
Android ATrace
|
||||
--------------
|
||||
|
||||
Running any tool with `--trace atrace` on an Android device will cause the application to forward
|
||||
tracing information to [atrace](https://source.android.com/devices/tech/debug/ftrace). On other
|
||||
platforms, this has no effect.
|
||||
|
||||
If you run `systrace` from the host command line, you will need to supply `-a <app_name>`,
|
||||
and the `<app_name>` argument will need to exactly match the command line used on the target
|
||||
device. For example, if you use `adb shell "cd /data/local/tmp; ./nanobench --trace atrace ..."`
|
||||
you must pass `-a ./nanobench` or systrace will ignore events from the application.
|
||||
|
||||
Console Logging
|
||||
---------------
|
||||
|
||||
For simple situations, all tracing events can be directed to the console with `--trace debugf`:
|
||||
|
||||
<!--?prettify lang=sh?-->
|
||||
|
||||
# Run DM on a single GM with SkDebugf tracing
|
||||
out/Release/dm --config gl --match ^gamma$ --trace debugf
|
||||
|
||||
~~~
|
||||
[ 0] <skia.gpu> GrDrawingManager::internalFlush id=1 #0 {
|
||||
[ 0] } GrDrawingManager::internalFlush
|
||||
[ 0] <skia.gpu> GrGpu::createTexture id=1 #1 {
|
||||
[ 0] } GrGpu::createTexture
|
||||
[ 0] <skia.gpu> GrRenderTargetContext::discard id=1 #2 {
|
||||
[ 0] } GrRenderTargetContext::discard
|
||||
[ 0] <skia.gpu> SkGpuDevice::clearAll id=1 #3 {
|
||||
[ 1] <skia.gpu> GrRenderTargetContext::clear id=1 #4 {
|
||||
[ 1] } GrRenderTargetContext::clear
|
||||
[ 0] } SkGpuDevice::clearAll
|
||||
[ 0] <skia> SkCanvas::drawRect() #5 {
|
||||
[ 1] <skia.gpu> SkGpuDevice::drawRect id=1 #6 {
|
||||
[ 2] <skia.gpu> GrRenderTargetContext::drawRect id=1 #7 {
|
||||
[ 3] <skia.gpu> GrRenderTargetContext::addDrawOp id=1 #8 {
|
||||
[ 3] } GrRenderTargetContext::addDrawOp
|
||||
[ 2] } GrRenderTargetContext::drawRect
|
||||
[ 1] } SkGpuDevice::drawRect
|
||||
[ 0] } SkCanvas::drawRect()
|
||||
...
|
||||
~~~
|
||||
|
||||
Adding More Trace Events
|
||||
------------------------
|
||||
|
||||
Adding more trace events involves using a set of `TRACE_` macros. The simplest example, to record
|
||||
the time spent in a function or other scope, is:
|
||||
|
||||
~~~
|
||||
#include "SkTraceEvent.h"
|
||||
...
|
||||
void doSomething() {
|
||||
// Add an event for the duration of the current function (or other scope)
|
||||
// "skia" is a category name, for filtering events while recording
|
||||
// TRACE_FUNC is the event name, and expands to the name of the current function
|
||||
TRACE_EVENT0("skia", TRACE_FUNC);
|
||||
|
||||
if (doExtraWork) {
|
||||
TRACE_EVENT0("skia", "ExtraWorkBeingDone");
|
||||
...
|
||||
}
|
||||
}
|
||||
~~~
|
||||
|
||||
For more examples, including other kinds of trace events and attaching parameters to events, see
|
||||
the comments in
|
||||
[SkTraceEventCommon.h](https://cs.chromium.org/chromium/src/third_party/skia/src/core/SkTraceEventCommon.h).
|
||||
|
73
site2/docs/roles.md
Normal file
73
site2/docs/roles.md
Normal file
@ -0,0 +1,73 @@
|
||||
|
||||
---
|
||||
title: "Project Roles"
|
||||
linkTitle: "Project Roles"
|
||||
|
||||
---
|
||||
|
||||
|
||||
The Skia open source project includes individuals working in a variety of
|
||||
roles. Anyone can view the code, use the Skia library, file bugs, and submit
|
||||
patches. This page describes in detail the kinds of roles that interested
|
||||
parties can assume.
|
||||
|
||||
For more information on ways to get involved in Skia development, see the
|
||||
[Contributing to Skia page](/dev/contrib/).
|
||||
|
||||
<div>
|
||||
<style scoped><!--
|
||||
#rolestable {border-collapse:collapse;}
|
||||
#rolestable tr th, #rolestable tr td {border-right:white 2px solid;padding:0 5px;}
|
||||
#rolestable tr td {height:10ex;}
|
||||
#rolestable tr td p {margin:5px 0; padding:0;}
|
||||
--></style>
|
||||
<table id="rolestable">
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Source Code & Documentation</th>
|
||||
<th>Code Reviews</th>
|
||||
<th>Bug Tracker</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
<tr style="background-color:#e6b8af;color:black;">
|
||||
<th>Committer</th>
|
||||
<td>
|
||||
<p>force-commit</p>
|
||||
</td>
|
||||
<td>
|
||||
<p>approve changes</p>
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr style="background-color:#ffe599;color:black;">
|
||||
<th>Contributor<br>(and above)</th>
|
||||
<td></td>
|
||||
<td>
|
||||
<p>launch try jobs</p>
|
||||
</td>
|
||||
<td>
|
||||
<p>change bug status</p>
|
||||
<p>edit bug labels</p>
|
||||
<p>own bugs</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr style="background-color:#d0e0e3;color:black;">
|
||||
<th>Developer<br>(and above)</th>
|
||||
<td>
|
||||
<p>download</p>
|
||||
<p>view history</p>
|
||||
</td>
|
||||
<td>
|
||||
<p>commit changes (once approved)</p>
|
||||
<p>upload changes for approval</p>
|
||||
<p>view</p>
|
||||
</td>
|
||||
<td>
|
||||
<p>add comments to existing bugs</p>
|
||||
<p>file new bugs</p>
|
||||
<p>view bugs</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
29
site2/docs/user/_index.md
Normal file
29
site2/docs/user/_index.md
Normal file
@ -0,0 +1,29 @@
|
||||
|
||||
---
|
||||
title: "User Documentation"
|
||||
linkTitle: "User Documentation"
|
||||
|
||||
weight: 1
|
||||
|
||||
---
|
||||
|
||||
|
||||
If you want to write code that uses the Skia library, this is the place for you.
|
||||
|
||||
The Skia graphics library can be used for drawing Text, Geometries, and Images:
|
||||
|
||||
* 3x3 matrices w/ perspective
|
||||
* antialiasing, transparency, filters
|
||||
* shaders, xfermodes, maskfilters, patheffects
|
||||
* subpixel text
|
||||
|
||||
Device backends for Skia currently include:
|
||||
|
||||
* Raster
|
||||
* OpenGL
|
||||
* PDF
|
||||
* XPS
|
||||
* SVG
|
||||
* Picture (for recording and then playing back into another Canvas)
|
||||
|
||||
|
78
site2/docs/user/api/SkBlendMode_Overview.md
Normal file
78
site2/docs/user/api/SkBlendMode_Overview.md
Normal file
@ -0,0 +1,78 @@
|
||||
|
||||
---
|
||||
title: "SkBlendMode Overview"
|
||||
linkTitle: "SkBlendMode Overview"
|
||||
|
||||
weight: 260
|
||||
|
||||
---
|
||||
|
||||
Describes how destination <a href='undocumented#Pixel'>pixel</a> is replaced with a combination of itself and
|
||||
source <a href='undocumented#Pixel'>pixel</a>. <a href='#Blend_Mode'>Blend_Mode</a> may use source, destination, or both. <a href='#Blend_Mode'>Blend_Mode</a> may
|
||||
operate on each <a href='SkColor_Reference#Color'>Color</a> component independently, or may allow all source <a href='undocumented#Pixel'>pixel</a>
|
||||
components to contribute to one destination <a href='undocumented#Pixel'>pixel</a> component.
|
||||
|
||||
<a href='#Blend_Mode'>Blend_Mode</a> does not use adjacent pixels to determine the outcome.
|
||||
|
||||
<a href='#Blend_Mode'>Blend_Mode</a> uses source and read destination <a href='SkColor_Reference#Alpha'>Alpha</a> to determine written
|
||||
destination <a href='SkColor_Reference#Alpha'>Alpha</a>; both source and destination <a href='SkColor_Reference#Alpha'>Alpha</a> may also affect written
|
||||
destination <a href='SkColor_Reference#Color'>Color</a> components.
|
||||
|
||||
Regardless of how <a href='SkColor_Reference#Alpha'>Alpha</a> is encoded in source and destination <a href='undocumented#Pixel'>pixel</a>, nearly all
|
||||
<a href='#Image_Info_Color_Type'>Color_Types</a> treat it as ranging from zero to one. And, nearly all <a href='#Blend_Mode'>Blend_Mode</a>
|
||||
algorithms limit the output so that all results are also zero to one.
|
||||
|
||||
Two exceptions are <a href='SkBlendMode_Reference#SkBlendMode'>SkBlendMode</a>::<a href='#SkBlendMode_kPlus'>kPlus</a> and <a href='SkImageInfo_Reference#kRGBA_F16_SkColorType'>kRGBA_F16_SkColorType</a>.
|
||||
|
||||
<a href='SkBlendMode_Reference#SkBlendMode'>SkBlendMode</a>::<a href='#SkBlendMode_kPlus'>kPlus</a> permits computing <a href='SkColor_Reference#Alpha'>Alpha</a> and <a href='SkColor_Reference#Color'>Color</a> component values larger
|
||||
than one. For <a href='#Image_Info_Color_Type'>Color_Types</a> other than <a href='SkImageInfo_Reference#kRGBA_F16_SkColorType'>kRGBA_F16_SkColorType</a>, resulting <a href='SkColor_Reference#Alpha'>Alpha</a>
|
||||
and component values are clamped to one.
|
||||
|
||||
<a href='SkImageInfo_Reference#kRGBA_F16_SkColorType'>kRGBA_F16_SkColorType</a> permits values outside the zero to one range. It is up
|
||||
to the client to ensure that the result is within the range of zero to one,
|
||||
and therefore well-defined.
|
||||
|
||||
<a name='Porter_Duff'></a>
|
||||
|
||||
<a href='https://graphics.pixar.com/library/Compositing/paper.pdf'>Compositing Digital Images</a></a> describes <a href='#Blend_Mode_Overview_Porter_Duff'>Porter_Duff</a> modes <a href='SkBlendMode_Reference#SkBlendMode'>SkBlendMode</a>::<a href='#SkBlendMode_kClear'>kClear</a> through <a href='SkBlendMode_Reference#SkBlendMode'>SkBlendMode</a>::<a href='#SkBlendMode_kXor'>kXor</a>.
|
||||
|
||||
Drawing a <a href='SkBitmap_Reference#Bitmap'>bitmap</a> with transparency using <a href='#Blend_Mode_Overview_Porter_Duff'>Porter_Duff</a> compositing is free to clear
|
||||
the destination.
|
||||
|
||||
![Porter_Duff](https://fiddle.skia.org/i/819903e0bb125385269948474b6c8a84_raster.png "")
|
||||
|
||||
Draw geometry with transparency using <a href='#Blend_Mode_Overview_Porter_Duff'>Porter_Duff</a> compositing does not combine
|
||||
transparent source pixels, leaving the destination outside the geometry untouched.
|
||||
|
||||
![Porter_Duff](https://fiddle.skia.org/i/8f320c1e94e77046e00f7e9e843caa27_raster.png "")
|
||||
|
||||
<a name='Lighten_Darken'></a>
|
||||
|
||||
Modes <a href='SkBlendMode_Reference#SkBlendMode'>SkBlendMode</a>::<a href='#SkBlendMode_kPlus'>kPlus</a> and <a href='SkBlendMode_Reference#SkBlendMode'>SkBlendMode</a>::<a href='#SkBlendMode_kScreen'>kScreen</a> use
|
||||
simple arithmetic to lighten or darken the destination. Modes
|
||||
<a href='SkBlendMode_Reference#SkBlendMode'>SkBlendMode</a>::<a href='#SkBlendMode_kOverlay'>kOverlay</a> through <a href='SkBlendMode_Reference#SkBlendMode'>SkBlendMode</a>::<a href='#SkBlendMode_kMultiply'>kMultiply</a> use more complicated
|
||||
algorithms to lighten or darken; sometimes one mode does both, as described by
|
||||
<a href='https://en.wikipedia.org/wiki/Blend_modes'>Blend Modes</a></a> .
|
||||
|
||||
![Lighten_Darken](https://fiddle.skia.org/i/23a33fa04cdd0204b2490d05e340f87c_raster.png "")
|
||||
|
||||
<a name='Modulate_Blend'></a>
|
||||
|
||||
<a href='SkBlendMode_Reference#SkBlendMode'>SkBlendMode</a>::<a href='#SkBlendMode_kModulate'>kModulate</a> is a mashup of <a href='SkBlendMode_Reference#SkBlendMode'>SkBlendMode</a>::<a href='#SkBlendMode_kSrcATop'>kSrcATop</a> and <a href='SkBlendMode_Reference#SkBlendMode'>SkBlendMode</a>::<a href='#SkBlendMode_kMultiply'>kMultiply</a>.
|
||||
It multiplies all components, including <a href='SkColor_Reference#Alpha'>Alpha</a>; unlike <a href='SkBlendMode_Reference#SkBlendMode'>SkBlendMode</a>::<a href='#SkBlendMode_kMultiply'>kMultiply</a>, if either
|
||||
source or destination is transparent, result is transparent. <a href='SkBlendMode_Reference#SkBlendMode'>SkBlendMode</a>::<a href='#SkBlendMode_kModulate'>kModulate</a>
|
||||
uses <a href='undocumented#Premultiply'>Premultiplied</a> values to compute the product; <a href='SkBlendMode_Reference#SkBlendMode'>SkBlendMode</a>::<a href='#SkBlendMode_kMultiply'>kMultiply</a> uses <a href='undocumented#Unpremultiply'>Unpremultiplied</a>
|
||||
values to compute the product.
|
||||
|
||||
![Modulate_Blend](https://fiddle.skia.org/i/877f96610ab7638a310432674b04f837_raster.png "")
|
||||
|
||||
<a name='Color_Blends'></a>
|
||||
|
||||
Modes <a href='SkBlendMode_Reference#SkBlendMode'>SkBlendMode</a>::<a href='#SkBlendMode_kHue'>kHue</a>, <a href='SkBlendMode_Reference#SkBlendMode'>SkBlendMode</a>::<a href='#SkBlendMode_kSaturation'>kSaturation</a>, <a href='SkBlendMode_Reference#SkBlendMode'>SkBlendMode</a>::<a href='#SkBlendMode_kColor'>kColor</a>, and
|
||||
<a href='SkBlendMode_Reference#SkBlendMode'>SkBlendMode</a>::<a href='#SkBlendMode_kLuminosity'>kLuminosity</a> convert source and destination pixels using all
|
||||
components <a href='SkColor_Reference#Color'>color</a> information, using
|
||||
<a href='https://www.w3.org/TR/compositing-1/#blendingnonseparable'>non-separable blend modes</a></a> .
|
||||
|
||||
![Color_Blends](https://fiddle.skia.org/i/630fe21aea8369b307231f5bcf8b2d50_raster.png "")
|
||||
|
||||
|
88
site2/docs/user/api/SkPath_Overview.md
Normal file
88
site2/docs/user/api/SkPath_Overview.md
Normal file
@ -0,0 +1,88 @@
|
||||
|
||||
---
|
||||
title: "SkPath Overview"
|
||||
linkTitle: "SkPath Overview"
|
||||
|
||||
weight: 270
|
||||
|
||||
---
|
||||
|
||||
<a href='SkPath_Reference#Path'>Path</a> contains <a href='undocumented#Line'>Lines</a> and <a href='undocumented#Curve'>Curves</a> which can be stroked or filled. <a href='SkPath_Overview#Contour'>Contour</a> is
|
||||
composed of a series of connected <a href='undocumented#Line'>Lines</a> and <a href='undocumented#Curve'>Curves</a>. <a href='SkPath_Reference#Path'>Path</a> may contain zero,
|
||||
one, or more <a href='SkPath_Overview#Contour'>Contours</a>.
|
||||
Each <a href='undocumented#Line'>Line</a> and <a href='undocumented#Curve'>Curve</a> are described by Verb, <a href='SkPoint_Reference#Point'>Points</a>, and optional <a href='#Path_Conic_Weight'>Path_Conic_Weight</a>.
|
||||
|
||||
Each pair of connected <a href='undocumented#Line'>Lines</a> and <a href='undocumented#Curve'>Curves</a> share common <a href='SkPoint_Reference#Point'>Point</a>; for instance, <a href='SkPath_Reference#Path'>Path</a>
|
||||
containing two connected <a href='undocumented#Line'>Lines</a> are described the <a href='#Path_Verb'>Path_Verb</a> sequence:
|
||||
<a href='SkPath_Reference#SkPath'>SkPath</a>::<a href='#SkPath_kMove_Verb'>kMove_Verb</a>, <a href='SkPath_Reference#SkPath'>SkPath</a>::<a href='#SkPath_kLine_Verb'>kLine_Verb</a>, <a href='SkPath_Reference#SkPath'>SkPath</a>::<a href='#SkPath_kLine_Verb'>kLine_Verb</a>; and a <a href='SkPoint_Reference#Point'>Point</a> sequence
|
||||
with three entries, sharing
|
||||
the middle entry as the end of the first <a href='undocumented#Line'>Line</a> and the start of the second <a href='undocumented#Line'>Line</a>.
|
||||
|
||||
<a href='SkPath_Reference#Path'>Path</a> components <a href='undocumented#Arc'>Arc</a>, <a href='SkRect_Reference#Rect'>Rect</a>, <a href='#RRect'>Round_Rect</a>, <a href='undocumented#Circle'>Circle</a>, and <a href='undocumented#Oval'>Oval</a> are composed of
|
||||
<a href='undocumented#Line'>Lines</a> and <a href='undocumented#Curve'>Curves</a> with as many <a href='SkPath_Reference#Verb'>Verbs</a> and <a href='SkPoint_Reference#Point'>Points</a> required
|
||||
for an exact description. Once added to <a href='SkPath_Reference#Path'>Path</a>, these components may lose their
|
||||
identity; although <a href='SkPath_Reference#Path'>Path</a> can be inspected to determine if it describes a single
|
||||
<a href='SkRect_Reference#Rect'>Rect</a>, <a href='undocumented#Oval'>Oval</a>, <a href='#RRect'>Round_Rect</a>, and so on.
|
||||
|
||||
### Example
|
||||
|
||||
<div><fiddle-embed name="93887af0c1dac49521972698cf04069c"><div><a href='SkPath_Reference#Path'>Path</a> contains three <a href='SkPath_Overview#Contour'>Contours</a>: <a href='undocumented#Line'>Line</a>, <a href='undocumented#Circle'>Circle</a>, and <a href='SkPath_Reference#Quad'>Quad</a>. <a href='undocumented#Line'>Line</a> is stroked but
|
||||
not filled. <a href='undocumented#Circle'>Circle</a> is stroked and filled; <a href='undocumented#Circle'>Circle</a> stroke forms a loop. <a href='SkPath_Reference#Quad'>Quad</a>
|
||||
is stroked and filled, but since it is not closed, <a href='SkPath_Reference#Quad'>Quad</a> does not stroke a loop.
|
||||
</div></fiddle-embed></div>
|
||||
|
||||
<a href='SkPath_Reference#Path'>Path</a> contains a <a href='#Path_Fill_Type'>Path_Fill_Type</a> which determines whether overlapping <a href='SkPath_Overview#Contour'>Contours</a>
|
||||
form fills or holes. <a href='#Path_Fill_Type'>Path_Fill_Type</a> also determines whether area inside or outside
|
||||
<a href='undocumented#Line'>Lines</a> and <a href='undocumented#Curve'>Curves</a> is filled.
|
||||
|
||||
### Example
|
||||
|
||||
<div><fiddle-embed name="36a995442c081ee779ecab2962d36e69"><div><a href='SkPath_Reference#Path'>Path</a> is drawn filled, then stroked, then stroked and filled.
|
||||
</div></fiddle-embed></div>
|
||||
|
||||
<a href='SkPath_Reference#Path'>Path</a> contents are never shared. Copying <a href='SkPath_Reference#Path'>Path</a> by value effectively creates
|
||||
a new <a href='SkPath_Reference#Path'>Path</a> independent of the original. Internally, the copy does not duplicate
|
||||
its contents until it is edited, to reduce memory use and improve performance.
|
||||
|
||||
<a name='Contour'></a>
|
||||
|
||||
---
|
||||
|
||||
<a href='SkPath_Overview#Contour'>Contour</a> contains one or more <a href='SkPath_Reference#Verb'>Verbs</a>, and as many <a href='SkPoint_Reference#Point'>Points</a> as
|
||||
are required to satisfy <a href='#Path_Verb_Array'>Path_Verb_Array</a>. First <a href='#Path_Verb'>Path_Verb</a> in <a href='SkPath_Reference#Path'>Path</a> is always
|
||||
<a href='SkPath_Reference#SkPath'>SkPath</a>::<a href='#SkPath_kMove_Verb'>kMove_Verb</a>; each <a href='SkPath_Reference#SkPath'>SkPath</a>::<a href='#SkPath_kMove_Verb'>kMove_Verb</a> that follows starts a new <a href='SkPath_Overview#Contour'>Contour</a>.
|
||||
|
||||
### Example
|
||||
|
||||
<div><fiddle-embed name="0374f2dcd7effeb1dd435205a6c2de6f"><div>Each <a href='SkPath_Reference#SkPath'>SkPath</a>::<a href='#SkPath_moveTo'>moveTo</a> starts a new <a href='SkPath_Overview#Contour'>Contour</a>, and content after <a href='SkPath_Reference#SkPath'>SkPath</a>::<a href='#SkPath_close'>close()</a>
|
||||
also starts a new <a href='SkPath_Overview#Contour'>Contour</a>. Since <a href='SkPath_Reference#SkPath'>SkPath</a>::<a href='#SkPath_conicTo'>conicTo</a> is not preceded by
|
||||
<a href='SkPath_Reference#SkPath'>SkPath</a>::<a href='#SkPath_moveTo'>moveTo</a>, the first <a href='SkPoint_Reference#Point'>Point</a> of the third <a href='SkPath_Overview#Contour'>Contour</a> starts at the last <a href='SkPoint_Reference#Point'>Point</a>
|
||||
of the second <a href='SkPath_Overview#Contour'>Contour</a>.
|
||||
</div></fiddle-embed></div>
|
||||
|
||||
If final <a href='#Path_Verb'>Path_Verb</a> in <a href='SkPath_Overview#Contour'>Contour</a> is <a href='SkPath_Reference#SkPath'>SkPath</a>::<a href='#SkPath_kClose_Verb'>kClose_Verb</a>, <a href='undocumented#Line'>Line</a> connects <a href='#Path_Last_Point'>Path_Last_Point</a> in
|
||||
<a href='SkPath_Overview#Contour'>Contour</a> with first <a href='SkPoint_Reference#Point'>Point</a>. A closed <a href='SkPath_Overview#Contour'>Contour</a>, stroked, draws
|
||||
<a href='#Paint_Stroke_Join'>Paint_Stroke_Join</a> at <a href='#Path_Last_Point'>Path_Last_Point</a> and first <a href='SkPoint_Reference#Point'>Point</a>. Without <a href='SkPath_Reference#SkPath'>SkPath</a>::<a href='#SkPath_kClose_Verb'>kClose_Verb</a>
|
||||
as final Verb, <a href='#Path_Last_Point'>Path_Last_Point</a> and first <a href='SkPoint_Reference#Point'>Point</a> are not connected; <a href='SkPath_Overview#Contour'>Contour</a>
|
||||
remains open. An open <a href='SkPath_Overview#Contour'>Contour</a>, stroked, draws <a href='#Paint_Stroke_Cap'>Paint_Stroke_Cap</a> at
|
||||
<a href='#Path_Last_Point'>Path_Last_Point</a> and first <a href='SkPoint_Reference#Point'>Point</a>.
|
||||
|
||||
### Example
|
||||
|
||||
<div><fiddle-embed name="7a1f39b12d2cd8b7f5b1190879259cb2"><div><a href='SkPath_Reference#Path'>Path</a> is drawn stroked, with an open <a href='SkPath_Overview#Contour'>Contour</a> and a closed <a href='SkPath_Overview#Contour'>Contour</a>.
|
||||
</div></fiddle-embed></div>
|
||||
|
||||
<a name='Contour_Zero_Length'></a>
|
||||
|
||||
---
|
||||
|
||||
<a href='SkPath_Overview#Contour'>Contour</a> length is distance traveled from first <a href='SkPoint_Reference#Point'>Point</a> to <a href='#Path_Last_Point'>Path_Last_Point</a>,
|
||||
plus, if <a href='SkPath_Overview#Contour'>Contour</a> is closed, distance from <a href='#Path_Last_Point'>Path_Last_Point</a> to first <a href='SkPoint_Reference#Point'>Point</a>.
|
||||
Even if <a href='SkPath_Overview#Contour'>Contour</a> length is zero, stroked <a href='undocumented#Line'>Lines</a> are drawn if <a href='#Paint_Stroke_Cap'>Paint_Stroke_Cap</a>
|
||||
makes them visible.
|
||||
|
||||
### Example
|
||||
|
||||
<div><fiddle-embed name="62848df605af6258653d9e16b27d8f7f"></fiddle-embed></div>
|
||||
|
||||
|
71
site2/docs/user/api/_index.md
Normal file
71
site2/docs/user/api/_index.md
Normal file
@ -0,0 +1,71 @@
|
||||
|
||||
---
|
||||
title: "API Reference and Overview"
|
||||
linkTitle: "API Reference and Overview"
|
||||
|
||||
weight: 5
|
||||
|
||||
---
|
||||
|
||||
|
||||
Skia documentation is actively under development.
|
||||
|
||||
Some key classes are:
|
||||
|
||||
* [SkAutoCanvasRestore](https://api.skia.org/classSkAutoCanvasRestore.html#details) - Canvas save stack manager
|
||||
* [SkBitmap](https://api.skia.org/classSkBitmap.html#details) - two-dimensional raster pixel array
|
||||
* [SkBlendMode](https://api.skia.org/SkBlendMode_8h.html) - pixel color arithmetic
|
||||
* [SkCanvas](https://api.skia.org/classSkCanvas.html#details) - drawing context
|
||||
* [SkColor](https://api.skia.org/SkColor_8h.html) - color encoding using integer numbers
|
||||
* [SkFont](https://api.skia.org/classSkFont.html#details) - text style and typeface
|
||||
* [SkImage](https://api.skia.org/classSkImage.html#details) - two dimensional array of pixels to draw
|
||||
* [SkImageInfo](https://api.skia.org/structSkImageInfo.html#details) - pixel dimensions and characteristics
|
||||
* [SkIPoint](https://api.skia.org/structSkIPoint.html#details) - two integer coordinates
|
||||
* [SkIRect](https://api.skia.org/structSkIRect.html#details) - integer rectangle
|
||||
* [SkMatrix](https://api.skia.org/classSkMatrix.html#details) - 3x3 transformation matrix
|
||||
* [SkPaint](https://api.skia.org/classSkPaint.html#details) - color, stroke, font, effects
|
||||
* [SkPath](https://api.skia.org/classSkPath.html#details) - sequence of connected lines and curves
|
||||
* [SkPicture](https://api.skia.org/classSkPicture.html#details) - sequence of drawing commands
|
||||
* [SkPixmap](https://api.skia.org/classSkPixmap.html#details) - pixel map: image info and pixel address
|
||||
* [SkPoint](https://api.skia.org/structSkPoint.html#details) - two floating point coordinates
|
||||
* [SkRRect](https://api.skia.org/classSkRRect.html#details) - floating point rounded rectangle
|
||||
* [SkRect](https://api.skia.org/structSkRect.html#details) - floating point rectangle
|
||||
* [SkRegion](https://api.skia.org/classSkRegion.html#details) - compressed clipping mask
|
||||
* [SkSurface](https://api.skia.org/classSkSurface.html#details) - drawing destination
|
||||
* [SkTextBlob](https://api.skia.org/classSkTextBlob.html#details) - runs of glyphs
|
||||
* [SkTextBlobBuilder](https://api.skia.org/classSkTextBlobBuilder.html#details) - constructor for runs of glyphs
|
||||
|
||||
All public APIs are indexed by Doxygen.
|
||||
|
||||
* [Skia Doxygen](https://api.skia.org)
|
||||
|
||||
## Overview
|
||||
|
||||
Skia is organized around the `SkCanvas` object. It is the host for the
|
||||
"draw" calls: `drawRect`, `drawPath`, `drawText`, etc. Each of these
|
||||
has two components: the primitive being drawn (`SkRect`, `SkPath`, etc.)
|
||||
and color/style attributes (`SkPaint`).
|
||||
|
||||
<!--?prettify lang=cc?-->
|
||||
|
||||
canvas->drawRect(rect, paint);
|
||||
|
||||
The paint holds much of the state describing how the rectangle (in
|
||||
this case) is drawn: what color it is, if it is filled or stroked, how
|
||||
it should blend with what was previously drawn.
|
||||
|
||||
The canvas holds relatively little state. It points to the actual
|
||||
pixels being drawn, and it maintains a stack of matrices and
|
||||
clips. Thus in the above call, the canvas' current matrix may
|
||||
transform the coordinates of the rectangle (translation, rotation,
|
||||
skewing, perspective), and the canvas' current clip may restrict where
|
||||
on the canvas the rectangle will be drawn, but all other stylistic
|
||||
attributes of the drawing are controlled by the paint.
|
||||
|
||||
Using the SkCanvas API:
|
||||
|
||||
1. [SkCanvas Overview](/user/api/skcanvas_overview) - the drawing context
|
||||
2. [SkPaint Overview](/user/api/skpaint_overview) - color, stroke, font, effects
|
||||
3. [SkCanvas Creation](/user/api/skcanvas_creation)
|
||||
|
||||
|
267
site2/docs/user/api/skcanvas_creation.md
Normal file
267
site2/docs/user/api/skcanvas_creation.md
Normal file
@ -0,0 +1,267 @@
|
||||
---
|
||||
title: 'SkCanvas Creation'
|
||||
linkTitle: 'SkCanvas Creation'
|
||||
|
||||
weight: 250
|
||||
---
|
||||
|
||||
First, read about [the SkCanvas API](skcanvas_overview).
|
||||
|
||||
Skia has multiple backends which receive SkCanvas drawing commands, including:
|
||||
|
||||
- [Raster](#raster) - CPU-only.
|
||||
- [GPU](#gpu) - Skia's GPU-accelerated backend.
|
||||
- [SkPDF](#skpdf) - PDF document creation.
|
||||
- [SkPicture](#skpicture) - Skia's display list format.
|
||||
- [NullCanvas](#nullcanvas) - Useful for testing only.
|
||||
- [SkXPS](#skxps) - Experimental XPS backend.
|
||||
- [SkSVG](#sksvg) - Experimental SVG backend.
|
||||
|
||||
Each backend has a unique way of creating a SkCanvas. This page gives an example
|
||||
for each:
|
||||
|
||||
<span id="raster"></span> Raster
|
||||
|
||||
---
|
||||
|
||||
The raster backend draws to a block of memory. This memory can be managed by
|
||||
Skia or by the client.
|
||||
|
||||
The recommended way of creating a canvas for the Raster and Ganesh backends is
|
||||
to use a `SkSurface`, which is an object that manages the memory into which the
|
||||
canvas commands are drawn.
|
||||
|
||||
<!--?prettify lang=cc?-->
|
||||
|
||||
#include "SkData.h"
|
||||
#include "SkImage.h"
|
||||
#include "SkStream.h"
|
||||
#include "SkSurface.h"
|
||||
void raster(int width, int height,
|
||||
void (*draw)(SkCanvas*),
|
||||
const char* path) {
|
||||
sk_sp<SkSurface> rasterSurface =
|
||||
SkSurface::MakeRasterN32Premul(width, height);
|
||||
SkCanvas* rasterCanvas = rasterSurface->getCanvas();
|
||||
draw(rasterCanvas);
|
||||
sk_sp<SkImage> img(rasterSurface->makeImageSnapshot());
|
||||
if (!img) { return; }
|
||||
sk_sp<SkData> png(img->encode());
|
||||
if (!png) { return; }
|
||||
SkFILEWStream out(path);
|
||||
(void)out.write(png->data(), png->size());
|
||||
}
|
||||
|
||||
Alternatively, we could have specified the memory for the surface explicitly,
|
||||
instead of asking Skia to manage it.
|
||||
|
||||
<!--?prettify lang=cc?-->
|
||||
|
||||
#include <vector>
|
||||
#include "SkSurface.h"
|
||||
std::vector<char> raster_direct(int width, int height,
|
||||
void (*draw)(SkCanvas*)) {
|
||||
SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
|
||||
size_t rowBytes = info.minRowBytes();
|
||||
size_t size = info.getSafeSize(rowBytes);
|
||||
std::vector<char> pixelMemory(size); // allocate memory
|
||||
sk_sp<SkSurface> surface =
|
||||
SkSurface::MakeRasterDirect(
|
||||
info, &pixelMemory[0], rowBytes);
|
||||
SkCanvas* canvas = surface->getCanvas();
|
||||
draw(canvas);
|
||||
return pixelMemory;
|
||||
}
|
||||
|
||||
<span id="gpu"></span> GPU
|
||||
|
||||
---
|
||||
|
||||
GPU Surfaces must have a `GrContext` object which manages the GPU context, and
|
||||
related caches for textures and fonts. GrContexts are matched one to one with
|
||||
OpenGL contexts or Vulkan devices. That is, all SkSurfaces that will be rendered
|
||||
to using the same OpenGL context or Vulkan device should share a GrContext. Skia
|
||||
does not create a OpenGL context or Vulkan device for you. In OpenGL mode it
|
||||
also assumes that the correct OpenGL context has been made current to the
|
||||
current thread when Skia calls are made.
|
||||
|
||||
<!--?prettify lang=cc?-->
|
||||
|
||||
#include "GrDirectContext.h"
|
||||
#include "gl/GrGLInterface.h"
|
||||
#include "SkData.h"
|
||||
#include "SkImage.h"
|
||||
#include "SkStream.h"
|
||||
#include "SkSurface.h"
|
||||
|
||||
void gl_example(int width, int height, void (*draw)(SkCanvas*), const char* path) {
|
||||
// You've already created your OpenGL context and bound it.
|
||||
const GrGLInterface* interface = nullptr;
|
||||
// Leaving interface as null makes Skia extract pointers to OpenGL functions for the current
|
||||
// context in a platform-specific way. Alternatively, you may create your own GrGLInterface and
|
||||
// initialize it however you like to attach to an alternate OpenGL implementation or intercept
|
||||
// Skia's OpenGL calls.
|
||||
sk_sp<GrContext> context = GrContext::MakeGL(interface);
|
||||
SkImageInfo info = SkImageInfo:: MakeN32Premul(width, height);
|
||||
sk_sp<SkSurface> gpuSurface(
|
||||
SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info));
|
||||
if (!gpuSurface) {
|
||||
SkDebugf("SkSurface::MakeRenderTarget returned null\n");
|
||||
return;
|
||||
}
|
||||
SkCanvas* gpuCanvas = gpuSurface->getCanvas();
|
||||
draw(gpuCanvas);
|
||||
sk_sp<SkImage> img(gpuSurface->makeImageSnapshot());
|
||||
if (!img) { return; }
|
||||
sk_sp<SkData> png(img->encode());
|
||||
if (!png) { return; }
|
||||
SkFILEWStream out(path);
|
||||
(void)out.write(png->data(), png->size());
|
||||
}
|
||||
|
||||
<span id="skpdf"></span> SkPDF
|
||||
|
||||
---
|
||||
|
||||
The SkPDF backend uses `SkDocument` instead of `SkSurface`, since a document
|
||||
must include multiple pages.
|
||||
|
||||
<!--?prettify lang=cc?-->
|
||||
|
||||
#include "SkPDFDocument.h"
|
||||
#include "SkStream.h"
|
||||
void skpdf(int width, int height,
|
||||
void (*draw)(SkCanvas*),
|
||||
const char* path) {
|
||||
SkFILEWStream pdfStream(path);
|
||||
auto pdfDoc = SkPDF::MakeDocument(&pdfStream);
|
||||
SkCanvas* pdfCanvas = pdfDoc->beginPage(SkIntToScalar(width),
|
||||
SkIntToScalar(height));
|
||||
draw(pdfCanvas);
|
||||
pdfDoc->close();
|
||||
}
|
||||
|
||||
<span id="skpicture"></span> SkPicture
|
||||
|
||||
---
|
||||
|
||||
The SkPicture backend uses SkPictureRecorder instead of SkSurface.
|
||||
|
||||
<!--?prettify lang=cc?-->
|
||||
|
||||
#include "SkPictureRecorder.h"
|
||||
#include "SkPicture.h"
|
||||
#include "SkStream.h"
|
||||
void picture(int width, int height,
|
||||
void (*draw)(SkCanvas*),
|
||||
const char* path) {
|
||||
SkPictureRecorder recorder;
|
||||
SkCanvas* recordingCanvas = recorder.beginRecording(SkIntToScalar(width),
|
||||
SkIntToScalar(height));
|
||||
draw(recordingCanvas);
|
||||
sk_sp<SkPicture> picture = recorder.finishRecordingAsPicture();
|
||||
SkFILEWStream skpStream(path);
|
||||
// Open SKP files with `viewer --skps PATH_TO_SKP --slide SKP_FILE`
|
||||
picture->serialize(&skpStream);
|
||||
}
|
||||
|
||||
<span id="nullcanvas"></span> NullCanvas
|
||||
|
||||
---
|
||||
|
||||
The null canvas is a canvas that ignores all drawing commands and does nothing.
|
||||
|
||||
<!--?prettify lang=cc?-->
|
||||
|
||||
#include "SkNullCanvas.h"
|
||||
void null_canvas_example(int, int, void (*draw)(SkCanvas*), const char*) {
|
||||
std::unique_ptr<SkCanvas> nullCanvas = SkMakeNullCanvas();
|
||||
draw(nullCanvas.get()); // NoOp
|
||||
}
|
||||
|
||||
<span id="skxps"></span> SkXPS
|
||||
|
||||
---
|
||||
|
||||
The (_still experimental_) SkXPS canvas writes into an XPS document.
|
||||
|
||||
<!--?prettify lang=cc?-->
|
||||
|
||||
#include "SkDocument.h"
|
||||
#include "SkStream.h"
|
||||
#ifdef SK_BUILD_FOR_WIN
|
||||
void skxps(IXpsOMObjectFactory* factory;
|
||||
int width, int height,
|
||||
void (*draw)(SkCanvas*),
|
||||
const char* path) {
|
||||
SkFILEWStream xpsStream(path);
|
||||
sk_sp<SkDocument> xpsDoc = SkDocument::MakeXPS(&pdfStream, factory);
|
||||
SkCanvas* xpsCanvas = xpsDoc->beginPage(SkIntToScalar(width),
|
||||
SkIntToScalar(height));
|
||||
draw(xpsCanvas);
|
||||
xpsDoc->close();
|
||||
}
|
||||
#endif
|
||||
|
||||
<span id="sksvg"></span> SkSVG
|
||||
|
||||
---
|
||||
|
||||
The (_still experimental_) SkSVG canvas writes into an SVG document.
|
||||
|
||||
<!--?prettify lang=cc?-->
|
||||
|
||||
#include "SkStream.h"
|
||||
#include "SkSVGCanvas.h"
|
||||
#include "SkXMLWriter.h"
|
||||
void sksvg(int width, int height,
|
||||
void (*draw)(SkCanvas*),
|
||||
const char* path) {
|
||||
SkFILEWStream svgStream(path);
|
||||
std::unique_ptr<SkXMLWriter> xmlWriter(
|
||||
new SkXMLStreamWriter(&svgStream));
|
||||
SkRect bounds = SkRect::MakeIWH(width, height);
|
||||
std::unique_ptr<SkCanvas> svgCanvas =
|
||||
SkSVGCanvas::Make(bounds, xmlWriter.get());
|
||||
draw(svgCanvas.get());
|
||||
}
|
||||
|
||||
<span id="example"></span> Example
|
||||
|
||||
---
|
||||
|
||||
To try this code out, make a
|
||||
[new unit test using instructions here](/dev/testing/tests) and wrap these
|
||||
functions together:
|
||||
|
||||
<!--?prettify lang=cc?-->
|
||||
|
||||
#include "SkCanvas.h"
|
||||
#include "SkPath.h"
|
||||
#include "Test.h"
|
||||
void example(SkCanvas* canvas) {
|
||||
const SkScalar scale = 256.0f;
|
||||
const SkScalar R = 0.45f * scale;
|
||||
const SkScalar TAU = 6.2831853f;
|
||||
SkPath path;
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
SkScalar theta = 2 * i * TAU / 5;
|
||||
if (i == 0) {
|
||||
path.moveTo(R * cos(theta), R * sin(theta));
|
||||
} else {
|
||||
path.lineTo(R * cos(theta), R * sin(theta));
|
||||
}
|
||||
}
|
||||
path.close();
|
||||
SkPaint p;
|
||||
p.setAntiAlias(true);
|
||||
canvas->clear(SK_ColorWHITE);
|
||||
canvas->translate(0.5f * scale, 0.5f * scale);
|
||||
canvas->drawPath(path, p);
|
||||
}
|
||||
DEF_TEST(FourBackends, r) {
|
||||
raster( 256, 256, example, "out_raster.png" );
|
||||
gl_example( 256, 256, example, "out_gpu.png" );
|
||||
skpdf( 256, 256, example, "out_skpdf.pdf" );
|
||||
picture( 256, 256, example, "out_picture.skp");
|
||||
}
|
78
site2/docs/user/api/skcanvas_overview.md
Normal file
78
site2/docs/user/api/skcanvas_overview.md
Normal file
@ -0,0 +1,78 @@
|
||||
|
||||
---
|
||||
title: "SkCanvas Overview"
|
||||
linkTitle: "SkCanvas Overview"
|
||||
|
||||
weight: 240
|
||||
|
||||
---
|
||||
|
||||
|
||||
*The drawing context*
|
||||
|
||||
<!-- Updated Mar 4, 2011 -->
|
||||
|
||||
Preview
|
||||
-------
|
||||
|
||||
Here is an example of a set of drawing commands to draw a filled
|
||||
heptagram. This function can be cut and pasted into
|
||||
[fiddle.skia.org](https://fiddle.skia.org/).
|
||||
|
||||
<fiddle-embed name='@skcanvas_star'></fiddle-embed>
|
||||
|
||||
Details
|
||||
-------
|
||||
|
||||
SkCanvas is the drawing context for Skia. It knows where to direct the
|
||||
drawing (i.e. where the screen of offscreen pixels are), and maintains
|
||||
a stack of matrices and clips. Note however, that unlike similar
|
||||
contexts in other APIs like postscript, cairo, or awt, Skia does not
|
||||
store any other drawing attributes in the context (e.g. color, pen
|
||||
size). Rather, these are specified explicitly in each draw call, via a
|
||||
SkPaint.
|
||||
|
||||
<fiddle-embed name='@skcanvas_square'></fiddle-embed>
|
||||
|
||||
The code above will draw a rectangle rotated by 45 degrees. Exactly
|
||||
what color and style the rect will be drawn in is described by the
|
||||
paint, not the canvas.
|
||||
|
||||
Check out more detailed info on [creating a SkCanvas object](canvas).
|
||||
|
||||
To begin with, we might want to erase the entire canvas. We can do
|
||||
this by drawing an enormous rectangle, but there are easier ways to do
|
||||
it.
|
||||
|
||||
<!--?prettify lang=cc?-->
|
||||
|
||||
void draw(SkCanvas* canvas) {
|
||||
SkPaint paint;
|
||||
paint.setColor(SK_ColorWHITE);
|
||||
canvas->drawPaint(paint);
|
||||
}
|
||||
|
||||
This fills the entire canvas (though respecting the current clip of
|
||||
course) with whatever color or shader (and xfermode) is specified by
|
||||
the paint. If there is a shader in the paint, then it will respect the
|
||||
current matrix on the canvas as well (see SkShader). If you just want
|
||||
to draw a color (with an optional xfermode), you can just call
|
||||
drawColor(), and save yourself having to allocate a paint.
|
||||
|
||||
<!--?prettify lang=cc?-->
|
||||
|
||||
void draw(SkCanvas* canvas) {
|
||||
canvas->drawColor(SK_ColorWHITE);
|
||||
}
|
||||
|
||||
All of the other draw APIs are similar, each one ending with a paint
|
||||
parameter.
|
||||
|
||||
<fiddle-embed name='@skcanvas_paint'></fiddle-embed>
|
||||
|
||||
In some of the calls, we pass a pointer, rather than a reference, to
|
||||
the paint. In those instances, the paint parameter may be null. In all
|
||||
other cases the paint parameter is required.
|
||||
|
||||
Next: [SkPaint](/user/api/skpaint_overview)
|
||||
|
209
site2/docs/user/api/skpaint_overview.md
Normal file
209
site2/docs/user/api/skpaint_overview.md
Normal file
@ -0,0 +1,209 @@
|
||||
|
||||
---
|
||||
title: "SkPaint Overview"
|
||||
linkTitle: "SkPaint Overview"
|
||||
|
||||
weight: 280
|
||||
|
||||
---
|
||||
|
||||
<span id="top"></span>
|
||||
|
||||
*color, stroke, font, effects*
|
||||
|
||||
<div class="float">
|
||||
<ul>
|
||||
<li><a href="#">SkPaint</a></li>
|
||||
<li><a href="#SkXfermode">SkXfermode</a></li>
|
||||
<li><a href="#SkShader">SkShader</a></li>
|
||||
<li><a href="#SkMaskFilter">SkMaskFilter</a></li>
|
||||
<li><a href="#SkColorFilter">SkColorFilter</a></li>
|
||||
<li><a href="#SkPathEffect">SkPathEffect</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
Anytime you draw something in Skia, and want to specify what color it
|
||||
is, or how it blends with the background, or what style or font to
|
||||
draw it in, you specify those attributes in a paint.
|
||||
|
||||
Unlike `SkCanvas`, paints do not maintain an internal stack of state
|
||||
(i.e. there is no save/restore on a paint). However, paints are
|
||||
relatively light-weight, so the client may create and maintain any
|
||||
number of paint objects, each set up for a particular use. Factoring
|
||||
all of these color and stylistic attributes out of the canvas state,
|
||||
and into (multiple) paint objects, allows canvas' save/restore to be
|
||||
that much more efficient, as all they have to do is maintain the stack
|
||||
of matrix and clip settings.
|
||||
|
||||
<fiddle-embed name='@skpaint_skia'></fiddle-embed>
|
||||
|
||||
This shows three different paints, each set up to draw in a different
|
||||
style. Now the caller can intermix these paints freely, either using
|
||||
them as is, or modifying them as the drawing proceeds.
|
||||
|
||||
<fiddle-embed name='@skpaint_mix'></fiddle-embed>
|
||||
|
||||
Beyond simple attributes such as color, strokes, and text values,
|
||||
paints support effects. These are subclasses of different aspects of
|
||||
the drawing pipeline, that when referenced by a paint (each of them is
|
||||
reference-counted), are called to override some part of the drawing
|
||||
pipeline.
|
||||
|
||||
For example, to draw using a gradient instead of a single color,
|
||||
assign a SkShader to the paint.
|
||||
|
||||
<fiddle-embed name='@skpaint_shader'></fiddle-embed>
|
||||
|
||||
Now, anything drawn with that paint will be drawn with the gradient
|
||||
specified in the call to `MakeLinear()`. The shader object that is
|
||||
returned is reference-counted. Whenever any effects object, like a
|
||||
shader, is assigned to a paint, its reference-count is increased by
|
||||
the paint. To balance this, the caller in the above example calls
|
||||
`unref()` on the shader once it has assigned it to the paint. Now the
|
||||
paint is the only "owner" of that shader, and it will automatically
|
||||
call `unref()` on the shader when either the paint goes out of scope, or
|
||||
if another shader (or null) is assigned to it.
|
||||
|
||||
There are 6 types of effects that can be assigned to a paint:
|
||||
|
||||
* **SkPathEffect** - modifications to the geometry (path) before it
|
||||
generates an alpha mask (e.g. dashing)
|
||||
* **SkRasterizer** - composing custom mask layers (e.g. shadows)
|
||||
* **SkMaskFilter** - modifications to the alpha mask before it is
|
||||
colorized and drawn (e.g. blur)
|
||||
* **SkShader** - e.g. gradients (linear, radial, sweep), bitmap patterns
|
||||
(clamp, repeat, mirror)
|
||||
* **SkColorFilter** - modify the source color(s) before applying the
|
||||
xfermode (e.g. color matrix)
|
||||
* **SkXfermode** - e.g. porter-duff transfermodes, blend modes
|
||||
|
||||
Paints also hold a reference to a SkTypeface. The typeface represents
|
||||
a specific font style, to be used for measuring and drawing
|
||||
text. Speaking of which, paints are used not only for drawing text,
|
||||
but also for measuring it.
|
||||
|
||||
<!--?prettify lang=cc?-->
|
||||
|
||||
paint.measureText(...);
|
||||
paint.getTextBounds(...);
|
||||
paint.textToGlyphs(...);
|
||||
paint.getFontMetrics(...);
|
||||
|
||||
<span id="SkXfermode"></span>
|
||||
|
||||
SkXfermode
|
||||
----------
|
||||
|
||||
The following example demonstrates all of the Skia's standard transfer
|
||||
modes. In this example the source is a solid magenta color with a
|
||||
horizontal alpha gradient and the destination is a solid cyan color
|
||||
with a vertical alpha gradient.
|
||||
|
||||
<fiddle-embed name='@skpaint_xfer'></fiddle-embed>
|
||||
|
||||
<span id="SkShader"></span>
|
||||
|
||||
SkShader
|
||||
--------
|
||||
|
||||
Several shaders are defined (besides the linear gradient already mentioned):
|
||||
|
||||
* Bitmap Shader
|
||||
|
||||
<fiddle-embed name='@skpaint_bitmap_shader'></fiddle-embed>
|
||||
|
||||
* Radial Gradient Shader
|
||||
|
||||
<fiddle-embed name='@skpaint_radial'></fiddle-embed>
|
||||
|
||||
* Two-Point Conical Gradient Shader
|
||||
|
||||
<fiddle-embed name='@skpaint_2pt'></fiddle-embed>
|
||||
|
||||
|
||||
* Sweep Gradient Shader
|
||||
|
||||
<fiddle-embed name='@skpaint_sweep'></fiddle-embed>
|
||||
|
||||
* Fractal Perlin Noise Shader
|
||||
|
||||
<fiddle-embed name='@skpaint_perlin'></fiddle-embed>
|
||||
|
||||
* Turbulence Perlin Noise Shader
|
||||
|
||||
<fiddle-embed name='@skpaint_turb'></fiddle-embed>
|
||||
|
||||
* Compose Shader
|
||||
|
||||
<fiddle-embed name='@skpaint_compose_shader'></fiddle-embed>
|
||||
|
||||
|
||||
<span id="SkMaskFilter"></span>
|
||||
|
||||
SkMaskFilter
|
||||
------------
|
||||
|
||||
* Blur Mask Filter
|
||||
|
||||
<fiddle-embed name='@skpaint_blur_mask_filter'></fiddle-embed>
|
||||
|
||||
|
||||
<span id="SkColorFilter"></span>
|
||||
|
||||
SkColorFilter
|
||||
-------------
|
||||
|
||||
* Color Matrix Color Filter
|
||||
|
||||
<fiddle-embed name='@skpaint_matrix_color_filter'></fiddle-embed>
|
||||
|
||||
* Color Table Color Filter
|
||||
|
||||
<fiddle-embed name='@skpaint_color_table_filter'></fiddle-embed>
|
||||
|
||||
<span id="SkPathEffect"></span>
|
||||
|
||||
SkPathEffect
|
||||
------------
|
||||
|
||||
* SkPath2DPathEffect: Stamp the specified path to fill the shape,
|
||||
using the matrix to define the latice.
|
||||
|
||||
<fiddle-embed name='@skpaint_path_2d_path_effect'></fiddle-embed>
|
||||
|
||||
* SkLine2DPathEffect: a special case of SkPath2DPathEffect where the
|
||||
path is a straight line to be stroked, not a path to be filled.
|
||||
|
||||
<fiddle-embed name='@skpaint_line_2d_path_effect'></fiddle-embed>
|
||||
|
||||
* SkPath1DPathEffect: create dash-like effects by replicating the specified path along the drawn path.
|
||||
|
||||
<fiddle-embed name='@skpaint_path_1d_path_effect'></fiddle-embed>
|
||||
|
||||
* SkCornerPathEffect: a path effect that can turn sharp corners into
|
||||
various treatments (e.g. rounded corners).
|
||||
|
||||
<fiddle-embed name='@skpaint_corner_path_effects'></fiddle-embed>
|
||||
|
||||
* SkDashPathEffect: a path effect that implements dashing.
|
||||
|
||||
<fiddle-embed name='@skpaint_dash_path_effect'></fiddle-embed>
|
||||
|
||||
* SkDiscretePathEffect: This path effect chops a path into discrete
|
||||
segments, and randomly displaces them.
|
||||
|
||||
<fiddle-embed name='@skpaint_discrete_path_effect'></fiddle-embed>
|
||||
|
||||
* SkComposePathEffect: a pathEffect whose effect is to apply
|
||||
first the inner pathEffect and the the outer pathEffect (i.e.
|
||||
outer(inner(path))).
|
||||
|
||||
<fiddle-embed name='@skpaint_compose_path_effect'></fiddle-embed>
|
||||
|
||||
* SkSumPathEffect: a pathEffect whose effect is to apply two effects,
|
||||
in sequence (i.e. first(path) + second(path)).
|
||||
|
||||
<fiddle-embed name='@skpaint_sum_path_effect'></fiddle-embed>
|
||||
|
||||
|
414
site2/docs/user/build.md
Normal file
414
site2/docs/user/build.md
Normal file
@ -0,0 +1,414 @@
|
||||
---
|
||||
title: 'How to build Skia'
|
||||
linkTitle: 'How to build Skia'
|
||||
|
||||
weight: 20
|
||||
---
|
||||
|
||||
Make sure you have first followed the
|
||||
[instructions to download Skia](./download).
|
||||
|
||||
Skia uses [GN](https://chromium.googlesource.com/chromium/src/tools/gn/) to
|
||||
configure its builds.
|
||||
|
||||
- [`is_official_build` and Third-party Dependencies](#third-party)
|
||||
- [Supported and Preferred Compilers](#compilers)
|
||||
- [Quickstart](#quick)
|
||||
- [Android](#android)
|
||||
- [ChromeOS](#cros)
|
||||
- [Mac](#macos)
|
||||
- [iOS](#ios)
|
||||
- [Windows](#windows)
|
||||
- [Windows ARM64](#win-arm64)
|
||||
- [CMake](#cmake)
|
||||
|
||||
## <span id="third-party">`is_official_build` and Third-party Dependencies</span>
|
||||
|
||||
Most users of Skia should set `is_official_build=true`, and most developers
|
||||
should leave it to its `false` default.
|
||||
|
||||
This mode configures Skia in a way that's suitable to ship: an optimized build
|
||||
with no debug symbols, dynamically linked against its third-party dependencies
|
||||
using the ordinary library search path.
|
||||
|
||||
In contrast, the developer-oriented default is an unoptimized build with full
|
||||
debug symbols and all third-party dependencies built from source and embedded
|
||||
into libskia. This is how we do all our manual and automated testing.
|
||||
|
||||
Skia offers several features that make use of third-party libraries, like
|
||||
libpng, libwebp, or libjpeg-turbo to decode images, or ICU and sftnly to subset
|
||||
fonts. All these third-party dependencies are optional and can be controlled by
|
||||
a GN argument that looks something like `skia_use_foo` for appropriate `foo`.
|
||||
|
||||
If `skia_use_foo` is enabled, enabling `skia_use_system_foo` will build and link
|
||||
Skia against the headers and libraries found on the system paths.
|
||||
`is_official_build=true` enables all `skia_use_system_foo` by default. You can
|
||||
use `extra_cflags` and `extra_ldflags` to add include or library paths if
|
||||
needed.
|
||||
|
||||
## <span id="compilers">Supported and Preferred Compilers</span>
|
||||
|
||||
While Skia should compile with GCC, MSVC, and other compilers, a number of
|
||||
routines in Skia's software backend have been written to run fastest when
|
||||
compiled with Clang. If you depend on software rasterization, image decoding, or
|
||||
color space conversion and compile Skia with a compiler other than Clang, you
|
||||
will see dramatically worse performance. This choice was only a matter of
|
||||
prioritization; there is nothing fundamentally wrong with non-Clang compilers.
|
||||
So if this is a serious issue for you, please let us know on the mailing list.
|
||||
|
||||
Skia makes use of C++17 language features (compiles with `-std=c++17` flag) and
|
||||
thus requires a C++17 compatible compiler. Clang 5 and later implement all of
|
||||
the features of the c++17 standard. Older compilers that lack C++17 support may
|
||||
produce non-obvious compilation errors. You can configure your build to use
|
||||
specific executables for `cc` and `cxx` invocations using e.g.
|
||||
`--args='cc="clang-6.0" cxx="clang++6.0"'` GN build arguments, as illustrated in
|
||||
[Quickstart](#quick). This can be useful for building Skia without needing to
|
||||
modify your machine's default compiler toolchain.
|
||||
|
||||
## <span id="quick">Quickstart</span>
|
||||
|
||||
Run `gn gen` to generate your build files. As arguments to `gn gen`, pass a name
|
||||
for your build directory, and optionally `--args=` to configure the build type.
|
||||
|
||||
To build Skia as a static library in a build directory named `out/Static`:
|
||||
|
||||
```
|
||||
bin/gn gen out/Static --args='is_official_build=true'
|
||||
```
|
||||
|
||||
To build Skia as a shared library (DLL) in a build directory named `out/Shared`:
|
||||
|
||||
```
|
||||
bin/gn gen out/Shared --args='is_official_build=true is_component_build=true'
|
||||
```
|
||||
|
||||
If you find that you don't have `bin/gn`, make sure you've run:
|
||||
|
||||
```
|
||||
python2 tools/git-sync-deps
|
||||
```
|
||||
|
||||
For a list of available build arguments, take a look at `gn/skia.gni`, or run:
|
||||
|
||||
```
|
||||
bin/gn args out/Debug --list
|
||||
```
|
||||
|
||||
GN allows multiple build folders to coexist; each build can be configured
|
||||
separately as desired. For example:
|
||||
|
||||
```
|
||||
bin/gn gen out/Debug
|
||||
bin/gn gen out/Release --args='is_debug=false'
|
||||
bin/gn gen out/Clang --args='cc="clang" cxx="clang++"'
|
||||
bin/gn gen out/Cached --args='cc_wrapper="ccache"'
|
||||
bin/gn gen out/RTTI --args='extra_cflags_cc=["-frtti"]'
|
||||
```
|
||||
|
||||
Once you have generated your build files, run Ninja to compile and link Skia:
|
||||
|
||||
```
|
||||
ninja -C out/Static
|
||||
```
|
||||
|
||||
If some header files are missing, install the corresponding dependencies:
|
||||
|
||||
```
|
||||
tools/install_dependencies.sh
|
||||
```
|
||||
|
||||
To pull new changes and rebuild:
|
||||
|
||||
```
|
||||
git pull
|
||||
python tools/git-sync-deps
|
||||
ninja -C out/Static
|
||||
```
|
||||
|
||||
## <span id="android">Android</span>
|
||||
|
||||
To build Skia for Android you need an
|
||||
[Android NDK](https://developer.android.com/ndk/index.html).
|
||||
|
||||
If you do not have an NDK and have access to CIPD, you can use one of these
|
||||
commands to fetch the NDK our bots use:
|
||||
|
||||
```
|
||||
python2 infra/bots/assets/android_ndk_linux/download.py -t /tmp/ndk
|
||||
python2 infra/bots/assets/android_ndk_darwin/download.py -t /tmp/ndk
|
||||
python2 infra/bots/assets/android_ndk_windows/download.py -t C:/ndk
|
||||
```
|
||||
|
||||
When generating your GN build files, pass the path to your `ndk` and your
|
||||
desired `target_cpu`:
|
||||
|
||||
```
|
||||
bin/gn gen out/arm --args='ndk="/tmp/ndk" target_cpu="arm"'
|
||||
bin/gn gen out/arm64 --args='ndk="/tmp/ndk" target_cpu="arm64"'
|
||||
bin/gn gen out/x64 --args='ndk="/tmp/ndk" target_cpu="x64"'
|
||||
bin/gn gen out/x86 --args='ndk="/tmp/ndk" target_cpu="x86"'
|
||||
```
|
||||
|
||||
Other arguments like `is_debug` and `is_component_build` continue to work.
|
||||
Tweaking `ndk_api` gives you access to newer Android features like Vulkan.
|
||||
|
||||
To test on an Android device, push the binary and `resources` over, and run it
|
||||
as normal. You may find `bin/droid` convenient.
|
||||
|
||||
```
|
||||
ninja -C out/arm64
|
||||
adb push out/arm64/dm /data/local/tmp
|
||||
adb push resources /data/local/tmp
|
||||
adb shell "cd /data/local/tmp; ./dm --src gm --config gl"
|
||||
```
|
||||
|
||||
## <span id="cros">ChromeOS</span>
|
||||
|
||||
To cross-compile Skia for arm ChromeOS devices the following is needed:
|
||||
|
||||
- Clang 4 or newer
|
||||
- An armhf sysroot
|
||||
- The (E)GL lib files on the arm chromebook to link against.
|
||||
|
||||
To compile Skia for an x86 ChromeOS device, one only needs Clang and the lib
|
||||
files.
|
||||
|
||||
If you have access to CIPD, you can fetch all of these as follows:
|
||||
|
||||
```
|
||||
python2 infra/bots/assets/clang_linux/download.py -t /opt/clang
|
||||
python2 infra/bots/assets/armhf_sysroot/download.py -t /opt/armhf_sysroot
|
||||
python2 infra/bots/assets/chromebook_arm_gles/download.py -t /opt/chromebook_arm_gles
|
||||
python2 infra/bots/assets/chromebook_x86_64_gles/download.py -t /opt/chromebook_x86_64_gles
|
||||
```
|
||||
|
||||
If you don't have authorization to use those assets, then see the README.md
|
||||
files for
|
||||
[armhf_sysroot](https://skia.googlesource.com/skia/+/master/infra/bots/assets/armhf_sysroot/README.md),
|
||||
[chromebook_arm_gles](https://skia.googlesource.com/skia/+/master/infra/bots/assets/chromebook_arm_gles/README.md),
|
||||
and
|
||||
[chromebook_x86_64_gles](https://skia.googlesource.com/skia/+/master/infra/bots/assets/chromebook_x86_64_gles/README.md)
|
||||
for instructions on creating those assets.
|
||||
|
||||
Once those files are in place, generate the GN args that resemble the following:
|
||||
|
||||
```
|
||||
#ARM
|
||||
cc= "/opt/clang/bin/clang"
|
||||
cxx = "/opt/clang/bin/clang++"
|
||||
|
||||
extra_asmflags = [
|
||||
"--target=armv7a-linux-gnueabihf",
|
||||
"--sysroot=/opt/armhf_sysroot/",
|
||||
"-march=armv7-a",
|
||||
"-mfpu=neon",
|
||||
"-mthumb",
|
||||
]
|
||||
extra_cflags=[
|
||||
"--target=armv7a-linux-gnueabihf",
|
||||
"--sysroot=/opt/armhf_sysroot",
|
||||
"-I/opt/chromebook_arm_gles/include",
|
||||
"-I/opt/armhf_sysroot/include/",
|
||||
"-I/opt/armhf_sysroot/include/c++/4.8.4/",
|
||||
"-I/opt/armhf_sysroot/include/c++/4.8.4/arm-linux-gnueabihf/",
|
||||
"-DMESA_EGL_NO_X11_HEADERS",
|
||||
"-funwind-tables",
|
||||
]
|
||||
extra_ldflags=[
|
||||
"--sysroot=/opt/armhf_sysroot",
|
||||
"-B/opt/armhf_sysroot/bin",
|
||||
"-B/opt/armhf_sysroot/gcc-cross",
|
||||
"-L/opt/armhf_sysroot/gcc-cross",
|
||||
"-L/opt/armhf_sysroot/lib",
|
||||
"-L/opt/chromebook_arm_gles/lib",
|
||||
"--target=armv7a-linux-gnueabihf",
|
||||
]
|
||||
target_cpu="arm"
|
||||
skia_use_fontconfig = false
|
||||
skia_use_system_freetype2 = false
|
||||
skia_use_egl = true
|
||||
|
||||
|
||||
# x86_64
|
||||
cc= "/opt/clang/bin/clang"
|
||||
cxx = "/opt/clang/bin/clang++"
|
||||
extra_cflags=[
|
||||
"-I/opt/clang/include/c++/v1/",
|
||||
"-I/opt/chromebook_x86_64_gles/include",
|
||||
"-DMESA_EGL_NO_X11_HEADERS",
|
||||
"-DEGL_NO_IMAGE_EXTERNAL",
|
||||
]
|
||||
extra_ldflags=[
|
||||
"-stdlib=libc++",
|
||||
"-fuse-ld=lld",
|
||||
"-L/opt/chromebook_x86_64_gles/lib",
|
||||
]
|
||||
target_cpu="x64"
|
||||
skia_use_fontconfig = false
|
||||
skia_use_system_freetype2 = false
|
||||
skia_use_egl = true
|
||||
```
|
||||
|
||||
Compile dm (or another executable of your choice) with ninja, as per usual.
|
||||
|
||||
Push the binary to a chromebook via ssh and
|
||||
[run dm as normal](https://skia.org/dev/testing/tests) using the gles GPU
|
||||
config.
|
||||
|
||||
Most chromebooks by default have their home directory partition marked as
|
||||
noexec. To avoid "permission denied" errors, remember to run something like:
|
||||
|
||||
```
|
||||
sudo mount -i -o remount,exec /home/chronos
|
||||
```
|
||||
|
||||
## <span id="macos">Mac</span>
|
||||
|
||||
Mac users may want to pass `--ide=xcode` to `bin/gn gen` to generate an Xcode
|
||||
project.
|
||||
|
||||
## <span id="ios">iOS</span>
|
||||
|
||||
Run GN to generate your build files. Set `target_os="ios"` to build for iOS.
|
||||
This defaults to `target_cpu="arm64"`. Choosing `x64` targets the iOS simulator.
|
||||
|
||||
```
|
||||
bin/gn gen out/ios64 --args='target_os="ios"'
|
||||
bin/gn gen out/ios32 --args='target_os="ios" target_cpu="arm"'
|
||||
bin/gn gen out/iossim --args='target_os="ios" target_cpu="x64"'
|
||||
```
|
||||
|
||||
This will also package (and for devices, sign) iOS test binaries. This defaults
|
||||
to a Google signing identity and provisioning profile. To use a different one
|
||||
set the GN args `skia_ios_identity` to match your code signing identity and
|
||||
`skia_ios_profile` to the name of your provisioning profile, e.g.
|
||||
|
||||
```
|
||||
skia_ios_identity=".*Jane Doe.*"
|
||||
skia_ios_profile="iPad Profile"`
|
||||
```
|
||||
|
||||
A list of identities can be found by typing `security find-identity` on the
|
||||
command line. The name of the provisioning profile should be available on the
|
||||
Apple Developer site. Alternatively, `skia_ios_profile` can be the absolute path
|
||||
to the mobileprovision file.
|
||||
|
||||
If you find yourself missing a Google signing identity or provisioning profile,
|
||||
you'll want to have a read through go/appledev.
|
||||
|
||||
For signed packages `ios-deploy` makes installing and running them on a device
|
||||
easy:
|
||||
|
||||
```
|
||||
ios-deploy -b out/Debug/dm.app -d --args "--match foo"
|
||||
```
|
||||
|
||||
Alternatively you can generate an Xcode project by passing `--ide=xcode` to
|
||||
`bin/gn gen`. If you are using Xcode version 10 or later, you may need to go to
|
||||
`Project Settings...` and verify that `Build System:` is set to
|
||||
`Legacy Build System`.
|
||||
|
||||
Deploying to a device with an OS older than the current SDK can be done by
|
||||
setting the `ios_min_target` arg:
|
||||
|
||||
```
|
||||
ios_min_target = "<major>.<minor>"
|
||||
```
|
||||
|
||||
where `<major>.<minor>` is the iOS version on the device, e.g., 12.0 or 11.4.
|
||||
|
||||
## <span id="windows">Windows</span>
|
||||
|
||||
Skia can build on Windows with Visual Studio 2017 or 2019. If GN is unable to
|
||||
locate either of those, it will print an error message. In that case, you can
|
||||
pass your `VC` path to GN via `win_vc`.
|
||||
|
||||
Skia can be compiled with the free
|
||||
[Build Tools for Visual Studio 2017 or 2019](https://www.visualstudio.com/downloads/#build-tools-for-visual-studio-2019).
|
||||
|
||||
The bots use a packaged 2019 toolchain, which Googlers can download like this:
|
||||
|
||||
```
|
||||
python2 infra/bots/assets/win_toolchain/download.py -t C:/toolchain
|
||||
```
|
||||
|
||||
You can then pass the VC and SDK paths to GN by setting your GN args:
|
||||
|
||||
```
|
||||
win_vc = "C:\toolchain\VC"
|
||||
win_sdk = "C:\toolchain\win_sdk"
|
||||
```
|
||||
|
||||
This toolchain is the only way we support 32-bit builds, by also setting
|
||||
`target_cpu="x86"`.
|
||||
|
||||
The Skia build assumes that the PATHEXT environment variable contains ".EXE".
|
||||
|
||||
### **Highly Recommended**: Build with clang-cl
|
||||
|
||||
Skia uses generated code that is only optimized when Skia is built with clang.
|
||||
Other compilers get generic unoptimized code.
|
||||
|
||||
Setting the `cc` and `cxx` gn args is _not_ sufficient to build with clang-cl.
|
||||
These variables are ignored on Windows. Instead set the variable `clang_win` to
|
||||
your LLVM installation directory. If you installed the prebuilt LLVM downloaded
|
||||
from [here](https://releases.llvm.org/download.html 'LLVM Download') in the
|
||||
default location that would be:
|
||||
|
||||
```
|
||||
clang_win = "C:\Program Files\LLVM"
|
||||
```
|
||||
|
||||
Follow the standard Windows path specification and not MinGW convention (e.g.
|
||||
`C:\Program Files\LLVM` not ~~`/c/Program Files/LLVM`~~).
|
||||
|
||||
### Visual Studio Solutions
|
||||
|
||||
If you use Visual Studio, you may want to pass `--ide=vs` to `bin/gn gen` to
|
||||
generate `all.sln`. That solution will exist within the GN directory for the
|
||||
specific configuration, and will only build/run that configuration.
|
||||
|
||||
If you want a Visual Studio Solution that supports multiple GN configurations,
|
||||
there is a helper script. It requires that all of your GN directories be inside
|
||||
the `out` directory. First, create all of your GN configurations as usual. Pass
|
||||
`--ide=vs` when running `bin/gn gen` for each one. Then:
|
||||
|
||||
```
|
||||
python2 gn/gn_meta_sln.py
|
||||
```
|
||||
|
||||
This creates a new dedicated output directory and solution file
|
||||
`out/sln/skia.sln`. It has one solution configuration for each GN configuration,
|
||||
and supports building and running any of them. It also adjusts syntax
|
||||
highlighting of inactive code blocks based on preprocessor definitions from the
|
||||
selected solution configuration.
|
||||
|
||||
## <span id="win-arm64">Windows ARM64</span>
|
||||
|
||||
There is early, experimental support for
|
||||
[Windows 10 on ARM](https://docs.microsoft.com/en-us/windows/arm/). This
|
||||
currently requires (a recent version of) MSVC, and the
|
||||
`Visual C++ compilers and libraries for ARM64` individual component in the
|
||||
Visual Studio Installer. For Googlers, the win_toolchain asset includes the
|
||||
ARM64 compiler.
|
||||
|
||||
To use that toolchain, set the `target_cpu` GN argument to `"arm64"`. Note that
|
||||
OpenGL is not supported by Windows 10 on ARM, so Skia's GL backends are stubbed
|
||||
out, and will not work. ANGLE is supported:
|
||||
|
||||
```
|
||||
bin/gn gen out/win-arm64 --args='target_cpu="arm64" skia_use_angle=true'
|
||||
```
|
||||
|
||||
This will produce a build of Skia that can use the software or ANGLE backends,
|
||||
in DM. Viewer only works when launched with `--backend angle`, because the
|
||||
software backend tries to use OpenGL to display the window contents.
|
||||
|
||||
## <span id="cmake">CMake</span>
|
||||
|
||||
We have added a GN-to-CMake translator mainly for use with IDEs that like CMake
|
||||
project descriptions. This is not meant for any purpose beyond development.
|
||||
|
||||
```
|
||||
bin/gn gen out/config --ide=json --json-ide-script=../../gn/gn_to_cmake.py
|
||||
```
|
91
site2/docs/user/color.md
Normal file
91
site2/docs/user/color.md
Normal file
@ -0,0 +1,91 @@
|
||||
|
||||
---
|
||||
title: "Skia Color Management"
|
||||
linkTitle: "Skia Color Management"
|
||||
|
||||
---
|
||||
|
||||
|
||||
What we mean by color management
|
||||
--------------------------------
|
||||
|
||||
All the color spaces Skia works with describe themselves by how to transform
|
||||
colors from that color space to a common "connection" color space called XYZ
|
||||
D50. And we can infer from that same description how to transform from that
|
||||
XYZ D50 space back to the original color space. XYZ D50 is a color space
|
||||
represented in three dimensions like RGB, but the XYZ parts are not RGB-like at
|
||||
all, rather a linear remix of those channels. Y is closest to what you'd think
|
||||
of as brightness, but X and Z are a little more abstract. It's kind of like
|
||||
YUV if you're familiar with that. The "D50" part refers to the whitepoint of
|
||||
this space, around 5000 Kelvin.
|
||||
|
||||
All color managed drawing is divided into six parts, three steps connecting the
|
||||
source colors to that XYZ D50 space, then three symmetric steps connecting back
|
||||
from XYZ D50 to the destination color space. Some of these steps can
|
||||
annihilate with each other into no-ops, sometimes all the way to the entire
|
||||
process amounting to a no-op when the source space and destination space are
|
||||
the same. Here are the steps:
|
||||
|
||||
Color management steps
|
||||
----------------------
|
||||
|
||||
1. unpremultiply if the source color is premultiplied -- alpha is not involved
|
||||
in color management, and we need to divide it out if it's multiplied in
|
||||
2. linearize the source color using the source color space's transfer function
|
||||
3. convert those unpremultiplied, linear source colors to XYZ D50 gamut by
|
||||
multiplying by a 3x3 matrix
|
||||
4. convert those XYZ D50 colors to the destination gamut by multiplying by a 3x3 matrix
|
||||
5. encode that color using the inverse of the destination color space's transfer function
|
||||
6. premultiply by alpha if the destination is premultiplied
|
||||
|
||||
If you poke around in our code the clearest place to see this logic is in a
|
||||
type called SkColorSpaceXformSteps. You'll see it as 5 steps there: we always
|
||||
merge the innermost two operations into a single 3x3 matrix multiply.
|
||||
|
||||
Optimizations
|
||||
-------------
|
||||
|
||||
Whenever we're about to do some drawing we look at which of those steps we
|
||||
really need to do. Any step that's a fundamental no-op we skip:
|
||||
|
||||
* skip 1 if the source is already unpremultiplied
|
||||
* skip 2 if the source is already linearly encoded
|
||||
* skip 3 and 4 if that single concatenated matrix is identity (i.e. the
|
||||
source and destination color spaces have the same gamut)
|
||||
* skip 5 if the destination wants linear encoding
|
||||
* skip 6 if the destination wants to be unpremultiplied
|
||||
|
||||
We can reason from those basic skips into some more advanced optimizations:
|
||||
|
||||
* if we've skipped 3 and 4 already, we can skip 2 and 5 any time the transfer
|
||||
functions are the same -- sending colors through a given transfer function
|
||||
and its own inverse is a no-op
|
||||
* if we've skipped all of 2-5, we can skip 1 and 6 if we were going to do
|
||||
both --- no sense in unpremultiplying just to re-premultiply.
|
||||
* opaque colors can be treated as either unpremultiplied or premultiplied,
|
||||
whichever lets us skip more steps.
|
||||
|
||||
All this comes together to an impressive "nothing to do" most of the time. If
|
||||
you're drawing opaque colors in a given color space to a destination tagged
|
||||
with that same color space, we'll notice we can skip all six steps. Sometimes
|
||||
fewer steps are needed, sometimes more. In general if you need to do a gamut
|
||||
conversion, you should generally expect all the middle steps to be active.
|
||||
Steps 2 and 5 are by far the most expensive to compute.
|
||||
|
||||
nullptr SkColorSpace defaults
|
||||
-----------------------------
|
||||
|
||||
Now how do nullptr SkColorSpace defaults work into all of this? We preface all
|
||||
that logic I've just mentioned above with this little snippet:
|
||||
|
||||
if (srcCS == nullptr) { srcCS = sRGB; }
|
||||
if (dstCS == nullptr) { dstCS = srcCS; }
|
||||
|
||||
(Order matters there.) The gist is, we assume any untagged sources are sRGB.
|
||||
And if you leave your surface untagged, we act as if your destination fluidly
|
||||
matches whatever source you're trying to draw into it, which skips at least
|
||||
steps 2-5 as listed above, maintaining an unmanaged color mode of drawing
|
||||
compatible with how retro Skia used to work before we introduce color
|
||||
management. It's not very principled, but it's handy in practice to keep
|
||||
around.
|
||||
|
47
site2/docs/user/download.md
Normal file
47
site2/docs/user/download.md
Normal file
@ -0,0 +1,47 @@
|
||||
---
|
||||
title: 'How to download Skia'
|
||||
linkTitle: 'Download'
|
||||
|
||||
weight: 10
|
||||
menu:
|
||||
main:
|
||||
weight: 50
|
||||
---
|
||||
|
||||
## Install `depot_tools` and Git
|
||||
|
||||
Follow the instructions on [Installing Chromium's
|
||||
depot_tools](http://www.chromium.org/developers/how-tos/install-depot-tools)
|
||||
to download `depot_tools` (which includes gclient, git-cl, and Ninja).
|
||||
Below is a summary of the necessary steps.
|
||||
|
||||
<!--?prettify lang=sh?-->
|
||||
|
||||
git clone 'https://chromium.googlesource.com/chromium/tools/depot_tools.git'
|
||||
export PATH="${PWD}/depot_tools:${PATH}"
|
||||
|
||||
`depot_tools` will also install Git on your system, if it wasn't installed
|
||||
already.
|
||||
|
||||
## Clone the Skia repository
|
||||
|
||||
Skia can either be cloned using `git` or the `fetch` tool that is
|
||||
installed with `depot_tools`.
|
||||
|
||||
<!--?prettify lang=sh?-->
|
||||
|
||||
git clone https://skia.googlesource.com/skia.git
|
||||
# or
|
||||
# fetch skia
|
||||
cd skia
|
||||
python2 tools/git-sync-deps
|
||||
|
||||
## Getting started with Skia
|
||||
|
||||
You will probably now want to [build](./build) Skia.
|
||||
|
||||
## Changing and contributing to Skia
|
||||
|
||||
At this point, you have everything you need to build and use Skia! If
|
||||
you want to make changes, and possibly contribute them back to the Skia
|
||||
project, read [How To Submit a Patch](../dev/contrib/submit).
|
34
site2/docs/user/issue-tracker.md
Normal file
34
site2/docs/user/issue-tracker.md
Normal file
@ -0,0 +1,34 @@
|
||||
|
||||
---
|
||||
title: "Issue Tracking"
|
||||
linkTitle: "Issue Tracking"
|
||||
|
||||
---
|
||||
|
||||
|
||||
The Skia Issue Tracker
|
||||
----------------------
|
||||
[Skia's Issue Tracker](https://bugs.chromium.org/p/skia/issues/list)
|
||||
(bug.skia.org or skbug.com) is the primary bug database where we track all defect
|
||||
reports and feature requests.
|
||||
|
||||
When filing a new issue, please select the appropriate template, most likely
|
||||
"Defect report from user" or "Feature request". Include an example
|
||||
[fiddle](https://fiddle.skia.org) or image. All issues will be triaged by our
|
||||
program manager and assigned to the appropriate functional team.
|
||||
|
||||
|
||||
Skia issues in the Chromium Tracker
|
||||
-----------------------------------
|
||||
Skia bugs found in Chrome may be filed in the [Chromium Tracker](https://bugs.chromium.org/p/chromium/issues/list) (crbug.com).
|
||||
|
||||
### Triage for Chromium developers
|
||||
* To have an issue triaged by the Skia team, add `Component:Internals>Skia`.
|
||||
* For problems related to Skia rolls where an obvious owner cannot be found in
|
||||
the list of CLs, assign to the Skia Gardener, listed in the gardeners widget
|
||||
on [status.skia.org](https://status.skia.org) and as a reviewer on the roll CL.
|
||||
* If the Gardener cannot be assigned, cc them and assign the issue to hcm@.
|
||||
* For GPU specific issues, add label `Hotlist-Ganesh`.
|
||||
* For image encoding or decoding issues, add
|
||||
`Component:Internals>Images>Codecs`.
|
||||
|
10
site2/docs/user/modules/_index.md
Normal file
10
site2/docs/user/modules/_index.md
Normal file
@ -0,0 +1,10 @@
|
||||
|
||||
---
|
||||
title: "Library Modules"
|
||||
linkTitle: "Library Modules"
|
||||
|
||||
weight: 3
|
||||
|
||||
---
|
||||
|
||||
|
858
site2/docs/user/modules/canvaskit.md
Normal file
858
site2/docs/user/modules/canvaskit.md
Normal file
@ -0,0 +1,858 @@
|
||||
|
||||
---
|
||||
title: "CanvasKit - Skia + WebAssembly"
|
||||
linkTitle: "CanvasKit - Skia + WebAssembly"
|
||||
|
||||
weight: 20
|
||||
|
||||
---
|
||||
|
||||
|
||||
Skia now offers a WebAssembly build for easy deployment of our graphics APIs on
|
||||
the web.
|
||||
|
||||
CanvasKit provides a playground for testing new Canvas and SVG platform APIs,
|
||||
enabling fast-paced development on the web platform.
|
||||
It can also be used as a deployment mechanism for custom web apps requiring
|
||||
cutting-edge features, like Skia's [Lottie
|
||||
animation](https://skia.org/user/modules/skottie) support.
|
||||
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
- WebGL context encapsulated as an SkSurface, allowing for direct drawing to
|
||||
an HTML canvas
|
||||
- Core set of Skia canvas/paint/path/text APIs available, see bindings
|
||||
- Draws to a hardware-accelerated backend
|
||||
- Security tested with Skia's fuzzers
|
||||
|
||||
Samples
|
||||
-------
|
||||
|
||||
<style>
|
||||
#demo canvas {
|
||||
border: 1px dashed #AAA;
|
||||
margin: 2px;
|
||||
}
|
||||
|
||||
#patheffect, #ink, #shaping, #shader1, #camera3d {
|
||||
width: 400px;
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
#sk_legos, #sk_drinks, #sk_party, #sk_onboarding {
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
figure {
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
figcaption > a {
|
||||
margin: 2px 10px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<div id=demo>
|
||||
<h3>Paragraph shaping, custom shaders, and perspective transformation</h3>
|
||||
<figure>
|
||||
<canvas id=shaping width=500 height=500></canvas>
|
||||
<figcaption>
|
||||
<a href="https://jsfiddle.skia.org/canvaskit/48c67bde53f66a2f1e578e14b88a85bd062fdcf80c143c5eb92071233d4d86ae"
|
||||
target=_blank rel=noopener>
|
||||
SkParagraph JSFiddle</a>
|
||||
</figcaption>
|
||||
</figure>
|
||||
<figure>
|
||||
<canvas id=shader1 width=512 height=512></canvas>
|
||||
<figcaption>
|
||||
<a href="https://jsfiddle.skia.org/canvaskit/b9a8d33dc9853e491d5e464bc3b212560d9c546c44d71f00c9db15180ab6d5b8"
|
||||
target=_blank rel=noopener>
|
||||
Shader JSFiddle</a>
|
||||
</figcaption>
|
||||
</figure>
|
||||
<figure>
|
||||
<canvas id=camera3d width=400 height=400></canvas>
|
||||
<figcaption>
|
||||
<a href="https://jsfiddle.skia.org/canvaskit/786b7dd80aac6eda6ecbc7f035de62249ef0f61fd7aa6ab9a6829f70ba00fce2"
|
||||
target=_blank rel=noopener>
|
||||
3D Cube JSFiddle</a>
|
||||
</figcaption>
|
||||
</figure>
|
||||
|
||||
<h3>Play back bodymovin lottie files with skottie (click for fiddles)</h3>
|
||||
<a href="https://jsfiddle.skia.org/canvaskit/6f4540a485ecbb8f0663b5ab3e04d9f3626a45234595d65f1b87942b90678aff"
|
||||
target=_blank rel=noopener>
|
||||
<canvas id=sk_legos width=300 height=300></canvas>
|
||||
</a>
|
||||
<a href="https://jsfiddle.skia.org/canvaskit/2fdbd163e5a4ec55e38e84b6c3e069738d3b12fd858362265192109917f9dd2c"
|
||||
target=_blank rel=noopener>
|
||||
<canvas id=sk_drinks width=500 height=500></canvas>
|
||||
</a>
|
||||
<a href="https://jsfiddle.skia.org/canvaskit/73624637ee6d96a8ce8ff8d523b90acdae3ec75b3fe16ddf3040990544c870ec"
|
||||
target=_blank rel=noopener>
|
||||
<canvas id=sk_party width=500 height=500></canvas>
|
||||
</a>
|
||||
<a href="https://jsfiddle.skia.org/canvaskit/d63a544b87ccbe6bf8f15c772355d1c6dbc5eafc355bcd27457eca41da843cb5"
|
||||
target=_blank rel=noopener>
|
||||
<canvas id=sk_onboarding width=500 height=500></canvas>
|
||||
</a>
|
||||
|
||||
<h3>Go beyond the HTML Canvas2D</h3>
|
||||
<figure>
|
||||
<canvas id=patheffect width=400 height=400></canvas>
|
||||
<figcaption>
|
||||
<a href="https://jsfiddle.skia.org/canvaskit/97d4b9ce527a7ffb0f4bed77d9b7f01f889c8dbe68ac053d56739a5122c65b53"
|
||||
target=_blank rel=noopener>
|
||||
Star JSFiddle</a>
|
||||
</figcaption>
|
||||
</figure>
|
||||
<figure>
|
||||
<canvas id=ink width=400 height=400></canvas>
|
||||
<figcaption>
|
||||
<a href="https://jsfiddle.skia.org/canvaskit/07c83d37d8115413442fda2c8b7f1bc56823d1c597473970638480e95cba42ee"
|
||||
target=_blank rel=noopener>
|
||||
Ink JSFiddle</a>
|
||||
</figcaption>
|
||||
</figure>
|
||||
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
(function() {
|
||||
// Tries to load the WASM version if supported, shows error otherwise
|
||||
let s = document.createElement('script');
|
||||
let locate_file = '';
|
||||
// Hey, if you are looking at this code for an example of how to do it yourself, please use
|
||||
// an actual CDN, such as https://unpkg.com/canvaskit-wasm - it will have better reliability
|
||||
// and niceties like brotli compression.
|
||||
if (window.WebAssembly && typeof window.WebAssembly.compile === 'function') {
|
||||
console.log('WebAssembly is supported!');
|
||||
locate_file = 'https://particles.skia.org/dist/';
|
||||
} else {
|
||||
console.log('WebAssembly is not supported (yet) on this browser.');
|
||||
document.getElementById('demo').innerHTML = "<div>WASM not supported by your browser. Try a recent version of Chrome, Firefox, Edge, or Safari.</div>";
|
||||
return;
|
||||
}
|
||||
s.src = locate_file + 'canvaskit.js';
|
||||
s.onload = () => {
|
||||
let CanvasKit = null;
|
||||
let legoJSON = null;
|
||||
let drinksJSON = null;
|
||||
let confettiJSON = null;
|
||||
let onboardingJSON = null;
|
||||
let fullBounds = [0, 0, 500, 500];
|
||||
const ckLoaded = CanvasKitInit({
|
||||
locateFile: (file) => locate_file + file,
|
||||
});
|
||||
|
||||
ckLoaded.then((CK) => {
|
||||
CanvasKit = CK;
|
||||
DrawingExample(CanvasKit);
|
||||
InkExample(CanvasKit);
|
||||
ShapingExample(CanvasKit);
|
||||
// Set bounds to fix the 4:3 resolution of the legos
|
||||
SkottieExample(CanvasKit, 'sk_legos', legoJSON, [-183, -100, 483, 400]);
|
||||
// Re-size to fit
|
||||
SkottieExample(CanvasKit, 'sk_drinks', drinksJSON, fullBounds);
|
||||
SkottieExample(CanvasKit, 'sk_party', confettiJSON, fullBounds);
|
||||
SkottieExample(CanvasKit, 'sk_onboarding', onboardingJSON, fullBounds);
|
||||
ShaderExample1(CanvasKit);
|
||||
});
|
||||
|
||||
fetch('https://storage.googleapis.com/skia-cdn/misc/lego_loader.json').then((resp) => {
|
||||
resp.text().then((str) => {
|
||||
legoJSON = str;
|
||||
SkottieExample(CanvasKit, 'sk_legos', legoJSON, [-183, -100, 483, 400]);
|
||||
});
|
||||
});
|
||||
|
||||
fetch('https://storage.googleapis.com/skia-cdn/misc/drinks.json').then((resp) => {
|
||||
resp.text().then((str) => {
|
||||
drinksJSON = str;
|
||||
SkottieExample(CanvasKit, 'sk_drinks', drinksJSON, fullBounds);
|
||||
});
|
||||
});
|
||||
|
||||
fetch('https://storage.googleapis.com/skia-cdn/misc/confetti.json').then((resp) => {
|
||||
resp.text().then((str) => {
|
||||
confettiJSON = str;
|
||||
SkottieExample(CanvasKit, 'sk_party', confettiJSON, fullBounds);
|
||||
});
|
||||
});
|
||||
|
||||
fetch('https://storage.googleapis.com/skia-cdn/misc/onboarding.json').then((resp) => {
|
||||
resp.text().then((str) => {
|
||||
onboardingJSON = str;
|
||||
SkottieExample(CanvasKit, 'sk_onboarding', onboardingJSON, fullBounds);
|
||||
});
|
||||
});
|
||||
|
||||
const loadBrickTex = fetch('https://storage.googleapis.com/skia-cdn/misc/brickwork-texture.jpg').then((response) => response.arrayBuffer());
|
||||
const loadBrickBump = fetch('https://storage.googleapis.com/skia-cdn/misc/brickwork_normal-map.jpg').then((response) => response.arrayBuffer());
|
||||
Promise.all([ckLoaded, loadBrickTex, loadBrickBump]).then((results) => {Camera3D(...results)});
|
||||
|
||||
function preventScrolling(canvas) {
|
||||
canvas.addEventListener('touchmove', (e) => {
|
||||
// Prevents touch events in the canvas from scrolling the canvas.
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
});
|
||||
}
|
||||
|
||||
function DrawingExample(CanvasKit) {
|
||||
const surface = CanvasKit.MakeCanvasSurface('patheffect');
|
||||
if (!surface) {
|
||||
console.log('Could not make surface');
|
||||
}
|
||||
const context = CanvasKit.currentContext();
|
||||
|
||||
const canvas = surface.getCanvas();
|
||||
|
||||
const paint = new CanvasKit.Paint();
|
||||
|
||||
const textPaint = new CanvasKit.Paint();
|
||||
textPaint.setColor(CanvasKit.Color(40, 0, 0, 1.0));
|
||||
textPaint.setAntiAlias(true);
|
||||
|
||||
const textFont = new CanvasKit.Font(null, 30);
|
||||
|
||||
let i = 0;
|
||||
|
||||
let X = 200;
|
||||
let Y = 200;
|
||||
|
||||
function drawFrame() {
|
||||
const path = starPath(CanvasKit, X, Y);
|
||||
CanvasKit.setCurrentContext(context);
|
||||
const dpe = CanvasKit.PathEffect.MakeDash([15, 5, 5, 10], i/5);
|
||||
i++;
|
||||
|
||||
paint.setPathEffect(dpe);
|
||||
paint.setStyle(CanvasKit.PaintStyle.Stroke);
|
||||
paint.setStrokeWidth(5.0 + -3 * Math.cos(i/30));
|
||||
paint.setAntiAlias(true);
|
||||
paint.setColor(CanvasKit.Color(66, 129, 164, 1.0));
|
||||
|
||||
canvas.clear(CanvasKit.Color(255, 255, 255, 1.0));
|
||||
|
||||
canvas.drawPath(path, paint);
|
||||
canvas.drawText('Try Clicking!', 10, 380, textPaint, textFont);
|
||||
canvas.flush();
|
||||
dpe.delete();
|
||||
path.delete();
|
||||
window.requestAnimationFrame(drawFrame);
|
||||
}
|
||||
window.requestAnimationFrame(drawFrame);
|
||||
|
||||
// Make animation interactive
|
||||
let interact = (e) => {
|
||||
if (!e.buttons) {
|
||||
return;
|
||||
}
|
||||
X = e.offsetX;
|
||||
Y = e.offsetY;
|
||||
};
|
||||
document.getElementById('patheffect').addEventListener('pointermove', interact);
|
||||
document.getElementById('patheffect').addEventListener('pointerdown', interact);
|
||||
preventScrolling(document.getElementById('patheffect'));
|
||||
|
||||
// A client would need to delete this if it didn't go on forever.
|
||||
// font.delete();
|
||||
// paint.delete();
|
||||
}
|
||||
|
||||
function InkExample(CanvasKit) {
|
||||
const surface = CanvasKit.MakeCanvasSurface('ink');
|
||||
if (!surface) {
|
||||
console.log('Could not make surface');
|
||||
}
|
||||
const context = CanvasKit.currentContext();
|
||||
|
||||
const canvas = surface.getCanvas();
|
||||
|
||||
let paint = new CanvasKit.Paint();
|
||||
paint.setAntiAlias(true);
|
||||
paint.setColor(CanvasKit.Color(0, 0, 0, 1.0));
|
||||
paint.setStyle(CanvasKit.PaintStyle.Stroke);
|
||||
paint.setStrokeWidth(4.0);
|
||||
// This effect smooths out the drawn lines a bit.
|
||||
paint.setPathEffect(CanvasKit.PathEffect.MakeCorner(50));
|
||||
|
||||
// Draw I N K
|
||||
let path = new CanvasKit.Path();
|
||||
path.moveTo(80, 30);
|
||||
path.lineTo(80, 80);
|
||||
|
||||
path.moveTo(100, 80);
|
||||
path.lineTo(100, 15);
|
||||
path.lineTo(130, 95);
|
||||
path.lineTo(130, 30);
|
||||
|
||||
path.moveTo(150, 30);
|
||||
path.lineTo(150, 80);
|
||||
path.moveTo(170, 30);
|
||||
path.lineTo(150, 55);
|
||||
path.lineTo(170, 80);
|
||||
|
||||
let paths = [path];
|
||||
let paints = [paint];
|
||||
|
||||
function drawFrame() {
|
||||
CanvasKit.setCurrentContext(context);
|
||||
|
||||
for (let i = 0; i < paints.length && i < paths.length; i++) {
|
||||
canvas.drawPath(paths[i], paints[i]);
|
||||
}
|
||||
canvas.flush();
|
||||
|
||||
window.requestAnimationFrame(drawFrame);
|
||||
}
|
||||
|
||||
let hold = false;
|
||||
let interact = (e) => {
|
||||
let type = e.type;
|
||||
if (type === 'lostpointercapture' || type === 'pointerup' || !e.pressure ) {
|
||||
hold = false;
|
||||
return;
|
||||
}
|
||||
if (hold) {
|
||||
path.lineTo(e.offsetX, e.offsetY);
|
||||
} else {
|
||||
paint = paint.copy();
|
||||
paint.setColor(CanvasKit.Color(Math.random() * 255, Math.random() * 255, Math.random() * 255, Math.random() + .2));
|
||||
paints.push(paint);
|
||||
path = new CanvasKit.Path();
|
||||
paths.push(path);
|
||||
path.moveTo(e.offsetX, e.offsetY);
|
||||
}
|
||||
hold = true;
|
||||
};
|
||||
document.getElementById('ink').addEventListener('pointermove', interact);
|
||||
document.getElementById('ink').addEventListener('pointerdown', interact);
|
||||
document.getElementById('ink').addEventListener('lostpointercapture', interact);
|
||||
document.getElementById('ink').addEventListener('pointerup', interact);
|
||||
preventScrolling(document.getElementById('ink'));
|
||||
window.requestAnimationFrame(drawFrame);
|
||||
}
|
||||
|
||||
function ShapingExample(CanvasKit) {
|
||||
const surface = CanvasKit.MakeCanvasSurface('shaping');
|
||||
if (!surface) {
|
||||
console.log('Could not make surface');
|
||||
return;
|
||||
}
|
||||
let robotoData = null;
|
||||
fetch('https://storage.googleapis.com/skia-cdn/google-web-fonts/Roboto-Regular.ttf').then((resp) => {
|
||||
resp.arrayBuffer().then((buffer) => {
|
||||
robotoData = buffer;
|
||||
requestAnimationFrame(drawFrame);
|
||||
});
|
||||
});
|
||||
|
||||
let emojiData = null;
|
||||
fetch('https://storage.googleapis.com/skia-cdn/misc/NotoColorEmoji.ttf').then((resp) => {
|
||||
resp.arrayBuffer().then((buffer) => {
|
||||
emojiData = buffer;
|
||||
requestAnimationFrame(drawFrame);
|
||||
});
|
||||
});
|
||||
|
||||
const skcanvas = surface.getCanvas();
|
||||
|
||||
const font = new CanvasKit.Font(null, 18);
|
||||
const fontPaint = new CanvasKit.Paint();
|
||||
fontPaint.setStyle(CanvasKit.PaintStyle.Fill);
|
||||
fontPaint.setAntiAlias(true);
|
||||
|
||||
skcanvas.drawText(`Fetching Font data...`, 5, 450, fontPaint, font);
|
||||
surface.flush();
|
||||
|
||||
const context = CanvasKit.currentContext();
|
||||
|
||||
let paragraph = null;
|
||||
let X = 10;
|
||||
let Y = 10;
|
||||
const str = 'The quick brown fox 🦊 ate a zesty hamburgerfons 🍔.\nThe 👩👩👧👧 laughed.';
|
||||
|
||||
function drawFrame() {
|
||||
if (robotoData && emojiData && !paragraph) {
|
||||
const fontMgr = CanvasKit.FontMgr.FromData([robotoData, emojiData]);
|
||||
|
||||
const paraStyle = new CanvasKit.ParagraphStyle({
|
||||
textStyle: {
|
||||
color: CanvasKit.BLACK,
|
||||
fontFamilies: ['Roboto', 'Noto Color Emoji'],
|
||||
fontSize: 50,
|
||||
},
|
||||
textAlign: CanvasKit.TextAlign.Left,
|
||||
maxLines: 7,
|
||||
ellipsis: '...',
|
||||
});
|
||||
|
||||
const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
|
||||
builder.addText(str);
|
||||
paragraph = builder.build();
|
||||
}
|
||||
if (!paragraph) {
|
||||
requestAnimationFrame(drawFrame);
|
||||
return;
|
||||
}
|
||||
CanvasKit.setCurrentContext(context);
|
||||
skcanvas.clear(CanvasKit.WHITE);
|
||||
|
||||
const wrapTo = 350 + 150 * Math.sin(Date.now() / 2000);
|
||||
paragraph.layout(wrapTo);
|
||||
skcanvas.drawParagraph(paragraph, 0, 0);
|
||||
skcanvas.drawLine(wrapTo, 0, wrapTo, 400, fontPaint);
|
||||
|
||||
let posA = paragraph.getGlyphPositionAtCoordinate(X, Y);
|
||||
const cp = str.codePointAt(posA.pos);
|
||||
if (cp) {
|
||||
const glyph = String.fromCodePoint(cp);
|
||||
skcanvas.drawText(`At (${X.toFixed(2)}, ${Y.toFixed(2)}) glyph is '${glyph}'`, 5, 450, fontPaint, font);
|
||||
}
|
||||
|
||||
surface.flush();
|
||||
requestAnimationFrame(drawFrame);
|
||||
}
|
||||
|
||||
// Make animation interactive
|
||||
let interact = (e) => {
|
||||
// multiply by 4/5 to account for the difference in the canvas width and the CSS width.
|
||||
// The 10 accounts for where the mouse actually is compared to where it is drawn.
|
||||
X = (e.offsetX * 4/5) - 10;
|
||||
Y = e.offsetY * 4/5;
|
||||
};
|
||||
document.getElementById('shaping').addEventListener('pointermove', interact);
|
||||
document.getElementById('shaping').addEventListener('pointerdown', interact);
|
||||
document.getElementById('shaping').addEventListener('lostpointercapture', interact);
|
||||
document.getElementById('shaping').addEventListener('pointerup', interact);
|
||||
preventScrolling(document.getElementById('shaping'));
|
||||
window.requestAnimationFrame(drawFrame);
|
||||
}
|
||||
|
||||
function starPath(CanvasKit, X=128, Y=128, R=116) {
|
||||
let p = new CanvasKit.Path();
|
||||
p.moveTo(X + R, Y);
|
||||
for (let i = 1; i < 8; i++) {
|
||||
let a = 2.6927937 * i;
|
||||
p.lineTo(X + R * Math.cos(a), Y + R * Math.sin(a));
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
function SkottieExample(CanvasKit, id, jsonStr, bounds) {
|
||||
if (!CanvasKit || !jsonStr) {
|
||||
return;
|
||||
}
|
||||
const animation = CanvasKit.MakeAnimation(jsonStr);
|
||||
const duration = animation.duration() * 1000;
|
||||
const size = animation.size();
|
||||
let c = document.getElementById(id);
|
||||
bounds = bounds || {fLeft: 0, fTop: 0, fRight: size.w, fBottom: size.h};
|
||||
|
||||
const surface = CanvasKit.MakeCanvasSurface(id);
|
||||
if (!surface) {
|
||||
console.log('Could not make surface');
|
||||
}
|
||||
const context = CanvasKit.currentContext();
|
||||
const canvas = surface.getCanvas();
|
||||
|
||||
let firstFrame = new Date().getTime();
|
||||
|
||||
function drawFrame() {
|
||||
let now = new Date().getTime();
|
||||
let seek = ((now - firstFrame) / duration) % 1.0;
|
||||
CanvasKit.setCurrentContext(context);
|
||||
animation.seek(seek);
|
||||
|
||||
animation.render(canvas, bounds);
|
||||
canvas.flush();
|
||||
window.requestAnimationFrame(drawFrame);
|
||||
}
|
||||
window.requestAnimationFrame(drawFrame);
|
||||
//animation.delete();
|
||||
}
|
||||
|
||||
function ShaderExample1(CanvasKit) {
|
||||
if (!CanvasKit) {
|
||||
return;
|
||||
}
|
||||
const surface = CanvasKit.MakeCanvasSurface('shader1');
|
||||
if (!surface) {
|
||||
throw 'Could not make surface';
|
||||
}
|
||||
const skcanvas = surface.getCanvas();
|
||||
const paint = new CanvasKit.Paint();
|
||||
|
||||
const prog = `
|
||||
uniform float rad_scale;
|
||||
uniform float2 in_center;
|
||||
uniform float4 in_colors0;
|
||||
uniform float4 in_colors1;
|
||||
|
||||
half4 main(float2 p) {
|
||||
float2 pp = p - in_center;
|
||||
float radius = sqrt(dot(pp, pp));
|
||||
radius = sqrt(radius);
|
||||
float angle = atan(pp.y / pp.x);
|
||||
float t = (angle + 3.1415926/2) / (3.1415926);
|
||||
t += radius * rad_scale;
|
||||
t = fract(t);
|
||||
return half4(mix(in_colors0, in_colors1, t));
|
||||
}
|
||||
`;
|
||||
|
||||
// If there are multiple contexts on the screen, we need to make sure
|
||||
// we switch to this one before we draw.
|
||||
const context = CanvasKit.currentContext();
|
||||
const fact = CanvasKit.RuntimeEffect.Make(prog);
|
||||
function drawFrame() {
|
||||
CanvasKit.setCurrentContext(context);
|
||||
skcanvas.clear(CanvasKit.WHITE);
|
||||
const shader = fact.makeShader([
|
||||
Math.sin(Date.now() / 2000) / 5,
|
||||
256, 256,
|
||||
1, 0, 0, 1,
|
||||
0, 1, 0, 1],
|
||||
true/*=opaque*/);
|
||||
|
||||
paint.setShader(shader);
|
||||
skcanvas.drawRect(CanvasKit.LTRBRect(0, 0, 512, 512), paint);
|
||||
surface.flush();
|
||||
requestAnimationFrame(drawFrame);
|
||||
shader.delete();
|
||||
}
|
||||
requestAnimationFrame(drawFrame);
|
||||
}
|
||||
|
||||
function Camera3D(canvas, textureImgData, normalImgData) {
|
||||
const surface = CanvasKit.MakeCanvasSurface('camera3d');
|
||||
if (!surface) {
|
||||
console.error('Could not make surface');
|
||||
return;
|
||||
}
|
||||
|
||||
const sizeX = document.getElementById('camera3d').width;
|
||||
const sizeY = document.getElementById('camera3d').height;
|
||||
|
||||
let clickToWorld = CanvasKit.M44.identity();
|
||||
let worldToClick = CanvasKit.M44.identity();
|
||||
// rotation of the cube shown in the demo
|
||||
let rotation = CanvasKit.M44.identity();
|
||||
// temporary during a click and drag
|
||||
let clickRotation = CanvasKit.M44.identity();
|
||||
|
||||
// A virtual sphere used for tumbling the object on screen.
|
||||
const vSphereCenter = [sizeX/2, sizeY/2];
|
||||
const vSphereRadius = Math.min(...vSphereCenter);
|
||||
|
||||
// The rounded rect used for each face
|
||||
const margin = vSphereRadius / 20;
|
||||
const rr = CanvasKit.RRectXY(CanvasKit.LTRBRect(margin, margin,
|
||||
vSphereRadius - margin, vSphereRadius - margin), margin*2.5, margin*2.5);
|
||||
|
||||
const camAngle = Math.PI / 12;
|
||||
const cam = {
|
||||
'eye' : [0, 0, 1 / Math.tan(camAngle/2) - 1],
|
||||
'coa' : [0, 0, 0],
|
||||
'up' : [0, 1, 0],
|
||||
'near' : 0.05,
|
||||
'far' : 4,
|
||||
'angle': camAngle,
|
||||
};
|
||||
|
||||
let mouseDown = false;
|
||||
let clickDown = [0, 0]; // location of click down
|
||||
let lastMouse = [0, 0]; // last mouse location
|
||||
|
||||
// keep spinning after mouse up. Also start spinning on load
|
||||
let axis = [0.4, 1, 1];
|
||||
let totalSpin = 0;
|
||||
let spinRate = 0.1;
|
||||
let lastRadians = 0;
|
||||
let spinning = setInterval(keepSpinning, 30);
|
||||
|
||||
const imgscale = CanvasKit.Matrix.scaled(2, 2);
|
||||
const textureShader = CanvasKit.MakeImageFromEncoded(textureImgData).makeShaderCubic(
|
||||
CanvasKit.TileMode.Clamp, CanvasKit.TileMode.Clamp, 1/3, 1/3, imgscale);
|
||||
const normalShader = CanvasKit.MakeImageFromEncoded(normalImgData).makeShaderCubic(
|
||||
CanvasKit.TileMode.Clamp, CanvasKit.TileMode.Clamp, 1/3, 1/3, imgscale);
|
||||
const children = [textureShader, normalShader];
|
||||
|
||||
const prog = `
|
||||
uniform shader color_map;
|
||||
uniform shader normal_map;
|
||||
|
||||
uniform float3 lightPos;
|
||||
layout (marker=local_to_world) uniform float4x4 localToWorld;
|
||||
layout (marker=normals(local_to_world)) uniform float4x4 localToWorldAdjInv;
|
||||
|
||||
float3 convert_normal_sample(half4 c) {
|
||||
float3 n = 2 * c.rgb - 1;
|
||||
n.y = -n.y;
|
||||
return n;
|
||||
}
|
||||
|
||||
half4 main(float2 p) {
|
||||
float3 norm = convert_normal_sample(sample(normal_map, p));
|
||||
float3 plane_norm = normalize(localToWorldAdjInv * float4(norm, 0)).xyz;
|
||||
|
||||
float3 plane_pos = (localToWorld * float4(p, 0, 1)).xyz;
|
||||
float3 light_dir = normalize(lightPos - plane_pos);
|
||||
|
||||
float ambient = 0.2;
|
||||
float dp = dot(plane_norm, light_dir);
|
||||
float scale = min(ambient + max(dp, 0), 1);
|
||||
|
||||
return sample(color_map, p) * half4(float4(scale, scale, scale, 1));
|
||||
}
|
||||
`;
|
||||
|
||||
const fact = CanvasKit.RuntimeEffect.Make(prog);
|
||||
|
||||
// properties of light
|
||||
let lightLocation = [...vSphereCenter];
|
||||
let lightDistance = vSphereRadius;
|
||||
let lightIconRadius = 12;
|
||||
let draggingLight = false;
|
||||
|
||||
function computeLightWorldPos() {
|
||||
return CanvasKit.Vector.add(CanvasKit.Vector.mulScalar([...vSphereCenter, 0], 0.5),
|
||||
CanvasKit.Vector.mulScalar(vSphereUnitV3(lightLocation), lightDistance));
|
||||
}
|
||||
|
||||
let lightWorldPos = computeLightWorldPos();
|
||||
|
||||
function drawLight(canvas) {
|
||||
const paint = new CanvasKit.Paint();
|
||||
paint.setAntiAlias(true);
|
||||
paint.setColor(CanvasKit.WHITE);
|
||||
canvas.drawCircle(...lightLocation, lightIconRadius + 2, paint);
|
||||
paint.setColor(CanvasKit.BLACK);
|
||||
canvas.drawCircle(...lightLocation, lightIconRadius, paint);
|
||||
}
|
||||
|
||||
// Takes an x and y rotation in radians and a scale and returns a 4x4 matrix used to draw a
|
||||
// face of the cube in that orientation.
|
||||
function faceM44(rx, ry, scale) {
|
||||
return CanvasKit.M44.multiply(
|
||||
CanvasKit.M44.rotated([0,1,0], ry),
|
||||
CanvasKit.M44.rotated([1,0,0], rx),
|
||||
CanvasKit.M44.translated([0, 0, scale]));
|
||||
}
|
||||
|
||||
const faceScale = vSphereRadius/2
|
||||
const faces = [
|
||||
{matrix: faceM44( 0, 0, faceScale ), color:CanvasKit.RED}, // front
|
||||
{matrix: faceM44( 0, Math.PI, faceScale ), color:CanvasKit.GREEN}, // back
|
||||
|
||||
{matrix: faceM44( Math.PI/2, 0, faceScale ), color:CanvasKit.BLUE}, // top
|
||||
{matrix: faceM44(-Math.PI/2, 0, faceScale ), color:CanvasKit.CYAN}, // bottom
|
||||
|
||||
{matrix: faceM44( 0, Math.PI/2, faceScale ), color:CanvasKit.MAGENTA}, // left
|
||||
{matrix: faceM44( 0,-Math.PI/2, faceScale ), color:CanvasKit.YELLOW}, // right
|
||||
];
|
||||
|
||||
// Returns a component of the matrix m indicating whether it faces the camera.
|
||||
// If it's positive for one of the matrices representing the face of the cube,
|
||||
// that face is currently in front.
|
||||
function front(m) {
|
||||
// Is this invertible?
|
||||
var m2 = CanvasKit.M44.invert(m);
|
||||
if (m2 === null) {
|
||||
m2 = CanvasKit.M44.identity();
|
||||
}
|
||||
// look at the sign of the z-scale of the inverse of m.
|
||||
// that's the number in row 2, col 2.
|
||||
return m2[10]
|
||||
}
|
||||
|
||||
function setClickToWorld(canvas, matrix) {
|
||||
const l2d = canvas.getLocalToDevice();
|
||||
worldToClick = CanvasKit.M44.multiply(CanvasKit.M44.mustInvert(matrix), l2d);
|
||||
clickToWorld = CanvasKit.M44.mustInvert(worldToClick);
|
||||
}
|
||||
|
||||
function drawCubeFace(canvas, m, color) {
|
||||
const trans = new CanvasKit.M44.translated([vSphereRadius/2, vSphereRadius/2, 0]);
|
||||
canvas.concat(CanvasKit.M44.multiply(trans, m, CanvasKit.M44.mustInvert(trans)));
|
||||
const znormal = front(canvas.getLocalToDevice());
|
||||
if (znormal < 0) {
|
||||
return; // skip faces facing backwards
|
||||
}
|
||||
// Pad with space for two 4x4 matrices. Even though the shader uses a layout()
|
||||
// statement to populate them, we still have to reserve space for them.
|
||||
const uniforms = [...lightWorldPos, ...Array(32).fill(0)];
|
||||
const paint = new CanvasKit.Paint();
|
||||
paint.setAntiAlias(true);
|
||||
const shader = fact.makeShaderWithChildren(uniforms, true /*=opaque*/, children);
|
||||
paint.setShader(shader);
|
||||
canvas.drawRRect(rr, paint);
|
||||
}
|
||||
|
||||
function drawFrame(canvas) {
|
||||
const clickM = canvas.getLocalToDevice();
|
||||
canvas.save();
|
||||
canvas.translate(vSphereCenter[0] - vSphereRadius/2, vSphereCenter[1] - vSphereRadius/2);
|
||||
// pass surface dimensions as viewport size.
|
||||
canvas.concat(CanvasKit.M44.setupCamera(
|
||||
CanvasKit.LTRBRect(0, 0, vSphereRadius, vSphereRadius), vSphereRadius/2, cam));
|
||||
// Mark the matrix to make it available to the shader by this name.
|
||||
canvas.markCTM('local_to_world');
|
||||
setClickToWorld(canvas, clickM);
|
||||
for (let f of faces) {
|
||||
const saveCount = canvas.getSaveCount();
|
||||
canvas.save();
|
||||
drawCubeFace(canvas, CanvasKit.M44.multiply(clickRotation, rotation, f.matrix), f.color);
|
||||
canvas.restoreToCount(saveCount);
|
||||
}
|
||||
canvas.restore(); // camera
|
||||
canvas.restore(); // center the following content in the window
|
||||
|
||||
// draw virtual sphere outline.
|
||||
const paint = new CanvasKit.Paint();
|
||||
paint.setAntiAlias(true);
|
||||
paint.setStyle(CanvasKit.PaintStyle.Stroke);
|
||||
paint.setColor(CanvasKit.Color(64, 255, 0, 1.0));
|
||||
canvas.drawCircle(vSphereCenter[0], vSphereCenter[1], vSphereRadius, paint);
|
||||
canvas.drawLine(vSphereCenter[0], vSphereCenter[1] - vSphereRadius,
|
||||
vSphereCenter[0], vSphereCenter[1] + vSphereRadius, paint);
|
||||
canvas.drawLine(vSphereCenter[0] - vSphereRadius, vSphereCenter[1],
|
||||
vSphereCenter[0] + vSphereRadius, vSphereCenter[1], paint);
|
||||
|
||||
drawLight(canvas);
|
||||
}
|
||||
|
||||
// convert a 2D point in the circle displayed on screen to a 3D unit vector.
|
||||
// the virtual sphere is a technique selecting a 3D direction by clicking on a the projection
|
||||
// of a hemisphere.
|
||||
function vSphereUnitV3(p) {
|
||||
// v = (v - fCenter) * (1 / fRadius);
|
||||
let v = CanvasKit.Vector.mulScalar(CanvasKit.Vector.sub(p, vSphereCenter), 1/vSphereRadius);
|
||||
|
||||
// constrain the clicked point within the circle.
|
||||
let len2 = CanvasKit.Vector.lengthSquared(v);
|
||||
if (len2 > 1) {
|
||||
v = CanvasKit.Vector.normalize(v);
|
||||
len2 = 1;
|
||||
}
|
||||
// the closer to the edge of the circle you are, the closer z is to zero.
|
||||
const z = Math.sqrt(1 - len2);
|
||||
v.push(z);
|
||||
return v;
|
||||
}
|
||||
|
||||
function computeVSphereRotation(start, end) {
|
||||
const u = vSphereUnitV3(start);
|
||||
const v = vSphereUnitV3(end);
|
||||
// Axis is in the scope of the Camera3D function so it can be used in keepSpinning.
|
||||
axis = CanvasKit.Vector.cross(u, v);
|
||||
const sinValue = CanvasKit.Vector.length(axis);
|
||||
const cosValue = CanvasKit.Vector.dot(u, v);
|
||||
|
||||
let m = new CanvasKit.M44.identity();
|
||||
if (Math.abs(sinValue) > 0.000000001) {
|
||||
m = CanvasKit.M44.rotatedUnitSinCos(
|
||||
CanvasKit.Vector.mulScalar(axis, 1/sinValue), sinValue, cosValue);
|
||||
const radians = Math.atan(cosValue / sinValue);
|
||||
spinRate = lastRadians - radians;
|
||||
lastRadians = radians;
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
function keepSpinning() {
|
||||
totalSpin += spinRate;
|
||||
clickRotation = CanvasKit.M44.rotated(axis, totalSpin);
|
||||
spinRate *= .998;
|
||||
if (spinRate < 0.01) {
|
||||
stopSpinning();
|
||||
}
|
||||
surface.requestAnimationFrame(drawFrame);
|
||||
}
|
||||
|
||||
function stopSpinning() {
|
||||
clearInterval(spinning);
|
||||
rotation = CanvasKit.M44.multiply(clickRotation, rotation);
|
||||
clickRotation = CanvasKit.M44.identity();
|
||||
}
|
||||
|
||||
function interact(e) {
|
||||
const type = e.type;
|
||||
let eventPos = [e.offsetX, e.offsetY];
|
||||
if (type === 'lostpointercapture' || type === 'pointerup' || type == 'pointerleave') {
|
||||
if (draggingLight) {
|
||||
draggingLight = false;
|
||||
} else if (mouseDown) {
|
||||
mouseDown = false;
|
||||
if (spinRate > 0.02) {
|
||||
stopSpinning();
|
||||
spinning = setInterval(keepSpinning, 30);
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
return;
|
||||
} else if (type === 'pointermove') {
|
||||
if (draggingLight) {
|
||||
lightLocation = eventPos;
|
||||
lightWorldPos = computeLightWorldPos();
|
||||
} else if (mouseDown) {
|
||||
lastMouse = eventPos;
|
||||
clickRotation = computeVSphereRotation(clickDown, lastMouse);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else if (type === 'pointerdown') {
|
||||
// Are we repositioning the light?
|
||||
if (CanvasKit.Vector.dist(eventPos, lightLocation) < lightIconRadius) {
|
||||
draggingLight = true;
|
||||
return;
|
||||
}
|
||||
stopSpinning();
|
||||
mouseDown = true;
|
||||
clickDown = eventPos;
|
||||
lastMouse = eventPos;
|
||||
}
|
||||
surface.requestAnimationFrame(drawFrame);
|
||||
};
|
||||
|
||||
document.getElementById('camera3d').addEventListener('pointermove', interact);
|
||||
document.getElementById('camera3d').addEventListener('pointerdown', interact);
|
||||
document.getElementById('camera3d').addEventListener('lostpointercapture', interact);
|
||||
document.getElementById('camera3d').addEventListener('pointerleave', interact);
|
||||
document.getElementById('camera3d').addEventListener('pointerup', interact);
|
||||
|
||||
surface.requestAnimationFrame(drawFrame);
|
||||
}
|
||||
|
||||
}
|
||||
document.head.appendChild(s);
|
||||
})();
|
||||
</script>
|
||||
|
||||
Lottie files courtesy of the lottiefiles.com community:
|
||||
[Lego Loader](https://www.lottiefiles.com/410-lego-loader),
|
||||
[I'm thirsty](https://www.lottiefiles.com/77-im-thirsty),
|
||||
[Confetti](https://www.lottiefiles.com/1370-confetti),
|
||||
[Onboarding](https://www.lottiefiles.com/1134-onboarding-1)
|
||||
|
||||
Test server
|
||||
-----------
|
||||
Test your code on our [CanvasKit Fiddle](https://jsfiddle.skia.org/canvaskit)
|
||||
|
||||
Download
|
||||
--------
|
||||
Get [CanvasKit on NPM](https://www.npmjs.com/package/canvaskit-wasm).
|
||||
Documentation and Typescript definitions are available in the `types/` subfolder
|
||||
of the npm package or from the
|
||||
[Skia repo](https://github.com/google/skia/tree/master/modules/canvaskit/canvaskit/types).
|
||||
|
||||
Check out the [quickstart guide](../modules/quickstart) as well.
|
||||
|
454
site2/docs/user/modules/particles.md
Normal file
454
site2/docs/user/modules/particles.md
Normal file
@ -0,0 +1,454 @@
|
||||
|
||||
---
|
||||
title: "Particles"
|
||||
linkTitle: "Particles"
|
||||
|
||||
weight: 40
|
||||
|
||||
---
|
||||
|
||||
|
||||
Skia’s particle module provides a way to quickly generate large numbers of
|
||||
drawing primitives with dynamic, animated behavior. Particles can be used to
|
||||
create effects like fireworks, spark trails, ambient “weather”, and much more.
|
||||
Nearly all properties and behavior are controlled by scripts written in Skia’s
|
||||
custom language, SkSL.
|
||||
|
||||
## Samples
|
||||
|
||||
<style>
|
||||
#demo canvas {
|
||||
border: 1px dashed #AAA;
|
||||
margin: 2px;
|
||||
}
|
||||
|
||||
figure {
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
figcaption > a {
|
||||
margin: 2px 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id=demo>
|
||||
<figure>
|
||||
<canvas id=trail width=400 height=400></canvas>
|
||||
<figcaption>
|
||||
Trail (Click and Drag!)
|
||||
</figcaption>
|
||||
</figure>
|
||||
<figure>
|
||||
<canvas id=cube width=400 height=400></canvas>
|
||||
<figcaption>
|
||||
<a href="https://particles.skia.org/?nameOrHash=@cube"
|
||||
target=_blank rel=noopener>Cuboid</a>
|
||||
</figcaption>
|
||||
</figure>
|
||||
<figure>
|
||||
<canvas id=confetti width=400 height=400></canvas>
|
||||
<figcaption>
|
||||
<a href="https://particles.skia.org/?nameOrHash=@confetti"
|
||||
target=_blank rel=noopener>Confetti</a>
|
||||
</figcaption>
|
||||
</figure>
|
||||
<figure>
|
||||
<canvas id=curves width=400 height=400></canvas>
|
||||
<figcaption>
|
||||
<a href="https://particles.skia.org/?nameOrHash=@spiral"
|
||||
target=_blank rel=noopener>Curves</a>
|
||||
</figcaption>
|
||||
</figure>
|
||||
<figure>
|
||||
<canvas id=fireworks width=400 height=400></canvas>
|
||||
<figcaption>
|
||||
<a href="https://particles.skia.org/?nameOrHash=@fireworks"
|
||||
target=_blank rel=noopener>Fireworks</a>
|
||||
</figcaption>
|
||||
</figure>
|
||||
<figure>
|
||||
<canvas id=text width=400 height=400></canvas>
|
||||
<figcaption>
|
||||
<a href="https://particles.skia.org/?nameOrHash=@text"
|
||||
target=_blank rel=noopener>Text</a>
|
||||
</figcaption>
|
||||
</figure>
|
||||
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
(function() {
|
||||
// Tries to load the WASM version if supported, shows error otherwise
|
||||
let s = document.createElement('script');
|
||||
var locate_file = '';
|
||||
if (window.WebAssembly && typeof window.WebAssembly.compile === 'function') {
|
||||
console.log('WebAssembly is supported!');
|
||||
locate_file = 'https://particles.skia.org/dist/';
|
||||
} else {
|
||||
console.log('WebAssembly is not supported (yet) on this browser.');
|
||||
document.getElementById('demo').innerHTML = "<div>WASM not supported by your browser. Try a recent version of Chrome, Firefox, Edge, or Safari.</div>";
|
||||
return;
|
||||
}
|
||||
s.src = locate_file + 'canvaskit.js';
|
||||
s.onload = () => {
|
||||
var CanvasKit = null;
|
||||
CanvasKitInit({
|
||||
locateFile: (file) => locate_file + file,
|
||||
}).then((CK) => {
|
||||
CanvasKit = CK;
|
||||
TrailExample(CanvasKit, 'trail', trail);
|
||||
ParticleExample(CanvasKit, 'confetti', confetti, 200, 200);
|
||||
ParticleExample(CanvasKit, 'curves', curves, 200, 300);
|
||||
ParticleExample(CanvasKit, 'cube', cube, 200, 200);
|
||||
ParticleExample(CanvasKit, 'fireworks', fireworks, 200, 300);
|
||||
ParticleExample(CanvasKit, 'text', text, 75, 250);
|
||||
});
|
||||
|
||||
function ParticleExample(CanvasKit, id, jsonData, cx, cy) {
|
||||
if (!CanvasKit || !jsonData) {
|
||||
return;
|
||||
}
|
||||
const surface = CanvasKit.MakeCanvasSurface(id);
|
||||
if (!surface) {
|
||||
console.error('Could not make surface');
|
||||
return;
|
||||
}
|
||||
const context = CanvasKit.currentContext();
|
||||
const canvas = surface.getCanvas();
|
||||
canvas.translate(cx, cy);
|
||||
|
||||
const particles = CanvasKit.MakeParticles(JSON.stringify(jsonData));
|
||||
particles.start(Date.now() / 1000.0, true);
|
||||
|
||||
function drawFrame(canvas) {
|
||||
particles.update(Date.now() / 1000.0);
|
||||
|
||||
canvas.clear(CanvasKit.WHITE);
|
||||
particles.draw(canvas);
|
||||
surface.requestAnimationFrame(drawFrame);
|
||||
}
|
||||
surface.requestAnimationFrame(drawFrame);
|
||||
}
|
||||
|
||||
const confetti ={
|
||||
"MaxCount": 200,
|
||||
"Drawable": {
|
||||
"Type": "SkCircleDrawable",
|
||||
"Radius": 8
|
||||
},
|
||||
"Code": [
|
||||
"void effectSpawn(inout Effect effect) {",
|
||||
" effect.lifetime = 2;",
|
||||
"}",
|
||||
"",
|
||||
"void effectUpdate(inout Effect effect) {",
|
||||
" if (effect.age < 0.25 || effect.age > 0.75) { effect.rate = 0; }",
|
||||
" else { effect.rate = 200; }",
|
||||
"}",
|
||||
"",
|
||||
"void spawn(inout Particle p) {",
|
||||
" int idx = int(rand(p.seed) * 4);",
|
||||
" p.color.rgb = (idx == 0) ? float3(0.87, 0.24, 0.11)",
|
||||
" : (idx == 1) ? float3(1.00, 0.90, 0.20)",
|
||||
" : (idx == 2) ? float3(0.44, 0.73, 0.24)",
|
||||
" : float3(0.38, 0.54, 0.95);",
|
||||
"",
|
||||
" p.lifetime = (1 - effect.age) * effect.lifetime;",
|
||||
" p.scale = mix(0.6, 1, rand(p.seed));",
|
||||
"}",
|
||||
"",
|
||||
"void update(inout Particle p) {",
|
||||
" p.color.a = 1 - p.age;",
|
||||
"",
|
||||
" float a = radians(rand(p.seed) * 360);",
|
||||
" float invAge = 1 - p.age;",
|
||||
" p.vel = float2(cos(a), sin(a)) * mix(250, 550, rand(p.seed)) * invAge * invAge;",
|
||||
"}",
|
||||
""
|
||||
],
|
||||
"Bindings": []
|
||||
};
|
||||
|
||||
const cube = {
|
||||
"MaxCount": 2000,
|
||||
"Drawable": {
|
||||
"Type": "SkCircleDrawable",
|
||||
"Radius": 4
|
||||
},
|
||||
"Code": [
|
||||
"void effectSpawn(inout Effect effect) {",
|
||||
" effect.lifetime = 2;",
|
||||
" effect.rate = 200;",
|
||||
"}",
|
||||
"",
|
||||
"void spawn(inout Particle p) {",
|
||||
" p.lifetime = 10;",
|
||||
"}",
|
||||
"",
|
||||
"float4x4 rx(float rad) {",
|
||||
" float c = cos(rad);",
|
||||
" float s = sin(rad);",
|
||||
" return float4x4(1, 0, 0, 0,",
|
||||
" 0, c, -s, 0,",
|
||||
" 0, s, c, 0,",
|
||||
" 0, 0, 0, 1);",
|
||||
"}",
|
||||
"",
|
||||
"float4x4 ry(float rad) {",
|
||||
" float c = cos(rad);",
|
||||
" float s = sin(rad);",
|
||||
" return float4x4(c, 0, -s, 0,",
|
||||
" 0, 1, 0, 0,",
|
||||
" s, 0, c, 0,",
|
||||
" 0, 0, 0, 1);",
|
||||
"}",
|
||||
"",
|
||||
"float4x4 rz(float rad) {",
|
||||
" float c = cos(rad);",
|
||||
" float s = sin(rad);",
|
||||
" return float4x4( c, s, 0, 0,",
|
||||
" -s, c, 0, 0,",
|
||||
" 0, 0, 1, 0,",
|
||||
" 0, 0, 0, 1);",
|
||||
"}",
|
||||
"",
|
||||
"void update(inout Particle p) {",
|
||||
" float3 pos = float3(rand(p.seed), rand(p.seed), rand(p.seed));",
|
||||
" if (rand(p.seed) < 0.33) {",
|
||||
" if (pos.x > 0.5) {",
|
||||
" pos.x = 1;",
|
||||
" p.color.rgb = float3(1, 0.2, 0.2);",
|
||||
" } else {",
|
||||
" pos.x = 0;",
|
||||
" p.color.rgb = float3(0.2, 1, 1);",
|
||||
" }",
|
||||
" } else if (rand(p.seed) < 0.5) {",
|
||||
" if (pos.y > 0.5) {",
|
||||
" pos.y = 1;",
|
||||
" p.color.rgb = float3(0.2, 0.2, 1);",
|
||||
" } else {",
|
||||
" pos.y = 0;",
|
||||
" p.color.rgb = float3(1, 1, 0.2);",
|
||||
" }",
|
||||
" } else {",
|
||||
" if (pos.z > 0.5) {",
|
||||
" pos.z = 1;",
|
||||
" p.color.rgb = float3(0.2, 1, 0.2);",
|
||||
" } else {",
|
||||
" pos.z = 0;",
|
||||
" p.color.rgb = float3(1, 0.2, 1);",
|
||||
" }",
|
||||
" }",
|
||||
"",
|
||||
" float s = effect.age * 2 - 1;",
|
||||
" s = s < 0 ? -s : s;",
|
||||
"",
|
||||
" pos = pos * 2 - 1;",
|
||||
" pos = mix(pos, normalize(pos), s);",
|
||||
" pos = pos * 100;",
|
||||
"",
|
||||
" float age = float(effect.loop) + effect.age;",
|
||||
" float4x4 mat = rx(age * radians(60))",
|
||||
" * ry(age * radians(70))",
|
||||
" * rz(age * radians(80));",
|
||||
" pos = (mat * float4(pos, 1)).xyz;",
|
||||
"",
|
||||
" p.pos.x = pos.x;",
|
||||
" p.pos.y = pos.y;",
|
||||
" p.scale = ((pos.z + 50) / 100 + 0.5) / 2;",
|
||||
"}",
|
||||
""
|
||||
],
|
||||
"Bindings": []
|
||||
};
|
||||
|
||||
const curves = {
|
||||
"MaxCount": 1000,
|
||||
"Drawable": {
|
||||
"Type": "SkCircleDrawable",
|
||||
"Radius": 2
|
||||
},
|
||||
"Code": [
|
||||
"void effectSpawn(inout Effect effect) {",
|
||||
" effect.rate = 200;",
|
||||
" effect.color = float4(1, 0, 0, 1);",
|
||||
"}",
|
||||
"",
|
||||
"void spawn(inout Particle p) {",
|
||||
" p.lifetime = 3 + rand(p.seed);",
|
||||
" p.vel.y = -50;",
|
||||
"}",
|
||||
"",
|
||||
"void update(inout Particle p) {",
|
||||
" float w = mix(15, 3, p.age);",
|
||||
" p.pos.x = sin(radians(p.age * 320)) * mix(25, 10, p.age) + mix(-w, w, rand(p.seed));",
|
||||
" if (rand(p.seed) < 0.5) { p.pos.x = -p.pos.x; }",
|
||||
"",
|
||||
" p.color.g = (mix(75, 220, p.age) + mix(-30, 30, rand(p.seed))) / 255;",
|
||||
"}",
|
||||
""
|
||||
],
|
||||
"Bindings": []
|
||||
};
|
||||
|
||||
const fireworks = {
|
||||
"MaxCount": 300,
|
||||
"Drawable": {
|
||||
"Type": "SkCircleDrawable",
|
||||
"Radius": 3
|
||||
},
|
||||
"Code": [
|
||||
"void effectSpawn(inout Effect effect) {",
|
||||
" // Phase one: Launch",
|
||||
" effect.lifetime = 4;",
|
||||
" effect.rate = 120;",
|
||||
" float a = radians(mix(-20, 20, rand(effect.seed)) - 90);",
|
||||
" float s = mix(200, 220, rand(effect.seed));",
|
||||
" effect.vel.x = cos(a) * s;",
|
||||
" effect.vel.y = sin(a) * s;",
|
||||
" effect.color.rgb = float3(rand(effect.seed), rand(effect.seed), rand(effect.seed));",
|
||||
" effect.pos.x = 0;",
|
||||
" effect.pos.y = 0;",
|
||||
" effect.scale = 0.25; // Also used as particle behavior flag",
|
||||
"}",
|
||||
"",
|
||||
"void effectUpdate(inout Effect effect) {",
|
||||
" if (effect.age > 0.5 && effect.rate > 0) {",
|
||||
" // Phase two: Explode",
|
||||
" effect.rate = 0;",
|
||||
" effect.burst = 50;",
|
||||
" effect.scale = 1;",
|
||||
" } else {",
|
||||
" effect.vel.y += dt * 90;",
|
||||
" }",
|
||||
"}",
|
||||
"",
|
||||
"void spawn(inout Particle p) {",
|
||||
" bool explode = p.scale == 1;",
|
||||
"",
|
||||
" p.lifetime = explode ? (2 + rand(p.seed) * 0.5) : 0.5;",
|
||||
" float a = radians(rand(p.seed) * 360);",
|
||||
" float s = explode ? mix(90, 100, rand(p.seed)) : mix(5, 10, rand(p.seed));",
|
||||
" p.vel.x = cos(a) * s;",
|
||||
" p.vel.y = sin(a) * s;",
|
||||
"}",
|
||||
"",
|
||||
"void update(inout Particle p) {",
|
||||
" p.color.a = 1 - p.age;",
|
||||
" if (p.scale == 1) {",
|
||||
" p.vel.y += dt * 50;",
|
||||
" }",
|
||||
"}",
|
||||
""
|
||||
],
|
||||
"Bindings": []
|
||||
};
|
||||
|
||||
const text = {
|
||||
"MaxCount": 2000,
|
||||
"Drawable": {
|
||||
"Type": "SkCircleDrawable",
|
||||
"Radius": 1
|
||||
},
|
||||
"Code": [
|
||||
"void effectSpawn(inout Effect effect) {",
|
||||
" effect.rate = 1000;",
|
||||
"}",
|
||||
"",
|
||||
"void spawn(inout Particle p) {",
|
||||
" p.lifetime = mix(1, 3, rand(p.seed));",
|
||||
" float a = radians(mix(250, 290, rand(p.seed)));",
|
||||
" float s = mix(10, 30, rand(p.seed));",
|
||||
" p.vel.x = cos(a) * s;",
|
||||
" p.vel.y = sin(a) * s;",
|
||||
" p.pos = text(rand(p.seed)).xy;",
|
||||
"}",
|
||||
"",
|
||||
"void update(inout Particle p) {",
|
||||
" float4 startColor = float4(1, 0.196, 0.078, 1);",
|
||||
" float4 endColor = float4(1, 0.784, 0.078, 1);",
|
||||
" p.color = mix(startColor, endColor, p.age);",
|
||||
"}",
|
||||
""
|
||||
],
|
||||
"Bindings": [
|
||||
{
|
||||
"Type": "SkTextBinding",
|
||||
"Name": "text",
|
||||
"Text": "SKIA",
|
||||
"FontSize": 96
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
function preventScrolling(canvas) {
|
||||
canvas.addEventListener('touchmove', (e) => {
|
||||
// Prevents touch events in the canvas from scrolling the canvas.
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
});
|
||||
}
|
||||
|
||||
function TrailExample(CanvasKit, id, jsonData) {
|
||||
if (!CanvasKit || !jsonData) {
|
||||
return;
|
||||
}
|
||||
const surface = CanvasKit.MakeCanvasSurface(id);
|
||||
if (!surface) {
|
||||
console.error('Could not make surface');
|
||||
return;
|
||||
}
|
||||
const context = CanvasKit.currentContext();
|
||||
const canvas = surface.getCanvas();
|
||||
|
||||
const particles = CanvasKit.MakeParticles(JSON.stringify(jsonData));
|
||||
particles.start(Date.now() / 1000.0, true);
|
||||
|
||||
function drawFrame(canvas) {
|
||||
particles.update(Date.now() / 1000.0);
|
||||
|
||||
canvas.clear(CanvasKit.WHITE);
|
||||
particles.draw(canvas);
|
||||
surface.requestAnimationFrame(drawFrame);
|
||||
}
|
||||
surface.requestAnimationFrame(drawFrame);
|
||||
|
||||
let interact = (e) => {
|
||||
particles.setPosition([e.offsetX, e.offsetY]);
|
||||
particles.setRate(e.pressure * 1000);
|
||||
};
|
||||
document.getElementById('trail').addEventListener('pointermove', interact);
|
||||
document.getElementById('trail').addEventListener('pointerdown', interact);
|
||||
document.getElementById('trail').addEventListener('pointerup', interact);
|
||||
preventScrolling(document.getElementById('trail'));
|
||||
}
|
||||
|
||||
const trail = {
|
||||
"MaxCount": 2000,
|
||||
"Drawable": {
|
||||
"Type": "SkCircleDrawable",
|
||||
"Radius": 4
|
||||
},
|
||||
"Code": [
|
||||
"void spawn(inout Particle p) {",
|
||||
" p.lifetime = 2 + rand(p.seed);",
|
||||
" float a = radians(rand(p.seed) * 360);",
|
||||
" p.vel = float2(cos(a), sin(a)) * mix(5, 15, rand(p.seed));",
|
||||
" p.scale = mix(0.25, 0.75, rand(p.seed));",
|
||||
"}",
|
||||
"",
|
||||
"void update(inout Particle p) {",
|
||||
" p.color.r = p.age;",
|
||||
" p.color.g = 1 - p.age;",
|
||||
"}",
|
||||
""
|
||||
],
|
||||
"Bindings": []
|
||||
};
|
||||
|
||||
}
|
||||
document.head.appendChild(s);
|
||||
})();
|
||||
</script>
|
||||
|
896
site2/docs/user/modules/pathkit.md
Normal file
896
site2/docs/user/modules/pathkit.md
Normal file
@ -0,0 +1,896 @@
|
||||
---
|
||||
title: 'PathKit - Geometry in the Browser'
|
||||
linkTitle: 'PathKit - Geometry in the Browser'
|
||||
|
||||
weight: 30
|
||||
---
|
||||
|
||||
Skia has made its [SkPath](../api/SkPath_Reference) object and many related
|
||||
methods available to JS clients (e.g. Web Browsers) using WebAssembly and
|
||||
asm.js.
|
||||
|
||||
## Features
|
||||
|
||||
PathKit is still under rapid development, so the exact API is subject to change.
|
||||
|
||||
The primary features are:
|
||||
|
||||
- API compatibility (e.g. drop-in replacement) with
|
||||
[Path2D](https://developer.mozilla.org/en-US/docs/Web/API/Path2D)
|
||||
- Can output to SVG / Canvas / Path2D
|
||||
- Exposes a variety of path effects:
|
||||
|
||||
<style>
|
||||
canvas.patheffect {
|
||||
border: 1px dashed #AAA;
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id=effects>
|
||||
<canvas class=patheffect id=canvas1 title="Plain: A drawn star with overlapping solid lines"></canvas>
|
||||
<canvas class=patheffect id=canvas2 title="Dash: A drawn star with overlapping dashed lines"></canvas>
|
||||
<canvas class=patheffect id=canvas3 title="Trim: A portion of a drawn star with overlapping solid lines"></canvas>
|
||||
<canvas class=patheffect id=canvas4 title="Simplify: A drawn star with non-overlapping solid lines."></canvas>
|
||||
<canvas class=patheffect id=canvas5 title="Stroke: A drawn star with non-overlapping solid lines stroked at various thicknesses and with square edges"></canvas>
|
||||
<canvas class=patheffect id=canvas6 title="Grow: A drawn star's expanding outline"></canvas>
|
||||
<canvas class=patheffect id=canvas7 title="Shrink: A solid drawn star shrunk down"></canvas>
|
||||
<canvas class=patheffect id=canvasTransform title="Transform: A drawn star moved and rotated by an Affine Matrix"></canvas>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
(function() {
|
||||
// Tries to load the WASM version if supported, then falls back to asmjs
|
||||
let s = document.createElement('script');
|
||||
if (window.WebAssembly && typeof window.WebAssembly.compile === 'function') {
|
||||
console.log('WebAssembly is supported! Using the wasm version of PathKit');
|
||||
window.__pathkit_locate_file = 'https://unpkg.com/pathkit-wasm@0.6.0/bin/';
|
||||
} else {
|
||||
console.log('WebAssembly is not supported (yet) on this browser. Using the asmjs version of PathKit');
|
||||
window.__pathkit_locate_file = 'https://unpkg.com/pathkit-asmjs@0.6.0/bin/';
|
||||
}
|
||||
s.src = window.__pathkit_locate_file+'pathkit.js';
|
||||
s.onload = () => {
|
||||
// TODO(kjlubick) remove .ready() when we update the version served here.
|
||||
try {
|
||||
PathKitInit({
|
||||
locateFile: (file) => window.__pathkit_locate_file+file,
|
||||
}).ready().then((PathKit) => {
|
||||
// Code goes here using PathKit
|
||||
PathEffectsExample(PathKit);
|
||||
MatrixTransformExample(PathKit);
|
||||
});
|
||||
}
|
||||
catch(error) {
|
||||
console.warn(error, 'falling back to image');
|
||||
document.getElementById('effects').innerHTML = '<img width=800 src="./PathKit_effects.png"/>'
|
||||
}
|
||||
}
|
||||
|
||||
document.head.appendChild(s);
|
||||
|
||||
function setCanvasSize(ctx, width, height) {
|
||||
ctx.canvas.width = width;
|
||||
ctx.canvas.height = height;
|
||||
}
|
||||
|
||||
function drawStar(path) {
|
||||
let R = 115.2, C = 128.0;
|
||||
path.moveTo(C + R + 22, C);
|
||||
for (let i = 1; i < 8; i++) {
|
||||
let a = 2.6927937 * i;
|
||||
path.lineTo(C + R * Math.cos(a) + 22, C + R * Math.sin(a));
|
||||
}
|
||||
path.closePath();
|
||||
return path;
|
||||
}
|
||||
|
||||
function PathEffectsExample(PathKit) {
|
||||
let effects = [
|
||||
// no-op
|
||||
(path) => path,
|
||||
// dash
|
||||
(path, counter) => path.dash(10, 3, counter/5),
|
||||
// trim (takes optional 3rd param for returning the trimmed part
|
||||
// or the complement)
|
||||
(path, counter) => path.trim((counter/100) % 1, 0.8, false),
|
||||
// simplify
|
||||
(path) => path.simplify(),
|
||||
// stroke
|
||||
(path, counter) => path.stroke({
|
||||
width: 10 * (Math.sin(counter/30) + 1),
|
||||
join: PathKit.StrokeJoin.BEVEL,
|
||||
cap: PathKit.StrokeCap.BUTT,
|
||||
miter_limit: 1,
|
||||
}),
|
||||
// "offset effect", that is, making a border around the shape.
|
||||
(path, counter) => {
|
||||
let orig = path.copy();
|
||||
path.stroke({
|
||||
width: 10 + (counter / 4) % 50,
|
||||
join: PathKit.StrokeJoin.ROUND,
|
||||
cap: PathKit.StrokeCap.SQUARE,
|
||||
})
|
||||
.op(orig, PathKit.PathOp.DIFFERENCE);
|
||||
orig.delete();
|
||||
},
|
||||
(path, counter) => {
|
||||
let simplified = path.simplify().copy();
|
||||
path.stroke({
|
||||
width: 2 + (counter / 2) % 100,
|
||||
join: PathKit.StrokeJoin.BEVEL,
|
||||
cap: PathKit.StrokeCap.BUTT,
|
||||
})
|
||||
.op(simplified, PathKit.PathOp.REVERSE_DIFFERENCE);
|
||||
simplified.delete();
|
||||
}
|
||||
];
|
||||
|
||||
let names = ["(plain)", "Dash", "Trim", "Simplify", "Stroke", "Grow", "Shrink"];
|
||||
|
||||
let counter = 0;
|
||||
function frame() {
|
||||
counter++;
|
||||
for (let i = 0; i < effects.length; i++) {
|
||||
let path = PathKit.NewPath();
|
||||
drawStar(path);
|
||||
|
||||
// The transforms apply directly to the path.
|
||||
effects[i](path, counter);
|
||||
|
||||
let ctx = document.getElementById(`canvas${i+1}`);
|
||||
if (!ctx) {
|
||||
return;
|
||||
} else {
|
||||
ctx = ctx.getContext('2d');
|
||||
}
|
||||
setCanvasSize(ctx, 300, 300);
|
||||
ctx.strokeStyle = '#3c597a';
|
||||
ctx.fillStyle = '#3c597a';
|
||||
if (i >=4 ) {
|
||||
ctx.fill(path.toPath2D(), path.getFillTypeString());
|
||||
} else {
|
||||
ctx.stroke(path.toPath2D());
|
||||
}
|
||||
|
||||
ctx.font = '42px monospace';
|
||||
|
||||
let x = 150-ctx.measureText(names[i]).width/2;
|
||||
ctx.strokeText(names[i], x, 290);
|
||||
|
||||
path.delete();
|
||||
}
|
||||
window.requestAnimationFrame(frame);
|
||||
}
|
||||
window.requestAnimationFrame(frame);
|
||||
}
|
||||
|
||||
function MatrixTransformExample(PathKit) {
|
||||
// Creates an animated star that twists and moves.
|
||||
let ctx = document.getElementById('canvasTransform').getContext('2d');
|
||||
setCanvasSize(ctx, 300, 300);
|
||||
ctx.strokeStyle = '#3c597a';
|
||||
|
||||
let path = drawStar(PathKit.NewPath());
|
||||
// TODO(kjlubick): Perhaps expose some matrix helper functions to allow
|
||||
// clients to build their own matrices like this?
|
||||
// These matrices represent a 2 degree rotation and a 1% scale factor.
|
||||
let scaleUp = [1.0094, -0.0352, 3.1041,
|
||||
0.0352, 1.0094, -6.4885,
|
||||
0 , 0 , 1];
|
||||
|
||||
let scaleDown = [ 0.9895, 0.0346, -2.8473,
|
||||
-0.0346, 0.9895, 6.5276,
|
||||
0 , 0 , 1];
|
||||
|
||||
let i = 0;
|
||||
function frame(){
|
||||
i++;
|
||||
if (Math.round(i/100) % 2) {
|
||||
path.transform(scaleDown);
|
||||
} else {
|
||||
path.transform(scaleUp);
|
||||
}
|
||||
|
||||
ctx.clearRect(0, 0, 300, 300);
|
||||
ctx.stroke(path.toPath2D());
|
||||
|
||||
ctx.font = '42px monospace';
|
||||
let x = 150-ctx.measureText('Transform').width/2;
|
||||
ctx.strokeText('Transform', x, 290);
|
||||
|
||||
window.requestAnimationFrame(frame);
|
||||
}
|
||||
window.requestAnimationFrame(frame);
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
||||
## Example Code
|
||||
|
||||
The best place to look for examples on how to use PathKit would be in the
|
||||
[example.html](https://github.com/google/skia/blob/master/modules/pathkit/npm-wasm/example.html#L45),
|
||||
which comes in the npm package.
|
||||
|
||||
## Download the library
|
||||
|
||||
See the the npm page for either the
|
||||
[WebAssembly](https://www.npmjs.com/package/pathkit-wasm) version or the
|
||||
[asm.js](https://www.npmjs.com/package/pathkit-asmjs) version for details on
|
||||
downloading and getting started.
|
||||
|
||||
WebAssembly has faster load times and better overall performance but is
|
||||
currently supported by Chrome, Firefox, Edge, and Safari. The asm.js version
|
||||
should run anywhere JavaScript does.
|
||||
|
||||
## API
|
||||
|
||||
The primary feature of the library is the `SkPath` object. It can be created:
|
||||
|
||||
- From the SVG string of a path `PathKit.FromSVGString(str)`
|
||||
- From a 2D array of verbs and arguments `PathKit.FromCmds(cmds)`
|
||||
- From `PathKit.NewPath()` (It will be blank)
|
||||
- As a copy of an existing `SkPath` with `path.copy()` or
|
||||
`PathKit.NewPath(path)`
|
||||
|
||||
It can be exported as:
|
||||
|
||||
- An SVG string `path.toSVGString()`
|
||||
- A [Path2D](https://developer.mozilla.org/en-US/docs/Web/API/Path2D) object
|
||||
`path.toPath2D()`
|
||||
- Directly to a canvas 2D context `path.toCanvas(ctx)`
|
||||
- A 2D array of verbs and arguments `path.toCmds()`
|
||||
|
||||
Once an SkPath object has been made, it can be interacted with in the following
|
||||
ways:
|
||||
|
||||
- expanded by any of the Path2D operations (`moveTo`, `lineTo`, `rect`, `arc`,
|
||||
etc)
|
||||
- combined with other paths using `op` or `PathKit.MakeFromOp(p1, p2, op)`. For
|
||||
example, `path1.op(path2, PathKit.PathOp.INTERSECT)` will set path1 to be the
|
||||
area represented by where path1 and path2 overlap (intersect).
|
||||
`PathKit.MakeFromOp(path1, path2, PathKit.PathOp.INTERSECT)` will do the same
|
||||
but returned as a new `SkPath` object.
|
||||
- adjusted with some of the effects (`trim`, `dash`, `stroke`, etc)
|
||||
|
||||
**Important**: Any objects (`SkPath`, `SkOpBuilder`, etc) that are created must
|
||||
be cleaned up with `path.delete()` when they leave the scope to avoid leaking
|
||||
the memory in the WASM heap. This includes any of the constructors, `copy()`, or
|
||||
any function prefixed with "make".
|
||||
|
||||
### PathKit
|
||||
|
||||
#### `FromSVGString(str)`
|
||||
|
||||
**str** - `String` representing an
|
||||
[SVGPath](https://www.w3schools.com/graphics/svg_path.asp)
|
||||
|
||||
Returns an `SkPath` with the same verbs and arguments as the SVG string, or
|
||||
`null` on a failure.
|
||||
|
||||
Example:
|
||||
|
||||
let path = PathKit.FromSVGString('M150 0 L75 200 L225 200 Z');
|
||||
// path represents a triangle
|
||||
// don't forget to do path.delete() when it goes out of scope.
|
||||
|
||||
#### `FromCmds(cmds)`
|
||||
|
||||
**cmds** - `Array<Array<Number>>`, a 2D array of commands, where a command is a
|
||||
verb followed by its arguments.
|
||||
|
||||
Returns an `SkPath` with the verbs and arguments from the list or `null` on a
|
||||
failure.
|
||||
|
||||
This can be faster than calling `.moveTo()`, `.lineTo()`, etc many times.
|
||||
|
||||
Example:
|
||||
|
||||
let cmds = [
|
||||
[PathKit.MOVE_VERB, 0, 10],
|
||||
[PathKit.LINE_VERB, 30, 40],
|
||||
[PathKit.QUAD_VERB, 20, 50, 45, 60],
|
||||
];
|
||||
let path = PathKit.FromCmds(cmds);
|
||||
// path is the same as if a user had done
|
||||
// let path = PathKit.NewPath().moveTo(0, 10).lineTo(30, 40).quadTo(20, 50, 45, 60);
|
||||
// don't forget to do path.delete() when it goes out of scope.
|
||||
|
||||
#### `NewPath()`
|
||||
|
||||
Returns an empty `SkPath` object.
|
||||
|
||||
Example:
|
||||
|
||||
let path = PathKit.NewPath();
|
||||
path.moveTo(0, 10)
|
||||
.lineTo(30, 40)
|
||||
.quadTo(20, 50, 45, 60);
|
||||
// don't forget to do path.delete() when it goes out of scope.
|
||||
// Users can also do let path = new PathKit.SkPath();
|
||||
|
||||
#### `NewPath(pathToCopy)`
|
||||
|
||||
**pathToCopy** - SkPath, a path to make a copy of.
|
||||
|
||||
Returns a `SkPath` that is a copy of the passed in `SkPath`.
|
||||
|
||||
Example:
|
||||
|
||||
let otherPath = ...;
|
||||
let clone = PathKit.NewPath(otherPath);
|
||||
clone.simplify();
|
||||
// don't forget to do clone.delete() when it goes out of scope.
|
||||
// Users can also do let clone = new PathKit.SkPath(otherPath);
|
||||
// or let clone = otherPath.copy();
|
||||
|
||||
#### `MakeFromOp(pathOne, pathTwo, op)`
|
||||
|
||||
**pathOne** - `SkPath`, a path. <br> **pathTwo** - `SkPath`, a path. <br>
|
||||
**op** - `PathOp`, an op to apply
|
||||
|
||||
Returns a new `SkPath` that is the result of applying the given PathOp to the
|
||||
first and second path (order matters).
|
||||
|
||||
Example:
|
||||
|
||||
let pathOne = PathKit.NewPath().moveTo(0, 20).lineTo(10, 10).lineTo(20, 20).close();
|
||||
let pathTwo = PathKit.NewPath().moveTo(10, 20).lineTo(20, 10).lineTo(30, 20).close();
|
||||
let mountains = PathKit.MakeFromOp(pathOne, pathTwo, PathKit.PathOp.UNION);
|
||||
// don't forget to do mountains.delete() when it goes out of scope.
|
||||
// Users can also do pathOne.op(pathTwo, PathKit.PathOp.UNION);
|
||||
// to have the resulting path be stored to pathOne and avoid allocating another object.
|
||||
|
||||
#### `cubicYFromX(cpx1, cpy1, cpx2, cpy2, X)`
|
||||
|
||||
**cpx1, cpy1, cpx2, cpy2** - `Number`, coordinates for control points. <br>
|
||||
**X** - `Number`, The X coordinate for which to find the corresponding Y
|
||||
coordinate.
|
||||
|
||||
Fast evaluation of a cubic ease-in / ease-out curve. This is defined as a
|
||||
parametric cubic curve inside the unit square. Makes the following assumptions:
|
||||
|
||||
- pt[0] is implicitly { 0, 0 }
|
||||
- pt[3] is implicitly { 1, 1 }
|
||||
- pts[1, 2] are inside the unit square
|
||||
|
||||
This returns the Y coordinate for the given X coordinate.
|
||||
|
||||
#### `cubicPtFromT(cpx1, cpy1, cpx2, cpy2, T)`
|
||||
|
||||
**cpx1, cpy1, cpx2, cpy2** - `Number`, coordinates for control points. <br>
|
||||
**T** - `Number`, The T param for which to find the corresponding (X, Y)
|
||||
coordinates.
|
||||
|
||||
Fast evaluation of a cubic ease-in / ease-out curve. This is defined as a
|
||||
parametric cubic curve inside the unit square. Makes the following assumptions:
|
||||
|
||||
- pt[0] is implicitly { 0, 0 }
|
||||
- pt[3] is implicitly { 1, 1 }
|
||||
- pts[1, 2] are inside the unit square
|
||||
|
||||
This returns the (X, Y) coordinate for the given T value as a length 2 array.
|
||||
|
||||
### SkPath (object)
|
||||
|
||||
#### `addPath(otherPath)`
|
||||
|
||||
**otherPath** - `SkPath`, a path to append to this path
|
||||
|
||||
Adds the given path to `this` and then returns `this` for chaining purposes.
|
||||
|
||||
#### `addPath(otherPath, transform)`
|
||||
|
||||
**otherPath** - `SkPath`, a path to append to this path. <br> **transform** -
|
||||
[SVGMatrix](https://developer.mozilla.org/en-US/docs/Web/API/SVGMatrix), a
|
||||
transform to apply to otherPath before appending it.
|
||||
|
||||
Adds the given path to `this` after applying the transform and then returns
|
||||
`this` for chaining purposes. See
|
||||
[Path2D.addPath()](https://developer.mozilla.org/en-US/docs/Web/API/Path2D/addPath)
|
||||
for more details.
|
||||
|
||||
#### `addPath(otherPath, a, b, c, d, e, f)`
|
||||
|
||||
**otherPath** - `SkPath`, a path to append to this path. <br> **a, b, c, d, e,
|
||||
f** - `Number`, the six components of an
|
||||
[SVGMatrix](https://developer.mozilla.org/en-US/docs/Web/API/SVGMatrix), which
|
||||
define the transform to apply to otherPath before appending it.
|
||||
|
||||
Adds the given path to `this` after applying the transform and then returns
|
||||
`this` for chaining purposes. See
|
||||
[Path2D.addPath()](https://developer.mozilla.org/en-US/docs/Web/API/Path2D/addPath)
|
||||
for more details.
|
||||
|
||||
Example:
|
||||
|
||||
let box = PathKit.NewPath().rect(0, 0, 100, 100);
|
||||
let moreBoxes = PathKit.NewPath();
|
||||
// add box un-transformed (i.e. at 0, 0)
|
||||
moreBoxes.addPath(box)
|
||||
// the params fill out a 2d matrix like:
|
||||
// a c e
|
||||
// b d f
|
||||
// 0 0 1
|
||||
// add box 300 points to the right
|
||||
.addPath(box, 1, 0, 0, 1, 300, 0)
|
||||
// add a box shrunk by 50% in both directions
|
||||
.addPath(box, 0.5, 0, 0, 0.5, 0, 0);
|
||||
// moreBoxes now has 3 paths appended to it
|
||||
|
||||
#### `addPath(otherPath, scaleX, skewX, transX, skewY, scaleY, transY, pers0, pers1, pers2)`
|
||||
|
||||
**otherPath** - `SkPath`, a path to append to this path. <br> **scaleX, skewX,
|
||||
transX, skewY, scaleY, transY, pers0, pers1, pers2** - `Number`, the nine
|
||||
components of an
|
||||
[Affine Matrix](https://en.wikipedia.org/wiki/Transformation_matrix#Affine_transformations),
|
||||
which define the transform to apply to otherPath before appending it.
|
||||
|
||||
Adds the given path to `this` after applying the transform and then returns
|
||||
`this` for chaining purposes.
|
||||
|
||||
Example:
|
||||
|
||||
let box = PathKit.NewPath().rect(0, 0, 100, 100);
|
||||
let moreBoxes = PathKit.NewPath();
|
||||
// add box un-transformed (i.e. at 0, 0)
|
||||
moreBoxes.addPath(box)
|
||||
// add box 300 points to the right
|
||||
.addPath(box, 1, 0, 0,
|
||||
0, 1, 300,
|
||||
0, 0 ,1)
|
||||
// add a box shrunk by 50% in both directions
|
||||
.addPath(box, 0.5, 0, 0,
|
||||
0, 0.5, 0,
|
||||
0, 0, 1)
|
||||
// moreBoxes now has 3 paths appended to it
|
||||
|
||||
#### `arc(x, y, radius, startAngle, endAngle, ccw=false)`
|
||||
|
||||
**x, y** - `Number`, The coordinates of the arc's center. <br> **radius** -
|
||||
`Number`, The radius of the arc. <br> **startAngle, endAngle** - `Number`, the
|
||||
start and end of the angle, measured clockwise from the positive x axis and in
|
||||
radians. <br> **ccw** - `Boolean`, optional argument specifying if the arc
|
||||
should be drawn counter-clockwise between **startAngle** and **endAngle**
|
||||
instead of clockwise, the default.
|
||||
|
||||
Adds the described arc to `this` then returns `this` for chaining purposes. See
|
||||
[Path2D.arc()](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/arc)
|
||||
for more details.
|
||||
|
||||
Example:
|
||||
|
||||
let path = PathKit.NewPath();
|
||||
path.moveTo(20, 120);
|
||||
.arc(20, 120, 18, 0, 1.75 * Math.PI);
|
||||
.lineTo(20, 120);
|
||||
// path looks like a pie with a 1/8th slice removed.
|
||||
|
||||
#### `arcTo(x1, y1, x2, y2, radius)`
|
||||
|
||||
**x1, y1, x2, y2** - `Number`, The coordinates defining the control points. <br>
|
||||
**radius** - `Number`, The radius of the arc.
|
||||
|
||||
Adds the described arc to `this` (appending a line, if needed) then returns
|
||||
`this` for chaining purposes. See
|
||||
[Path2D.arcTo()](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/arcTo)
|
||||
for more details.
|
||||
|
||||
#### `close()` or `closePath()`
|
||||
|
||||
Returns the pen to the start of the current sub-path, then returns `this` for
|
||||
chaining purposes. See
|
||||
[Path2D.closePath()](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/closePath)
|
||||
for more details.
|
||||
|
||||
#### `computeTightBounds()`
|
||||
|
||||
Returns an `SkRect` that represents the minimum and maximum area of `this` path.
|
||||
See
|
||||
[SkPath reference](https://skia.org/user/api/SkPath_Reference#SkPath_computeTightBounds)
|
||||
for more details.
|
||||
|
||||
#### `conicTo(x1, y1, x2, y2, w)`
|
||||
|
||||
**x1, y1, x2, y2** - `Number`, The coordinates defining the control point and
|
||||
the end point. <br> **w** - `Number`, The weight of the conic.
|
||||
|
||||
Adds the described conic line to `this` (appending a line, if needed) then
|
||||
returns `this` for chaining purposes. See
|
||||
[SkPath reference](https://skia.org/user/api/SkPath_Reference#SkPath_conicTo)
|
||||
for more details.
|
||||
|
||||
#### `copy()`
|
||||
|
||||
Return a copy of `this` path.
|
||||
|
||||
#### `cubicTo(cp1x, cp1y, cp2x, cp2y, x, y)` or `bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)`
|
||||
|
||||
**cp1x, cp1y, cp2x, cp2y** - `Number`, The coordinates defining the control
|
||||
points. <br> **x,y** - `Number`, The coordinates defining the end point
|
||||
|
||||
Adds the described cubic line to `this` (appending a line, if needed) then
|
||||
returns `this` for chaining purposes. See
|
||||
[Path2D.bezierCurveTo](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/bezierCurveTo)
|
||||
for more details.
|
||||
|
||||
#### `dash(on, off, phase)`
|
||||
|
||||
**on, off** - `Number`, The number of pixels the dash should be on (drawn) and
|
||||
off (blank). <br> **phase** - `Number`, The number of pixels the on/off should
|
||||
be offset (mod **on** + **off**)
|
||||
|
||||
Applies a dashed path effect to `this` then returns `this` for chaining
|
||||
purposes. See the "Dash" effect above for a visual example.
|
||||
|
||||
Example:
|
||||
|
||||
let box = PathKit.NewPath().rect(0, 0, 100, 100);
|
||||
box.dash(20, 10, 3);
|
||||
// box is now a dashed rectangle that will draw for 20 pixels, then
|
||||
// stop for 10 pixels. Since phase is 3, the first line won't start
|
||||
// at (0, 0), but 3 pixels around the path (3, 0)
|
||||
|
||||
#### `ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, ccw=false)`
|
||||
|
||||
**x, y** - `Number`, The coordinates of the center of the ellipse. <br>
|
||||
**radiusX, radiusY** - `Number`, The radii in the X and Y directions. <br>
|
||||
**rotation** - `Number`, The rotation in radians of this ellipse. <br>
|
||||
**startAngle, endAngle** - `Number`, the starting and ending angles of which to
|
||||
draw, measured in radians from the positive x axis. <br> **ccw** - `Boolean`,
|
||||
optional argument specifying if the ellipse should be drawn counter-clockwise
|
||||
between **startAngle** and **endAngle** instead of clockwise, the default.
|
||||
|
||||
Adds the described ellipse to `this` then returns `this` for chaining purposes.
|
||||
See
|
||||
[Path2D.ellipse](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/ellipse)
|
||||
for more details.
|
||||
|
||||
#### `equals(otherPath)`
|
||||
|
||||
**otherPath** - `SkPath`, the path to compare to.
|
||||
|
||||
Returns a `Boolean` value based on if `this` path is equal to **otherPath**.
|
||||
|
||||
#### `getBounds()`
|
||||
|
||||
Returns an `SkRect` that represents the minimum and maximum area of `this` path.
|
||||
See
|
||||
[SkPath reference](https://skia.org/user/api/SkPath_Reference#SkPath_getBounds)
|
||||
for more details.
|
||||
|
||||
#### `getFillType()`
|
||||
|
||||
Returns a `FillType` based on what this path is. This defaults to
|
||||
`PathKit.FillType.WINDING`, but may change with `op()` or `simplify()`.
|
||||
|
||||
Clients will typically want `getFillTypeString()` because that value can be
|
||||
passed directly to an SVG or Canvas.
|
||||
|
||||
#### `getFillTypeString()`
|
||||
|
||||
Returns a `String` representing the fillType of `this` path. The values are
|
||||
either "nonzero" or "evenodd".
|
||||
|
||||
Example:
|
||||
|
||||
let path = ...;
|
||||
let ctx = document.getElementById('canvas1').getContext('2d');
|
||||
ctx.strokeStyle = 'green';
|
||||
ctx.fill(path.toPath2D(), path.getFillTypeString());
|
||||
|
||||
#### `moveTo(x, y)`
|
||||
|
||||
**x, y** - `Number`, The coordinates of where the pen should be moved to.
|
||||
|
||||
Moves the pen (without drawing) to the given coordinates then returns `this` for
|
||||
chaining purposes. See
|
||||
[Path2D.moveTo](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/moveTo)
|
||||
for more details.
|
||||
|
||||
#### `lineTo(x, y)`
|
||||
|
||||
**x, y** - `Number`, The coordinates of where the pen should be moved to.
|
||||
|
||||
Draws a straight line to the given coordinates then returns `this` for chaining
|
||||
purposes. See
|
||||
[Path2D.lineTo](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineTo)
|
||||
for more details.
|
||||
|
||||
#### `op(otherPath, operation)`
|
||||
|
||||
**otherPath** - `SkPath`, The other path to be combined with `this`. <br>
|
||||
**operation** - `PathOp`, The operation to apply to the two paths.
|
||||
|
||||
Combines otherPath into `this` path with the given operation and returns `this`
|
||||
for chaining purposes.
|
||||
|
||||
Example:
|
||||
|
||||
let pathOne = PathKit.NewPath().moveTo(0, 20).lineTo(10, 10).lineTo(20, 20).close();
|
||||
let pathTwo = PathKit.NewPath().moveTo(10, 20).lineTo(20, 10).lineTo(30, 20).close();
|
||||
// Combine the two triangles to look like two mountains
|
||||
let mountains = pathOne.copy().op(pathOne, pathTwo, PathKit.PathOp.UNION);
|
||||
// set pathOne to be the small triangle where pathOne and pathTwo overlap
|
||||
pathOne.op(pathOne, pathTwo, PathKit.PathOp.INTERSECT);
|
||||
// since copy() was called, don't forget to call delete() on mountains.
|
||||
|
||||
#### `quadTo(cpx, cpy, x, y)` or `quadraticCurveTo(cpx, cpy, x, y)`
|
||||
|
||||
**cpx, cpy** - `Number`, The coordinates for the control point. <br> **x, y** -
|
||||
`Number`, The coordinates for the end point.
|
||||
|
||||
Draws a quadratic Bézier curve with the given coordinates then returns `this`
|
||||
for chaining purposes. See
|
||||
[Path2D.quadraticCurveTo](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/quadraticCurveTo)
|
||||
for more details.
|
||||
|
||||
#### `rect(x, y, w, h)`
|
||||
|
||||
**x, y** - `Number`, The coordinates of the upper-left corner of the rectangle.
|
||||
<br> **w, h** - `Number`, The width and height of the rectangle
|
||||
|
||||
Draws a rectangle on `this`, then returns `this` for chaining purposes. See
|
||||
[Path2D.rect](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/rect)
|
||||
for more details.
|
||||
|
||||
#### `setFillType(fillType)`
|
||||
|
||||
**fillType** - `FillType`, the new fillType.
|
||||
|
||||
Set the fillType of the path. See
|
||||
[SkPath reference](https://skia.org/user/api/SkPath_Reference#SkPath_FillType)
|
||||
for more details.
|
||||
|
||||
#### `simplify()`
|
||||
|
||||
Set `this` path to a set of _non-overlapping_ contours that describe the same
|
||||
area as the original path. See the "Simplify" effect above for a visual example.
|
||||
|
||||
#### `stroke(opts)`
|
||||
|
||||
**opts** - `StrokeOpts`, contains the options for stroking.
|
||||
|
||||
Strokes `this` path out with the given options. This can be used for a variety
|
||||
of effects. See the "Stroke", "Grow", and "Shrink" effects above for visual
|
||||
examples.
|
||||
|
||||
Example:
|
||||
|
||||
let box = PathKit.NewPath().rect(0, 0, 100, 100);
|
||||
// Stroke the path with width 10 and rounded corners
|
||||
let rounded = box.copy().stroke({width: 10, join: PathKit.StrokeJoin.ROUND});
|
||||
// Grow effect, that is, a 20 pixel expansion around the box.
|
||||
let grow = box.copy().stroke({width: 20}).op(box, PathKit.PathOp.DIFFERENCE);
|
||||
// Shrink effect, in which we subtract away from the original
|
||||
let simplified = box.copy().simplify(); // sometimes required for complicated paths
|
||||
let shrink = box.copy().stroke({width: 15, cap: PathKit.StrokeCap.BUTT})
|
||||
.op(simplified, PathKit.PathOp.REVERSE_DIFFERENCE);
|
||||
// Don't forget to call delete() on each of the copies!
|
||||
|
||||
#### `toCanvas(ctx)`
|
||||
|
||||
**ctx** - `Canvas2DContext`, Canvas on which to draw the path.
|
||||
|
||||
Draws `this` path on the passed in
|
||||
[Canvas Context](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D).
|
||||
|
||||
Example:
|
||||
|
||||
let box = PathKit.NewPath().rect(0, 0, 100, 100);
|
||||
let ctx = document.getElementById('canvas1').getContext('2d');
|
||||
ctx.strokeStyle = 'green';
|
||||
ctx.beginPath();
|
||||
box.toCanvas(ctx);
|
||||
ctx.stroke(); // could also ctx.fill()
|
||||
|
||||
#### `toCmds()`
|
||||
|
||||
Returns a 2D Array of verbs and args. See `PathKit.FromCmds()` for more details.
|
||||
|
||||
#### `toPath2D()`
|
||||
|
||||
Returns a [Path2D](https://developer.mozilla.org/en-US/docs/Web/API/Path2D)
|
||||
object that has the same operations as `this` path.
|
||||
|
||||
Example:
|
||||
|
||||
let box = PathKit.NewPath().rect(0, 0, 100, 100);
|
||||
let ctx = document.getElementById('canvas1').getContext('2d');
|
||||
ctx.strokeStyle = 'green';
|
||||
ctx.stroke(box.toPath2D());
|
||||
|
||||
#### `toSVGString()`
|
||||
|
||||
Returns a `String` representing an
|
||||
[SVGPath](https://www.w3schools.com/graphics/svg_path.asp) based on `this` path.
|
||||
|
||||
Example:
|
||||
|
||||
let box = PathKit.NewPath().rect(0, 0, 100, 100);
|
||||
let svg = document.getElementById('svg1');
|
||||
let newPath = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
||||
newPath.setAttribute('stroke', 'green');
|
||||
newPath.setAttribute('fill', 'white');
|
||||
newPath.setAttribute('d', box.toSVGString());
|
||||
svg.appendChild(newPath);
|
||||
|
||||
#### `transform(matr)`
|
||||
|
||||
**matr** - `SkMatrix`, i.e. an `Array<Number>` of the nine numbers of an Affine
|
||||
Transform Matrix.
|
||||
|
||||
Applies the specified
|
||||
[transform](https://en.wikipedia.org/wiki/Transformation_matrix#Affine_transformations)
|
||||
to `this` and then returns `this` for chaining purposes.
|
||||
|
||||
#### `transform(scaleX, skewX, transX, skewY, scaleY, transY, pers0, pers1, pers2)`
|
||||
|
||||
**scaleX, skewX, transX, skewY, scaleY, transY, pers0, pers1, pers2** -
|
||||
`Number`, the nine numbers of an Affine Transform Matrix.
|
||||
|
||||
Applies the specified
|
||||
[transform](https://en.wikipedia.org/wiki/Transformation_matrix#Affine_transformations)
|
||||
to `this` and then returns `this` for chaining purposes.
|
||||
|
||||
Example:
|
||||
|
||||
let path = PathKit.NewPath().rect(0, 0, 100, 100);
|
||||
// scale up the path by 5x
|
||||
path.transform([5, 0, 0,
|
||||
0, 5, 0,
|
||||
0, 0, 1]);
|
||||
// move the path 75 px to the right.
|
||||
path.transform(1, 0, 75,
|
||||
0, 1, 0,
|
||||
0, 0, 1);
|
||||
|
||||
#### `trim(startT, stopT, isComplement=false)`
|
||||
|
||||
**startT, stopT** - `Number`, values in [0, 1] that indicate the start and stop
|
||||
"percentages" of the path to draw <br> **isComplement** - `Boolean`, If the
|
||||
complement of the trimmed section should be drawn instead of the areas between
|
||||
**startT** and **stopT**.
|
||||
|
||||
Sets `this` path to be a subset of the original path, then returns `this` for
|
||||
chaining purposes. See the "Trim" effect above for a visual example.
|
||||
|
||||
Example:
|
||||
|
||||
let box = PathKit.NewPath().rect(0, 0, 100, 100);
|
||||
box.trim(0.25, 1.0);
|
||||
// box is now the 3 segments that look like a U
|
||||
// (the top segment has been removed).
|
||||
|
||||
### SkOpBuilder (object)
|
||||
|
||||
This object enables chaining multiple PathOps together. Create one with
|
||||
`let builder = new PathKit.SkOpBuilder();` When created, the internal state is
|
||||
"empty path". Don't forget to call `delete()` on both the builder and the result
|
||||
of `resolve()`
|
||||
|
||||
#### `add(path, operation)`
|
||||
|
||||
**path** - `SkPath`, The path to be combined with the given rule. <br>
|
||||
**operation** - `PathOp`, The operation to apply to the two paths.
|
||||
|
||||
Adds a path and the operand to the builder.
|
||||
|
||||
#### `make()` or `resolve()`
|
||||
|
||||
Creates and returns a new `SkPath` based on all the given paths and operands.
|
||||
|
||||
Don't forget to call `.delete()` on the returned path when it goes out of scope.
|
||||
|
||||
### SkMatrix (struct)
|
||||
|
||||
`SkMatrix` translates between a C++ struct and a JS Array. It basically takes a
|
||||
nine element 1D Array and turns it into a 3x3 2D Affine Matrix.
|
||||
|
||||
### SkRect (struct)
|
||||
|
||||
`SkRect` translates between a C++ struct and a JS Object with the following keys
|
||||
(all values are `Number`:
|
||||
|
||||
- **fLeft**: x coordinate of top-left corner
|
||||
- **fTop**: y coordinate of top-left corner
|
||||
- **fRight**: x coordinate of bottom-right corner
|
||||
- **fBottom**: y coordinate of bottom-rightcorner
|
||||
|
||||
### StrokeOpts (struct)
|
||||
|
||||
`StrokeOpts` translates between a C++ struct and a JS Object with the following
|
||||
keys:
|
||||
|
||||
- **width**, `Number` the width of the lines of the path. Default 1.
|
||||
- **miter_limit**, `Number`, the miter limit. Defautl 4. See
|
||||
[SkPaint reference](https://skia.org/user/api/SkPaint_Reference#Miter_Limit)
|
||||
for more details.
|
||||
- **join**, `StrokeJoin`, the join to use. Default `PathKit.StrokeJoin.MITER`.
|
||||
See
|
||||
[SkPaint reference](https://skia.org/user/api/SkPaint_Reference#SkPaint_Join)
|
||||
for more details.
|
||||
- **cap**, `StrokeCap`, the cap to use. Default `PathKit.StrokeCap.BUTT`. See
|
||||
[SkPaint reference](https://skia.org/user/api/SkPaint_Reference#Stroke_Cap)
|
||||
for more details.
|
||||
|
||||
### PathOp (enum)
|
||||
|
||||
The following enum values are exposed. They are essentially constant objects,
|
||||
differentiated by their `.value` property.
|
||||
|
||||
- `PathKit.PathOp.DIFFERENCE`
|
||||
- `PathKit.PathOp.INTERSECT`
|
||||
- `PathKit.PathOp.REVERSE_DIFFERENCE`
|
||||
- `PathKit.PathOp.UNION`
|
||||
- `PathKit.PathOp.XOR`
|
||||
|
||||
These are used in `PathKit.MakeFromOp()` and `SkPath.op()`.
|
||||
|
||||
### FillType (enum)
|
||||
|
||||
The following enum values are exposed. They are essentially constant objects,
|
||||
differentiated by their `.value` property.
|
||||
|
||||
- `PathKit.FillType.WINDING` (also known as nonzero)
|
||||
- `PathKit.FillType.EVENODD`
|
||||
- `PathKit.FillType.INVERSE_WINDING`
|
||||
- `PathKit.FillType.INVERSE_EVENODD`
|
||||
|
||||
These are used by `SkPath.getFillType()` and `SkPath.setFillType()`, but
|
||||
generally clients will want `SkPath.getFillTypeString()`.
|
||||
|
||||
### StrokeJoin (enum)
|
||||
|
||||
The following enum values are exposed. They are essentially constant objects,
|
||||
differentiated by their `.value` property.
|
||||
|
||||
- `PathKit.StrokeJoin.MITER`
|
||||
- `PathKit.StrokeJoin.ROUND`
|
||||
- `PathKit.StrokeJoin.BEVEL`
|
||||
|
||||
See
|
||||
[SkPaint reference](https://skia.org/user/api/SkPaint_Reference#SkPaint_Join)
|
||||
for more details.
|
||||
|
||||
### StrokeCap (enum)
|
||||
|
||||
The following enum values are exposed. They are essentially constant objects,
|
||||
differentiated by their `.value` property.
|
||||
|
||||
- `PathKit.StrokeCap.BUTT`
|
||||
- `PathKit.StrokeCap.ROUND`
|
||||
- `PathKit.StrokeCap.SQUARE`
|
||||
|
||||
See [SkPaint reference](https://skia.org/user/api/SkPaint_Reference#Stroke_Cap)
|
||||
for more details.
|
||||
|
||||
### Constants
|
||||
|
||||
The following constants are exposed:
|
||||
|
||||
- `PathKit.MOVE_VERB` = 0
|
||||
- `PathKit.LINE_VERB` = 1
|
||||
- `PathKit.QUAD_VERB` = 2
|
||||
- `PathKit.CONIC_VERB` = 3
|
||||
- `PathKit.CUBIC_VERB` = 4
|
||||
- `PathKit.CLOSE_VERB` = 5
|
||||
|
||||
These are only needed for `PathKit.FromCmds()`.
|
||||
|
||||
### Functions for testing only
|
||||
|
||||
#### `PathKit.LTRBRect(left, top, right, bottom)`
|
||||
|
||||
**left** - `Number`, x coordinate of top-left corner of the `SkRect`. <br>
|
||||
**top** - `Number`, y coordinate of top-left corner of the `SkRect`. <br>
|
||||
**right** - `Number`, x coordinate of bottom-right corner of the `SkRect`. <br>
|
||||
**bottom** - `Number`, y coordinate of bottom-right corner of the `SkRect`.
|
||||
|
||||
Returns an `SkRect` object with the given params.
|
||||
|
||||
#### `SkPath.dump()`
|
||||
|
||||
Prints all the verbs and arguments to the console. Only available on Debug and
|
||||
Test builds.
|
373
site2/docs/user/modules/quickstart.md
Normal file
373
site2/docs/user/modules/quickstart.md
Normal file
@ -0,0 +1,373 @@
|
||||
|
||||
---
|
||||
title: "CanvasKit - Quickstart"
|
||||
linkTitle: "CanvasKit - Quickstart"
|
||||
|
||||
---
|
||||
|
||||
|
||||
CanvasKit is a wasm module that uses Skia to draw to canvas elements a more advance feature set than the canvas API.
|
||||
|
||||
Minimal application
|
||||
-------------------
|
||||
|
||||
This example is a minimal Canvaskit application that draws a rounded rect for one frame.
|
||||
It pulls the wasm binary from unpkg.com but you can also build and host it yourself.
|
||||
|
||||
<!--?prettify?-->
|
||||
``` js
|
||||
<canvas id=foo width=300 height=300></canvas>
|
||||
|
||||
<script type="text/javascript"
|
||||
src="https://unpkg.com/canvaskit-wasm@0.19.0/bin/canvaskit.js"></script>
|
||||
<script type="text/javascript">
|
||||
const ckLoaded = CanvasKitInit({
|
||||
locateFile: (file) => 'https://unpkg.com/canvaskit-wasm@0.19.0/bin/'+file});
|
||||
ckLoaded.then((CanvasKit) => {
|
||||
const surface = CanvasKit.MakeCanvasSurface('foo');
|
||||
|
||||
const paint = new CanvasKit.Paint();
|
||||
paint.setColor(CanvasKit.Color4f(0.9, 0, 0, 1.0));
|
||||
paint.setStyle(CanvasKit.PaintStyle.Stroke);
|
||||
paint.setAntiAlias(true);
|
||||
const rr = CanvasKit.RRectXY(CanvasKit.LTRBRect(10, 60, 210, 260), 25, 15);
|
||||
|
||||
function draw(canvas) {
|
||||
canvas.clear(CanvasKit.WHITE);
|
||||
canvas.drawRRect(rr, paint);
|
||||
}
|
||||
surface.drawOnce(draw);
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
<canvas id=foo width=300 height=300></canvas>
|
||||
|
||||
<script type="text/javascript"
|
||||
src="https://unpkg.com/canvaskit-wasm@0.19.0/bin/canvaskit.js"></script>
|
||||
<script type="text/javascript">
|
||||
const ckLoaded = CanvasKitInit({
|
||||
locateFile: (file) => 'https://unpkg.com/canvaskit-wasm@0.19.0/bin/'+file});
|
||||
ckLoaded.then((CanvasKit) => {
|
||||
const surface = CanvasKit.MakeCanvasSurface('foo');
|
||||
|
||||
const paint = new CanvasKit.Paint();
|
||||
paint.setColor(CanvasKit.Color4f(0.9, 0, 0, 1.0));
|
||||
paint.setStyle(CanvasKit.PaintStyle.Stroke);
|
||||
paint.setAntiAlias(true);
|
||||
const rr = CanvasKit.RRectXY(CanvasKit.LTRBRect(10, 60, 210, 260), 25, 15);
|
||||
|
||||
function draw(canvas) {
|
||||
canvas.clear(CanvasKit.WHITE);
|
||||
canvas.drawRRect(rr, paint);
|
||||
}
|
||||
surface.drawOnce(draw);
|
||||
});
|
||||
</script>
|
||||
|
||||
Let's break it down into parts and explain what they are doing:
|
||||
|
||||
`<canvas id=foo width=300 height=300></canvas>` Creates the canvas to which CanvasKit will draw.
|
||||
This element is where we control the width and height of the drawing buffer, while it's css style
|
||||
would control any scaling applied after drawing to those pixels. Despite using a canvas element,
|
||||
CanvasKit isn't calling the HTML canvas's own draw methods. It is using this canvas element to
|
||||
get a WebGL2 context and performing most of the drawing work in C++ code compiled to WebAssembly,
|
||||
then sending commands to the GPU at the end of each frame.
|
||||
|
||||
<!--?prettify?-->
|
||||
``` html
|
||||
<script type="text/javascript"
|
||||
src="https://unpkg.com/canvaskit-wasm@0.19.0/bin/canvaskit.js"></script>
|
||||
```
|
||||
and
|
||||
|
||||
<!--?prettify?-->
|
||||
``` js
|
||||
const ckLoaded = CanvasKitInit({
|
||||
locateFile: (file) => 'https://unpkg.com/canvaskit-wasm@0.19.0/bin/'+file});
|
||||
ckLoaded.then((CanvasKit) => {
|
||||
```
|
||||
are loading the canvaskit helper js and wasm binary respectively. CanvasKitInit accepts a function
|
||||
for allowing you to alter the path where it will try to find `canvaskit.wasm` and returns a promise
|
||||
that resolves with the loaded module, which we typically name `CanvasKit`.
|
||||
|
||||
<!--?prettify?-->
|
||||
``` js
|
||||
const surface = CanvasKit.MakeCanvasSurface('foo');
|
||||
```
|
||||
Creates a Surface associated with the HTML canvas element above.
|
||||
Hardware acceleration is the default behavior, but can be overridden by calling
|
||||
`MakeSWCanvasSurface` instead. `MakeCanvasSurface` is also where alternative color spaces or gl
|
||||
attrtributes can be specified.
|
||||
|
||||
<!--?prettify?-->
|
||||
``` js
|
||||
const paint = new CanvasKit.Paint();
|
||||
paint.setColor(CanvasKit.Color4f(0.9, 0, 0, 1.0));
|
||||
paint.setStyle(CanvasKit.PaintStyle.Stroke);
|
||||
paint.setAntiAlias(true);
|
||||
const rr = CanvasKit.RRectXY(CanvasKit.LTRBRect(10, 60, 210, 260), 25, 15);
|
||||
```
|
||||
Creates a paint, a description of how to fill or stroke rects, paths, text and other geometry in
|
||||
canvaskit. `rr` is a rounded rect, with corners having a radius of 25 in the x axis, and 15 pixels
|
||||
in the y axis.
|
||||
|
||||
<!--?prettify?-->
|
||||
``` js
|
||||
function draw(canvas) {
|
||||
canvas.clear(CanvasKit.WHITE);
|
||||
canvas.drawRRect(rr, paint);
|
||||
}
|
||||
```
|
||||
Defines a function that will draw our frame. The function is provided a Canvas object on which we
|
||||
make draw calls. One to clear the entire canvas, and one to draw the rounded rect with the
|
||||
paint from above.
|
||||
|
||||
We also delete the paint object. CanvasKit objects created with `new` or methods prefixed with
|
||||
`make` must be deleted for the wasm memory to be released. Javascript's GC will not take care of
|
||||
it automatically. `rr` is just an array, wasn't created with `new` and doesn't point to any WASM
|
||||
memory, so we don't have to call delete on it.
|
||||
|
||||
<!--?prettify?-->
|
||||
``` js
|
||||
surface.drawOnce(draw);
|
||||
paint.delete()
|
||||
```
|
||||
Hand the drawing function to `surface.drawOnce` which makes the calls and flushes the surface.
|
||||
Upon flushing, Skia will batch and send WebGL commands, making visible changes appear onscreen.
|
||||
This example draws once and disposes of the surface. As promised, it is is a minimal
|
||||
application.
|
||||
|
||||
Basic Draw Loop
|
||||
---------------
|
||||
|
||||
What if we need to redraw to our canvas every frame? This example
|
||||
bounces a rounded rect around like a 90s screensaver.
|
||||
|
||||
<!--?prettify?-->
|
||||
``` js
|
||||
ckLoaded.then((CanvasKit) => {
|
||||
const surface = CanvasKit.MakeCanvasSurface('foo2');
|
||||
|
||||
const paint = new CanvasKit.Paint();
|
||||
paint.setColor(CanvasKit.Color4f(0.9, 0, 0, 1.0));
|
||||
paint.setStyle(CanvasKit.PaintStyle.Stroke);
|
||||
paint.setAntiAlias(true);
|
||||
// const rr = CanvasKit.RRectXY(CanvasKit.LTRBRect(10, 60, 210, 260), 25, 15);
|
||||
const w = 100; // size of rect
|
||||
const h = 60;
|
||||
let x = 10; // initial position of top left corner.
|
||||
let y = 60;
|
||||
let dirX = 1; // box is always moving at a constant speed in one of the four diagonal directions
|
||||
let dirY = 1;
|
||||
|
||||
function drawFrame(canvas) {
|
||||
// boundary check
|
||||
if (x < 0 || x+w > 300) {
|
||||
dirX *= -1; // reverse x direction when hitting side walls
|
||||
}
|
||||
if (y < 0 || y+h > 300) {
|
||||
dirY *= -1; // reverse y direction when hitting top and bottom walls
|
||||
}
|
||||
// move
|
||||
x += dirX;
|
||||
y += dirY;
|
||||
|
||||
canvas.clear(CanvasKit.WHITE);
|
||||
const rr = CanvasKit.RRectXY(CanvasKit.LTRBRect(x, y, x+w, y+h), 25, 15);
|
||||
canvas.drawRRect(rr, paint);
|
||||
surface.requestAnimationFrame(drawFrame);
|
||||
}
|
||||
surface.requestAnimationFrame(drawFrame);
|
||||
});
|
||||
```
|
||||
|
||||
<canvas id=foo2 width=300 height=300></canvas>
|
||||
|
||||
<script type="text/javascript">
|
||||
ckLoaded.then((CanvasKit) => {
|
||||
const surface = CanvasKit.MakeCanvasSurface('foo2');
|
||||
|
||||
const paint = new CanvasKit.Paint();
|
||||
paint.setColor(CanvasKit.Color4f(0.9, 0, 0, 1.0));
|
||||
paint.setStyle(CanvasKit.PaintStyle.Stroke);
|
||||
paint.setAntiAlias(true);
|
||||
// const rr = CanvasKit.RRectXY(CanvasKit.LTRBRect(10, 60, 210, 260), 25, 15);
|
||||
const w = 100; // size of rect
|
||||
const h = 60;
|
||||
let x = 10; // initial position of top left corner.
|
||||
let y = 60;
|
||||
// The box is always moving at a constant speed in one of the four diagonal directions
|
||||
let dirX = 1;
|
||||
let dirY = 1;
|
||||
|
||||
function drawFrame(canvas) {
|
||||
// boundary check
|
||||
if (x < 0 || x+w > 300) {
|
||||
dirX *= -1; // reverse x direction when hitting side walls
|
||||
}
|
||||
if (y < 0 || y+h > 300) {
|
||||
dirY *= -1; // reverse y direction when hitting top and bottom walls
|
||||
}
|
||||
// move
|
||||
x += dirX;
|
||||
y += dirY;
|
||||
|
||||
canvas.clear(CanvasKit.WHITE);
|
||||
const rr = CanvasKit.RRectXY(CanvasKit.LTRBRect(x, y, x+w, y+h), 25, 15);
|
||||
canvas.drawRRect(rr, paint);
|
||||
surface.requestAnimationFrame(drawFrame);
|
||||
}
|
||||
surface.requestAnimationFrame(drawFrame);
|
||||
});
|
||||
</script>
|
||||
|
||||
The main difference here is that we define a function to be called before each frame is drawn and
|
||||
pass it to `surface.requestAnimationFrame(drawFrame);` That callback is handed a `canvas` and
|
||||
flushing is taken care of.
|
||||
|
||||
<!--?prettify?-->
|
||||
``` js
|
||||
function drawFrame(canvas) {
|
||||
canvas.clear(CanvasKit.WHITE);
|
||||
// code to update and draw the frame goes here
|
||||
surface.requestAnimationFrame(drawFrame);
|
||||
}
|
||||
surface.requestAnimationFrame(drawFrame);
|
||||
```
|
||||
|
||||
Creates a function to serve as our main drawing loop. Each time a frame is about to be rendered
|
||||
(the browser will typically target 60fps), our function is called, we clear the canvas with white,
|
||||
redraw the round rect, and call `surface.requestAnimationFrame(drawFrame)` registering the
|
||||
function to be called again before the next frame.
|
||||
|
||||
`surface.requestAnimationFrame(drawFrame)` combines window.requestAnimationFrame with
|
||||
`surface.flush()` and should be used in all the same ways. If your application would only make
|
||||
visible changes as a result of mouse events,
|
||||
don't call `surface.requestAnimationFrame` at the end of your drawFrame function. Call it only
|
||||
after handling mouse input.
|
||||
|
||||
Text Shaping
|
||||
------------
|
||||
|
||||
One of the biggest features that CanvasKit offers over the HTML Canvas API is paragraph shaping.
|
||||
To use text your applicatoin, supply a font file and use Promise.all to run your code when both
|
||||
CanvasKit and the font file are ready.
|
||||
|
||||
<!--?prettify?-->
|
||||
``` js
|
||||
const loadFont = fetch('https://storage.googleapis.com/skia-cdn/misc/Roboto-Regular.ttf')
|
||||
.then((response) => response.arrayBuffer());
|
||||
|
||||
Promise.all([ckLoaded, loadFont]).then(([CanvasKit, robotoData]) => {
|
||||
const surface = CanvasKit.MakeCanvasSurface('foo3');
|
||||
const canvas = surface.getCanvas();
|
||||
canvas.clear(CanvasKit.Color4f(0.9, 0.9, 0.9, 1.0));
|
||||
|
||||
const fontMgr = CanvasKit.FontMgr.FromData([robotoData]);
|
||||
const paraStyle = new CanvasKit.ParagraphStyle({
|
||||
textStyle: {
|
||||
color: CanvasKit.BLACK,
|
||||
fontFamilies: ['Roboto'],
|
||||
fontSize: 28,
|
||||
},
|
||||
textAlign: CanvasKit.TextAlign.Left,
|
||||
});
|
||||
const text = 'Any sufficiently entrenched technology is indistinguishable from Javascript';
|
||||
const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
|
||||
builder.addText(text);
|
||||
const paragraph = builder.build();
|
||||
paragraph.layout(290); // width in pixels to use when wrapping text
|
||||
canvas.drawParagraph(paragraph, 10, 10);
|
||||
surface.flush();
|
||||
});
|
||||
```
|
||||
|
||||
<canvas id=foo3 width=300 height=300></canvas>
|
||||
|
||||
<script type="text/javascript">
|
||||
const loadFont = fetch('https://storage.googleapis.com/skia-cdn/misc/Roboto-Regular.ttf')
|
||||
.then((response) => response.arrayBuffer());
|
||||
|
||||
Promise.all([ckLoaded, loadFont]).then(([CanvasKit, robotoData]) => {
|
||||
const surface = CanvasKit.MakeCanvasSurface('foo3');
|
||||
const canvas = surface.getCanvas();
|
||||
canvas.clear(CanvasKit.Color4f(0.9, 0.9, 0.9, 1.0));
|
||||
|
||||
const fontMgr = CanvasKit.FontMgr.FromData([robotoData]);
|
||||
const paraStyle = new CanvasKit.ParagraphStyle({
|
||||
textStyle: {
|
||||
color: CanvasKit.BLACK,
|
||||
fontFamilies: ['Roboto'],
|
||||
fontSize: 28,
|
||||
},
|
||||
textAlign: CanvasKit.TextAlign.Left,
|
||||
});
|
||||
const text = 'Any sufficiently entrenched technology is indistinguishable from Javascript';
|
||||
const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
|
||||
builder.addText(text);
|
||||
const paragraph = builder.build();
|
||||
paragraph.layout(290); // width in pixels to use when wrapping text
|
||||
canvas.drawParagraph(paragraph, 10, 10);
|
||||
surface.flush();
|
||||
});
|
||||
</script>
|
||||
|
||||
<!--?prettify?-->
|
||||
``` js
|
||||
const fontMgr = CanvasKit.FontMgr.FromData([robotoData]);
|
||||
```
|
||||
Creates an object that provides fonts by name to various text facilities in CanvasKit. You could
|
||||
load more than one font in this statement if needed.
|
||||
|
||||
<!--?prettify?-->
|
||||
``` js
|
||||
const paraStyle = new CanvasKit.ParagraphStyle({
|
||||
textStyle: {
|
||||
color: CanvasKit.BLACK,
|
||||
fontFamilies: ['Roboto'],
|
||||
fontSize: 28,
|
||||
},
|
||||
textAlign: CanvasKit.TextAlign.Left,
|
||||
});
|
||||
```
|
||||
Specifies the style of the text. The font's name, Roboto, will be used to fetch it from the font
|
||||
manager. You can specify either (color) or (foregroundColor and backgroundColor) in order to have
|
||||
a highlight. For the full documentation of the API, check out the Typescript definitions in the
|
||||
`types/` subfolder of the npm package or in the
|
||||
[Skia repo](https://github.com/google/skia/tree/master/modules/canvaskit/canvaskit/types).
|
||||
|
||||
<!--?prettify?-->
|
||||
``` js
|
||||
const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
|
||||
builder.addText(text);
|
||||
const paragraph = builder.build();
|
||||
```
|
||||
Next, we create a `ParagraphBuilder` with a style, add some text, and finalize it with `build()`.
|
||||
Alternatively, we could use multiple `TextStyle`s in one paragraph with
|
||||
|
||||
<!--?prettify?-->
|
||||
``` js
|
||||
const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
|
||||
builder.addText(text1);
|
||||
const boldTextStyle = CanvasKit.TextStyle({
|
||||
color: CanvasKit.BLACK,
|
||||
fontFamilies: ['Roboto'],
|
||||
fontSize: 28,
|
||||
fontStyle: {'weight': CanvasKit.FontWeight.Bold},
|
||||
})
|
||||
builder.pushStyle(boldTextStyle);
|
||||
builder.addText(text2);
|
||||
builder.pop();
|
||||
builder.addText(text3);
|
||||
const paragraph = builder.build();
|
||||
```
|
||||
Finally, we *layout* the paragraph, meaning wrap the text to a particular width, and draw it to
|
||||
the canvas with
|
||||
|
||||
<!--?prettify?-->
|
||||
``` js
|
||||
paragraph.layout(290); // width in pixels to use when wrapping text
|
||||
canvas.drawParagraph(paragraph, 10, 10); // (x, y) position of left top corner of paragraph.
|
||||
```
|
||||
|
89
site2/docs/user/modules/skottie.md
Normal file
89
site2/docs/user/modules/skottie.md
Normal file
@ -0,0 +1,89 @@
|
||||
|
||||
---
|
||||
title: "Skottie - Lottie Animation Player"
|
||||
linkTitle: "Skottie - Lottie Animation Player"
|
||||
|
||||
weight: 10
|
||||
|
||||
---
|
||||
|
||||
|
||||
Skia now offers a performant, secure native player for JSON animations derived
|
||||
from the Bodymovin plugin for After Effects. It can be used on any platform
|
||||
where you are using Skia, including Android & iOS.
|
||||
|
||||
The player aims to build upon the Lottie player widely used for animations
|
||||
today, improving on the performance, feature set, and platform cohesiveness for
|
||||
our clients. We are big fans of the Bodymovin format and where possible,
|
||||
contributing advancements back to Bodymovin/Lottie.
|
||||
|
||||
<br>
|
||||
|
||||
Sample JSON animations
|
||||
----------------------
|
||||
|
||||
Here are some test samples rendering with Skia's animation player:
|
||||
|
||||
<a href="https://skottie.skia.org/e6741dda67629da1f80c254dad3df865">
|
||||
<skottie-inline-sk src="https://skottie.skia.org/_/j/e6741dda67629da1f80c254dad3df865" width=200 height=200></skottie-inline-sk>
|
||||
</a>
|
||||
<a href="https://skottie.skia.org/ffea72cf6be48fa061671c124ed7789c">
|
||||
<skottie-inline-sk src="https://skottie.skia.org/_/j/ffea72cf6be48fa061671c124ed7789c" width=200 height=200></skottie-inline-sk>
|
||||
</a>
|
||||
<a href="https://skottie.skia.org/00e850cdbed7304985eaefe98a4e8a9c">
|
||||
<skottie-inline-sk src="https://skottie.skia.org/_/j/00e850cdbed7304985eaefe98a4e8a9c" width=200 height=200></skottie-inline-sk>
|
||||
</a>
|
||||
<a href="https://skottie.skia.org/e1aca009d5ebec9bd122b87b018bb673">
|
||||
<skottie-inline-sk src="https://skottie.skia.org/_/j/e1aca009d5ebec9bd122b87b018bb673" width=200 height=200></skottie-inline-sk>
|
||||
</a>
|
||||
<a href="https://skottie.skia.org/821fd79dd7437b97ba891e7a00970a06">
|
||||
<skottie-inline-sk src="https://skottie.skia.org/_/j/821fd79dd7437b97ba891e7a00970a06" width=200 height=200></skottie-inline-sk>
|
||||
</a>
|
||||
<a href="https://skottie.skia.org/ad63f250084685c96edd9b52ae2f436b">
|
||||
<skottie-inline-sk src="https://skottie.skia.org/_/j/ad63f250084685c96edd9b52ae2f436b" width=200 height=200></skottie-inline-sk>
|
||||
</a>
|
||||
<a href="https://skottie.skia.org/40f78ddc751c16348a08e1d61d3e78b1">
|
||||
<skottie-inline-sk src="https://skottie.skia.org/_/j/40f78ddc751c16348a08e1d61d3e78b1" width=200 height=200></skottie-inline-sk>
|
||||
</a>
|
||||
<a href="https://skottie.skia.org/fc42db7c75741437b5cb0e90b3febc65">
|
||||
<skottie-inline-sk src="https://skottie.skia.org/_/j/fc42db7c75741437b5cb0e90b3febc65" width=200 height=200></skottie-inline-sk>
|
||||
</a>
|
||||
<a href="https://skottie.skia.org/c16eee287f2cea44102b6670c66e60ab">
|
||||
<skottie-inline-sk src="https://skottie.skia.org/_/j/c16eee287f2cea44102b6670c66e60ab" width=200 height=200></skottie-inline-sk>
|
||||
</a>
|
||||
|
||||
*Sample animations courtesy of the lottiefiles.com community
|
||||
|
||||
<br>
|
||||
|
||||
Test server
|
||||
-----------
|
||||
|
||||
Test your Lottie files in our player at https://skottie.skia.org
|
||||
|
||||
<br>
|
||||
|
||||
The code
|
||||
--------
|
||||
Skia's animation code entry point can be found here on
|
||||
[Googlesource](https://skia.googlesource.com/skia/+/master/modules/skottie/include/Skottie.h)
|
||||
and [GitHub](https://github.com/google/skia/blob/master/modules/skottie/include/Skottie.h).
|
||||
The code is part of Skia's library but can also be made available as a separate
|
||||
package.
|
||||
|
||||
<br>
|
||||
|
||||
Embedding examples
|
||||
------------------
|
||||
Sample C code for using the Skottie native player can be found
|
||||
[here](https://github.com/google/skia/blob/master/modules/skottie/src/SkottieTool.cpp).
|
||||
|
||||
Android app code for inspiration can be found
|
||||
[here](https://github.com/google/skia/tree/master/platform_tools/android/apps/skottie).
|
||||
|
||||
Example code embedding Skottie into our Viewer app is
|
||||
[here](https://github.com/google/skia/blob/master/tools/viewer/SkottieSlide.cpp).
|
||||
|
||||
The Viewer or Skottie Android apps can be built following [these](https://skia.org/user/sample/viewer)
|
||||
instructions.
|
||||
|
11
site2/docs/user/privacy.md
Normal file
11
site2/docs/user/privacy.md
Normal file
@ -0,0 +1,11 @@
|
||||
|
||||
---
|
||||
title: "Privacy"
|
||||
linkTitle: "Privacy"
|
||||
|
||||
---
|
||||
|
||||
|
||||
When interacting with any of the web applications provided on
|
||||
skia.org we follow this [privacy policy](https://policies.google.com/privacy).
|
||||
|
16
site2/docs/user/release/_index.md
Normal file
16
site2/docs/user/release/_index.md
Normal file
@ -0,0 +1,16 @@
|
||||
---
|
||||
title: 'Release Information'
|
||||
linkTitle: 'Releases'
|
||||
|
||||
weight: 2
|
||||
menu:
|
||||
main:
|
||||
weight: 10
|
||||
---
|
||||
|
||||
We recommend most clients track our tip-of-tree, as we work hard to keep it
|
||||
green, and doing so gives us timely feedback and users the best chance of
|
||||
getting fixes.
|
||||
|
||||
If you require less frequent updates, we cut a stable milestone every 6 weeks
|
||||
along with Chrome.
|
766
site2/docs/user/release/release_notes.md
Normal file
766
site2/docs/user/release/release_notes.md
Normal file
@ -0,0 +1,766 @@
|
||||
|
||||
---
|
||||
title: "Milestone Release Notes"
|
||||
linkTitle: "Milestone Release Notes"
|
||||
|
||||
---
|
||||
|
||||
|
||||
This page includes a list of high level updates for each milestone release.
|
||||
|
||||
* * *
|
||||
|
||||
Milestone 90
|
||||
------------
|
||||
* Renamed use of sk_cf_obj in external Metal types to sk_cfp.
|
||||
https://review.skia.org/372556
|
||||
|
||||
* GrDirectContext::ComputeImageSize() is removed. Use SkImage::textureSize() instead.
|
||||
https://review.skia.org/368621
|
||||
https://review.skia.org/369317
|
||||
https://review.skia.org/371958
|
||||
|
||||
* Remove SkImageFilter::MakeMatrixFilter as it was unused and replaced with
|
||||
SkImageFilters::MatrixTransform.
|
||||
https://review.skia.org/366318
|
||||
|
||||
* Refactored particle system to use a single code string containing both Effect and Particle code.
|
||||
Uniform APIs are now shared for all program entry points, and no longer prefixed with 'Effect'
|
||||
or 'Particle'. For example, instead of `SkParticleEffect::effectUniformInfo` and
|
||||
`SkParticleEffect::particleUniformInfo`, there is just `SkParticleEffect::uniformInfo`.
|
||||
|
||||
* Remove SkImageFilter::CropRect from the public API as it's no longer usable. All factories
|
||||
work with 'SkRect', 'SkIRect', or nullable pointers to 'Sk[I]Rect'.
|
||||
https://review.skia.org/361496
|
||||
|
||||
* Remove deprecated SkImageFilter factory functions and supporting types. All default-provided
|
||||
SkImageFilters are now only constructed via 'include/effects/SkImageFilters.h'
|
||||
https://review.skia.org/357285
|
||||
|
||||
* Added SkRuntimeEffect::makeImage() to capture the output of an SkRuntimeEffect in an SkImage.
|
||||
https://review.skia.org/357284
|
||||
|
||||
* Updated SkRuntimeEffect::Make() to take an Options struct. It also now returns a Results struct
|
||||
instead of a tuple.
|
||||
https://review.skia.org/363785
|
||||
https://review.skia.org/367060
|
||||
|
||||
* Changed SkRuntimeEffect::Varying to have lower-case member names, with no 'f' prefix.
|
||||
https://review.skia.org/365656
|
||||
|
||||
* Changed SkRuntimeEffect::Uniform to have lower-case member names, with no 'f' prefix.
|
||||
https://review.skia.org/365696
|
||||
|
||||
* Deprecate (and ignore) SkAndroidCodec::ExifOrientation
|
||||
https://review.skia.org/344763
|
||||
|
||||
* Fix several minor issues in lighting image filters:
|
||||
- The spotlight falloff exponent is no longer clamped to [1, 128]. SVG 1.1 requires the specular
|
||||
lighting effect's exponent (shininess) to be clamped; not the spotlight's falloff. Any such
|
||||
parameter clamping is the client's responisibility, which makes Skia's lighting effect easily
|
||||
adaptable to SVG 1.1 (clamp exponent) or SVG 2 (no clamp).
|
||||
- Fix spotlight incorrectly scaling light within the cone angle.
|
||||
- Move saturation of RGBA to after multiplying lighting intensity with the lighting color, which
|
||||
improves rendering when diffuse and specular constants are greater than 1.
|
||||
https://review.skia.org/355496
|
||||
|
||||
* SkDeferredDisplayListRecorder::makePromiseTexture has moved to SkImage::MakePromiseTexture.
|
||||
New code should use the new entry point – migration CLs will be coming soon.
|
||||
https://review.skia.org/373716
|
||||
|
||||
* * *
|
||||
|
||||
Milestone 89
|
||||
------------
|
||||
|
||||
* Removed SkYUVAIndex and SkYUVASizeInfo. These were no longer used in any
|
||||
public APIs.
|
||||
https://review.skia.org/352497
|
||||
|
||||
* Numerous changes to SkRuntimeEffect, aligning the capabilities and restrictions with
|
||||
The OpenGL ES Shading Language 1.00 (aka, the shading language of OpenGL ES2 and WebGL 1.0).
|
||||
All built-in functions from sections 8.1 through 8.6 implemented & tested on all backends.
|
||||
Removed types and features that require newer versions of GLSL:
|
||||
https://review.skia.org/346657 [Non-square matrices]
|
||||
https://review.skia.org/347046 [uint, short, ushort, byte, ubyte]
|
||||
https://review.skia.org/349056 [while and do-while loops]
|
||||
https://review.skia.org/350030 [Bitwise operators and integer remainder]
|
||||
|
||||
* Add SkShadowUtils::GetLocalBounds. Generates bounding box for shadows
|
||||
relative to path.
|
||||
https://review.skia.org/351922
|
||||
|
||||
* Removed SkPerlinNoiseShader::MakeImprovedNoise.
|
||||
https://review.skia.org/352057
|
||||
|
||||
* Removed deprecated version of MakeFromYUVATextures. Use the version
|
||||
that takes GrYUVABackendTextures instead.
|
||||
https://review.skia.org/345174
|
||||
|
||||
* SkAnimatedImage: Always respect exif orientation
|
||||
Replace SkPixmapPriv::ShouldSwapWidthHeight with
|
||||
SkEncodedOriginSwapsWidthHeight.
|
||||
https://review.skia.org/344762
|
||||
|
||||
* Add kDirectionalLight_ShadowFlag support. If enabled, light position represents
|
||||
a vector pointing towards the light, and light radius is blur radius at elevation 1.
|
||||
https://review.skia.org/321792
|
||||
|
||||
* Support GL_LUMINANCE8_ALPHA8 textures. These can be used with GrBackendTexture APIs
|
||||
on GrDirectContext and as planes of YUVA images via GrYUVABackendTextures.
|
||||
https://review.skia.org/344761
|
||||
|
||||
* Removed previously deprecated SkImage::MakeFromYUVATexturesCopyToExternal.
|
||||
https://review.skia.org/342077
|
||||
|
||||
* Add versions of GrDirectContext::createBackendTexture and updateBackendTexture
|
||||
that take a GrSurfaceOrigin. The previous versions are deprecated.
|
||||
https://review.skia.org/341005
|
||||
|
||||
* Remove support for deprecated kDontClipToLayer_SaveLayerFlag in SkCanvas::SaveLayerRec
|
||||
https://review.skia.org/339988
|
||||
|
||||
* Expose more info in SkCodec::FrameInfo
|
||||
https://review.skia.org/339857
|
||||
|
||||
* Added dither control to the SkImageFilters::Shader factory.
|
||||
https://review.skia.org/338156
|
||||
|
||||
* Add MTLBinaryArchive parameter to GrMtlBackendContext. This allows
|
||||
Skia to cache PipelineStates in the given archive for faster
|
||||
shader compiles on future runs. The client must handle loading and
|
||||
saving of the archive.
|
||||
https://review.skia.org/333758
|
||||
|
||||
* Deprecated enum SkYUVAInfo::PlanarConfig has been removed.
|
||||
https://review.skia.org/334161
|
||||
|
||||
* Deprecated SkImage factories have been removed from
|
||||
SkDeferredDisplayListRecorder.
|
||||
|
||||
* The following YUV image factories have been removed:
|
||||
SkImage::MakeFromYUVTexturesCopyWithExternalBackend
|
||||
SkImage::MakeFromNV12TexturesCopyWithExternalBackend
|
||||
Replacement pattern outlined below.
|
||||
1) Make image using MakeFromYUVATextures
|
||||
2) Make a SkSurface around result texture using SkSurface::MakeFromBackendTexture
|
||||
3) surface->getCanvas()->drawImage(image, 0, 0);
|
||||
4) surface->flushAndSubmit()
|
||||
5) Optional: SkImage::MakeFromBackendTexture() to use as SkImage.
|
||||
https://review.skia.org/334596
|
||||
|
||||
* Added a new interface for GrDirectContext creation in Metal, using
|
||||
a new struct called GrMtlBackendContext. The previous interface taking
|
||||
a MTLDevice and MTLCommandQueue is deprecated.
|
||||
https://review.skia.org/334426
|
||||
|
||||
* SkCanvas::flush has been deprecated.
|
||||
|
||||
* * *
|
||||
|
||||
Milestone 88
|
||||
------------
|
||||
|
||||
* SkYUVAInfo now has separate enums for division of channels among planes and
|
||||
the subsampling. The previous combined enum, PlanarConfig, is deprecated.
|
||||
https://review.skia.org/334102
|
||||
|
||||
* Simplified SkDeferredDisplayListRecorder promise image API. Removed "release"
|
||||
callback and renamed "done" callback to "release". The new "release" proc can
|
||||
be null. Added a new SkYUVAInfo-based factory for YUVA promise texture images
|
||||
and deprecated the old SkYUVAIndex-based one.
|
||||
https://review.skia.org/331836
|
||||
https://review.skia.org/333519
|
||||
|
||||
* Limit the types and intrinsics supported in SkRuntimeEffect to GLSL ES 1.00
|
||||
https://review.skia.org/332597
|
||||
|
||||
* Add AVIF support to SkHeifCodec.
|
||||
|
||||
* Add support for creating SkSurfaceCharacterizations directly for use by a
|
||||
GrVkSecondaryCBDrawContext.
|
||||
https://review.skia.org/331877
|
||||
|
||||
* Removed SkSurfaceProps::kLegacyFontHost_InitType, SkFontLCDConfig, and related code.
|
||||
The default pixel geometry for SkSurfaceProps is now kUnknown instead of kRGB_H.
|
||||
The removal was guarded by the SK_LEGACY_SURFACE_PROPS build flag which was later removed.
|
||||
https://review.skia.org/322490
|
||||
https://review.skia.org/329364
|
||||
|
||||
* Legacy 8-bit YUV interface removed from SkImageGenerator. Use more flexible SkYUVAPixmaps-
|
||||
based interface instead.
|
||||
https://review.skia.org/327917
|
||||
|
||||
* New variant of SkImage::MakeFromYUVATextures. Takes a new type GrYUVATextures
|
||||
which wraps an SkYUVAInfo and compatible set of GrBackendTextures. The provides
|
||||
a more complete and structured specification of the planar configuration. Previous
|
||||
version is deprecated.
|
||||
Already deprecated MakeFromYUVATexturesCopyToExternal added to replace other deprecated
|
||||
APIs. It's not recommended that clients use this and instead use the pattern described
|
||||
in the API comment.
|
||||
https://review.skia.org/317762
|
||||
https://review.skia.org/329956
|
||||
|
||||
* Add field to GrContextOptions to disable mipmap support even if the backend
|
||||
supports it.
|
||||
|
||||
* SkTPin() removed from public API.
|
||||
|
||||
* * *
|
||||
|
||||
Milestone 87
|
||||
------------
|
||||
|
||||
* GrVkImageInfo now has a field for sample count. GrBackendRenderTarget constructor
|
||||
that took both a GrVkImageInfo and separate sample count is deprecated. Use the
|
||||
version without sample count instead. Similarly, GrD3DTextureResourceInfo now
|
||||
has a sample count field and GrBackendRenderTarget no longer takes a separate
|
||||
sample count for Direct3D. The sample count for GrBackendRenderTarget is now
|
||||
directly queried from MtlTexture rather than passed separately. The version that
|
||||
takes a separate sample count is deprecated and the parameter is ignored.
|
||||
https://review.skia.org/320262
|
||||
https://review.skia.org/320757
|
||||
https://review.skia.org/320956
|
||||
|
||||
* Added deprecation warning for Metal support on MacOS 10.13, iOS 8.3, and older.
|
||||
https://review.skia.org/320260
|
||||
|
||||
* GrVkImageInfo now has a field for sample count. GrBackendRenderTarget constructor
|
||||
that took both a GrVkImageInfo and separate sample count is deprecated. Use the
|
||||
version without sample count instead.
|
||||
|
||||
* Update SkClipOp::kMax_EnumValue to include only intersect and difference when
|
||||
SK_SUPPORT_DEPRECATED_CLIPOPS is not defined.
|
||||
https://review.skia.org/320064
|
||||
|
||||
* Add support for external allocator for Direct3D 12 backend.
|
||||
Defines base classes for an allocation associated with a backend texture and a
|
||||
a memory allocator to create such allocations.
|
||||
Adds memory allocator to backend context.
|
||||
https://review.skia.org/317243
|
||||
|
||||
* Add new optional parameter to GrContext::setBackend[Texture/RenderTarget]State which can
|
||||
be used to return the previous GrBackendSurfaceMutableState before the requested change.
|
||||
https://review.skia.org/318698
|
||||
|
||||
* New optimized clip stack for GPU backends. Enabled by default but old behavior based on
|
||||
SkClipStack can be restored by defining SK_DISABLE_NEW_GR_CLIP_STACK when building. It is not
|
||||
compatible with SK_SUPPORT_DEPRECATED_CLIPOPS and we are targeting the removal of support for
|
||||
the deprecated, expanding clip ops.
|
||||
https://review.skia.org/317209
|
||||
|
||||
* GPU backends now properly honor the SkFilterQuality when calling drawAtlas.
|
||||
https://review.skia.org/313081
|
||||
|
||||
* The signature of 'main' used with SkRuntimeEffect SkSL has changed. There is no longer an
|
||||
'inout half4 color' parameter, effects must return their color instead.
|
||||
Valid signatures are now 'half4 main()' or 'half4 main(float2 coord)'.
|
||||
https://review.skia.org/310756
|
||||
|
||||
* New YUVA planar specifications for SkCodec, SkImageGenerator, SkImage::MakeFromYUVAPixmaps.
|
||||
Chroma subsampling is specified in more structured way. SkCodec and SkImageGenerator
|
||||
don't assume 3 planes with 8bit planar values. Old APIs are deprecated.
|
||||
https://review.skia.org/309658
|
||||
https://review.skia.org/312886
|
||||
https://review.skia.org/314276
|
||||
https://review.skia.org/316837
|
||||
https://review.skia.org/317097
|
||||
|
||||
* Added VkImageUsageFlags to GrVkImageInfo struct.
|
||||
|
||||
* * *
|
||||
|
||||
Milestone 86
|
||||
------------
|
||||
|
||||
* Remove support for 'in' variables from SkRuntimeEffect. API now exclusively refers to inputs
|
||||
as 'uniforms'.
|
||||
https://review.skia.org/309050
|
||||
|
||||
* Add SkImageGeneratorNDK and SkEncodeImageWithNDK for using Android's NDK APIs to decode and
|
||||
encode.
|
||||
https://review.skia.org/308185
|
||||
https://review.skia.org/308800
|
||||
|
||||
* SkImage:remove DecodeToRaster, DecodeToTexture
|
||||
https://review.skia.org/306331
|
||||
|
||||
* Add GrContext api to update compressed backend textures.
|
||||
https://review.skia.org/302265
|
||||
|
||||
* Rename GrMipMapped to GrMipmapped for consistency with new APIs.
|
||||
Also rename GrBackendTexture::hasMipMaps() to GrBackendTexture::hasMipmaps()
|
||||
https://review.skia.org/304576
|
||||
https://review.skia.org/304598
|
||||
|
||||
* Add option for clients to own semaphores after wait calls.
|
||||
https://review.skia.org/301216
|
||||
|
||||
* Remove obsolete GrFlushFlags.
|
||||
https://review.skia.org/298818
|
||||
|
||||
* Adds default flush() calls to SkSurface, SkImage, and GrContext. These calls do
|
||||
a basic flush without a submit. If you haven't updated Skia in a couple releases
|
||||
and still have flush() calls in your code that you expect to do a flush and
|
||||
submit, you should update all those to the previously added flushAndSubmit() calls
|
||||
instead.
|
||||
https://review.skia.org/299141
|
||||
|
||||
* Enable BackendSemaphores for the Direct3D backend.
|
||||
https://review.skia.org/298752
|
||||
|
||||
* Added SkImage:asyncRescaleAndReadPixels and SkImage::asyncRescaleAndReadPixelsYUV420
|
||||
https://review.skia.org/299281
|
||||
|
||||
* Ganesh is moving towards replacing GrContext with the GrDirectContext/GrRecordingContext
|
||||
pair. GrDirectContexts have _direct_ access to the GPU and are very similar to the old
|
||||
GrContext. GrRecordingContexts are less powerful contexts that lack GPU access but provided
|
||||
context-like utilities during DDL recording. SkSurfaces and SkCanvas will now only return
|
||||
GrRecordingContexts. Clients requiring context features that need GPU access can then
|
||||
check (via GrRecordingContext::asDirectContext) if the available recording context is actually
|
||||
a direct context.
|
||||
|
||||
* Replace #defined values in SkString with equivalent constexprs.
|
||||
http://review.skia.org/306160
|
||||
|
||||
|
||||
* * *
|
||||
|
||||
Milestone 85
|
||||
------------
|
||||
|
||||
* Added GrContext::oomed() which reports whether Skia has seen a GL_OUT_OF_MEMORY
|
||||
error from Open GL [ES] or VK_ERROR_OUT_OF_*_MEMORY from Vulkan.
|
||||
https://review.skia.org/298216
|
||||
|
||||
* Add option on SkSurface::flush to pass in a GrBackendSurfaceMutableState which
|
||||
we will set the gpu backend surface to be at the end of the flush.
|
||||
https://review.skia.org/295567
|
||||
|
||||
* Add GrContext function to set mutable state on a backend surface. Currently this
|
||||
is only used for setting vulkan VkImage layout and queue family.
|
||||
https://review.skia.org/293844
|
||||
|
||||
* SkSurface factores that take GrBackendTexture or GrBackendRenderTarget now always
|
||||
call the release proc (if provided) on failure. SkSurface::replaceBackendTexture
|
||||
also calls the release proc on failure.
|
||||
https://review.skia.org/293762
|
||||
|
||||
* SkSurface::asyncRescaleAndReadPixels and SkSurfaceasyncRescaleAndReadPixelsYUV420
|
||||
now require explicit GrContext submit to guarantee finite time before callback
|
||||
is invoked.
|
||||
https://review.skia.org/292840
|
||||
|
||||
* Add VkSharingMode field to GrVkImageInfo.
|
||||
https://review.skia.org/293559
|
||||
|
||||
* Move SkBitmapRegionDecoder into client_utils/android.
|
||||
|
||||
* SkCanvas.clear and SkCanvas.drawColor now accept SkColor4f in addition to SkColor.
|
||||
|
||||
* Remove SkSurface::MakeFromBackendTextureAsRenderTarget.
|
||||
This factory existed to work around issues with GL_TEXTURE_RECTANGLE that existed
|
||||
in Chrome's command buffer. Those issues have since been resolved. Use
|
||||
SkSurface::MakeFromBackendTexutre or SkSurface::MakeFromBackendRenderTarget instead.
|
||||
https://review.skia.org/292719
|
||||
|
||||
* Adds submittedProc callback to GrFlushInfo which will be called when the work
|
||||
from the flush call is submitted to the GPU. This is specifically useful for knowing
|
||||
when semahpores sent with the flush have been submitted and can be waiting on.
|
||||
https://review.skia.org/291078
|
||||
|
||||
* GrContext submit is now required to be called in order to send GPU work to the
|
||||
actual GPU. The flush calls simply produces 3D API specific objects that are ready
|
||||
to be submitted (e.g. command buffers). For the GL backend, the flush will still
|
||||
send commands to the driver. However, clients should still assume the must call
|
||||
submit which is where any glFlush that is need for sync objects will be called. There,
|
||||
are flushAndSubmit() functions of GrContext, SkSurface, and SkImage that will act
|
||||
like the previous flush() functions. This will flush the work and immediately call
|
||||
submit.
|
||||
https://review.skia.org/289033
|
||||
|
||||
* Remove deprecated version of flush calls on GrContext and SkSurface.
|
||||
https://review.skia.org/2290540
|
||||
|
||||
* SkCanvas::drawVertices and drawPatch now support mapping an SkShader without explicit
|
||||
texture coordinates. If they're not supplied, the local positions (vertex position or
|
||||
patch cubic positions) will be directly used to sample the SkShader.
|
||||
https://review.skia.org/290130
|
||||
|
||||
* * *
|
||||
|
||||
Milestone 84
|
||||
------------
|
||||
|
||||
* Add api on GrContext, updateBackendTexture that will upload new data to a
|
||||
GrBackendTexture.
|
||||
https://review.skia.org/288909
|
||||
|
||||
* Add GrContext getter to SkSurface.
|
||||
https://review.skia.org/289479
|
||||
|
||||
* Deprecate GrContext and SkSurface flush() call and replace ith with flushAndSubmit().
|
||||
This only effects the default flush call that takes no parameters.
|
||||
https://review.skia.org/289478
|
||||
|
||||
* GrContext::createBackendTexture functions that initialize the texture no longer
|
||||
guarantee that all the data has been uploaded and the gpu is done with the texture.
|
||||
Instead the client can assume the upload work has been submitted to the gpu and they
|
||||
must wait for that work to finish before deleting the texture. This can be done via
|
||||
their own synchronization or by passing in a finish proc into the create calls which
|
||||
will be called when it is safe to delete the texture (at least in terms of work
|
||||
done during the create).
|
||||
https://review.skia.org/286517
|
||||
|
||||
* Remove unused SkMaskFilter helpers: compbine, compose
|
||||
Note: shadermaskfilter will likely be removed next (clipShader should serve)
|
||||
|
||||
* Add back SkCanvas::kPreserveLCDText_SaveLayerFlag to indicate that saveLayer()
|
||||
will preserve LCD-text. All text in the layer must be drawn on opaque background
|
||||
to ensure correct rendering.
|
||||
|
||||
* Add the new directory client_utils/ for code that is specific to a single client and
|
||||
should be considered separate from Skia proper. Move SkFrontBufferedStream into the
|
||||
subdir android/.
|
||||
|
||||
* SkBitmap and SkPixmap's erase() methods now treat their color parameters
|
||||
consistently with the rest of Skia, with all SkColors and any untagged
|
||||
SkColor4fs interpreted as sRGB, not as a color in the bitmap's color space.
|
||||
SkPixmap::erase(SkColor4f) now takes an SkColorSpace, so you can pass
|
||||
pixmap.colorSpace() if you want the old behavior.
|
||||
|
||||
* SkCamera.h and SkMatrix44.h are DEPRECATED.
|
||||
Use SkM44 if you want to have 3d transformations.
|
||||
|
||||
* Changed Dilate and Erode image filters to take SkScalar for radius instead of int. While
|
||||
the image filters themselves are defined in terms of discrete pixels, the radii provided by
|
||||
the user are mapped through the CTM so taking ints forced over discretization. After mapping
|
||||
through the CTM the radii are now rounded to pixels.
|
||||
https://review.skia.org/281731
|
||||
https://review.skia.org/282636
|
||||
|
||||
* Updated the contract of GrContext and SkSurface flush calls in regards to semaphores. Made it
|
||||
clear that the caller is responsible for deleting any initialized semaphores after the flush
|
||||
call regardless if we were able to submit them or not. Also, allows skia to only submit a
|
||||
subset of the requested semaphores if we failed to create some.
|
||||
https://review.skia.org/282265
|
||||
|
||||
|
||||
* SkCanvas::drawVertices will now always fill the triangles specified by the vertices. Previously,
|
||||
vertices with no colors and no (texture coordinates or shader) would be drawn in wireframe.
|
||||
https://review.skia.org/282043
|
||||
|
||||
* * *
|
||||
|
||||
Milestone 83
|
||||
------------
|
||||
|
||||
* Remove localmatrix option from SkShaders::[Blend, Lerp]
|
||||
|
||||
* Fill out Direct3D parameters for backend textures and backend rendertargets.
|
||||
|
||||
* SkImage::makeTextureImage() takes an optional SkBudgeted param
|
||||
|
||||
* Made non-GL builds of GPU backend more robust.
|
||||
https://review.skia.org/277456
|
||||
|
||||
* MoltenVK support removed. Use Metal backend instead.
|
||||
https://review.skia.org/277612
|
||||
|
||||
* * *
|
||||
|
||||
Milestone 82
|
||||
------------
|
||||
|
||||
* Removed drawBitmap and related functions from SkDevice; all public drawBitmap functions on
|
||||
SkCanvas automatically wrap the bitmap in an SkImage and call the equivalent drawImage function.
|
||||
Drawing mutable SkBitmaps will now incur a mandatory copy. Switch to using SkImage directly or
|
||||
mark the bitmap as immutable before drawing.
|
||||
|
||||
* Removed "volatile" flag from SkVertices. All SkVertices objects are assumed to be
|
||||
volatile (the previous default behavior).
|
||||
|
||||
* Removed exotic legacy bitmap functions from SkCanvas (drawBitmapLattic, drawBitmapNine); the
|
||||
exotic SkImage functions still exist.
|
||||
|
||||
* Make it possible to selectively turn on/off individual encoders/decoders,
|
||||
using skia_use_(libpng/libjpeg_turbo/libwebp)(decode/encode).
|
||||
|
||||
* Removed GrGpuResource, GrSurface, and GrTexture from public api. These were not
|
||||
meant to be public, and we now can move them into src. Also removed getTexture
|
||||
function from SkImage.h
|
||||
|
||||
* Removed Bones from SkVertices
|
||||
|
||||
* Added a field to GrContextOptions that controls whether GL errors are checked after
|
||||
GL calls that allocate textures, etc. It also controls checking for shader compile
|
||||
success, and program linking success.
|
||||
|
||||
* Made SkDeferredDisplayList.h officially part of the public API (i.e., moved it to
|
||||
include/core). Also added a ProgramIterator to SkDeferredDisplayList which allows
|
||||
clients to pre-compile some of the shaders the DDL requires.
|
||||
|
||||
* Added two new helper methods to SkSurfaceCharacterization: createBackendFormat and
|
||||
createFBO0. These make it easier for clients to create new surface characterizations that
|
||||
differ only a little from an existing surface characterization.
|
||||
|
||||
* Removed SkTMax and SkTMin.
|
||||
* Removed SkTClamp and SkClampMax.
|
||||
* Removed SkScalarClampMax and SkScalarPin.
|
||||
* Removed SkMax32 and SkMin32.
|
||||
* Removed SkMaxScalar and SkMinScalar.
|
||||
|
||||
* SkColorSetA now warns if the result is unused.
|
||||
|
||||
* An SkImageInfo with a null SkColorSpace passed to SkCodec::getPixels() and
|
||||
related calls is treated as a request to do no color correction at decode
|
||||
time.
|
||||
|
||||
* Add new APIs to add attributes to document structure node when
|
||||
creating a tagged PDF.
|
||||
|
||||
* Remove CGFontRef parameter from SkCreateTypefaceFromCTFont.
|
||||
Use CTFontManagerCreateFontDescriptorFromData instead of
|
||||
CGFontCreateWithDataProvider to create CTFonts to avoid memory use issues.
|
||||
|
||||
* Added SkCodec:: and SkAndroidCodec::getICCProfile for reporting the native
|
||||
ICC profile of an encoded image, even if it doesn't map to an SkColorSpace.
|
||||
|
||||
* SkSurface::ReplaceBackendTexture takes ContentChangeMode as a parameter,
|
||||
which allow callers to specify whether retain a copy of the current content.
|
||||
|
||||
* Enforce the existing documentation in SkCanvas::saveLayer that it ignores
|
||||
any mask filter on the restore SkPaint. The 'coverage' of a layer is
|
||||
ill-defined, and masking should be handled by pre-clipping or using the
|
||||
auxiliary clip mask image of the SaveLayerRec.
|
||||
|
||||
Milestone 81
|
||||
------------
|
||||
|
||||
* Added support for GL_NV_fence extension.
|
||||
|
||||
* Make SkImageInfo::validRowBytes require rowBytes to be pixel aligned. This
|
||||
makes SkBitmap match the behavior of raster SkSurfaces in rejecting
|
||||
non-aligned rowBytes.
|
||||
|
||||
* Added an SkImage::MakeRasterFromCompressed entry point. Also updated
|
||||
SkImage::MakeFromCompressed to decompress the compressed image data if
|
||||
the GPU doesn't support the specified compression type (i.e., macOS Metal
|
||||
doesn't support BC1_RGB8_UNORM so such compressed images will always be
|
||||
decompressed on that platform).
|
||||
|
||||
* Added support for BC1 RGBA compressed textures
|
||||
|
||||
* Added CachingHint to SkImage::makeRasterImage
|
||||
|
||||
* Added SkAnimatedImage::getCurrentFrame()
|
||||
|
||||
* Add support to create an SkSurface from an MTKView, with delayed acquisition of
|
||||
the MTLDrawable.
|
||||
Entry point: SkSurface::MakeFromMTKView
|
||||
|
||||
* Removed SkIRect::EmptyIRect(). Use SkIRect::MakeEmpty() instead.
|
||||
https://review.skia.org/262382/
|
||||
|
||||
* Moved SkRuntimeEffect to public API. This is the new (experimental) interface to custom SkSL
|
||||
shaders and color filters.
|
||||
|
||||
* Added BC1 compressed format support. Metal and Vulkan seem to only support the BC
|
||||
formats on desktop machines.
|
||||
|
||||
* Added compressed format support for backend texture creation API.
|
||||
This adds the following new entry points:
|
||||
GrContext::compressedBackendFormat
|
||||
GrContext::createCompressedBackendTexture
|
||||
The latter method comes in variants that allow color-initialized and
|
||||
compressed texture data initialized.
|
||||
|
||||
* Added SkMatrix::MakeTrans(SkIVector)
|
||||
https://review.skia.org/259804
|
||||
|
||||
|
||||
* * *
|
||||
|
||||
Milestone 80
|
||||
------------
|
||||
|
||||
* Removed SkSize& SkSize::operator=(const SkISize&)
|
||||
https://review.skia.org/257880
|
||||
|
||||
* SkISize width() and height() now constexpr
|
||||
https://review.skia.org/257680
|
||||
|
||||
* Added SkMatrix::MakeTrans(SkVector) and SkRect::makeOffset(SkVector).
|
||||
https://review.skia.org/255782
|
||||
|
||||
* Added SkImageInfo::MakeA8(SkISize) and added optional color space parameter to
|
||||
SkImageInfo::MakeN32Premul(SkISize).
|
||||
|
||||
* Added dimensions() and getFrameCount() to SkAnimatedImage
|
||||
https://review.skia.org/253542
|
||||
|
||||
* Removed SkMatrix44 version of toXYZD50 from SkColorSpace. Switched to skcms types in
|
||||
transferFn, invTrasnferFn, and gamutTransformTo functions.
|
||||
https://review.skia.org/252596
|
||||
|
||||
* Removed rotation and YUV support from SkColorMatrix
|
||||
https://review.skia.org/252188
|
||||
|
||||
* Added kBT2020_SkYUVColorSpace. This is BT.2020's YCbCr conversion (non-constant-luminance).
|
||||
https://review.skia.org/252160
|
||||
|
||||
* Remove old async read pixels APIs
|
||||
https://review.skia.org/251198
|
||||
|
||||
* Expose SkBlendModeCoeff and SkBlendMode_AsCoeff for Porter-Duff blend modes.
|
||||
https://review.skia.org/252600
|
||||
|
||||
* * *
|
||||
|
||||
Milestone 79
|
||||
------------
|
||||
|
||||
* SkTextBlob::Iter to discover the glyph indices and typefaces in each run
|
||||
https://review.skia.org/246296
|
||||
|
||||
* Added support for PQ and HLG transfer functions to SkColorSpace.
|
||||
https://review.skia.org/249000
|
||||
|
||||
* Added new api on GrContext ComputeImageSize. This replaces the hold static helper
|
||||
ComputeTextureSize.
|
||||
https://review.skia.org/247337
|
||||
|
||||
* New versions of SkSurface async-rescale-and read APIs that allow client to extend
|
||||
the lifetime of the result data. Old versions are deprecated.
|
||||
https://review.skia.org/245457
|
||||
|
||||
* Add SkColorInfo. It's dimensionless SkImageInfo.
|
||||
https://review.skia.org/245261
|
||||
|
||||
* Added SkPixmap-based createBackendTexture method to GrContext. This allows clients to create
|
||||
backend resources (initialized with texture data) that Skia/Ganesh doesn't know about/track.
|
||||
https://review.skia.org/244676
|
||||
|
||||
* Add explicit src and dst colorspace parameters to SkColorFilter::filterColor4f()
|
||||
https://review.skia.org/244882
|
||||
|
||||
* Remove Vulkan/Metal float32 RGBA texture support
|
||||
https://review.skia.org/244881
|
||||
|
||||
* Add SkSurface::MakeFromCAMetalLayer
|
||||
https://review.skia.org/242563
|
||||
|
||||
* Added kAlpha_F16_SkColorType, kRG_F16_SkColorType and kRGBA_16161616_SkColorType.
|
||||
This is intended to help support HDR YUV uses case (e.g., P010 and P016). As such,
|
||||
the addition is focused on allowing creation of SkPixmaps and SkImages and not
|
||||
SkSurfaces (i.e., who wants to render to render to these?)
|
||||
https://review.skia.org/241357
|
||||
|
||||
* Start to move nested SkPath types (e.g. Direction, Verb) up to root level in SkPathTypes.h
|
||||
https://review.skia.org/241079
|
||||
|
||||
* Remove isRectContour and ksNestedFillRects from public
|
||||
https://review.skia.org/241078
|
||||
|
||||
* Added kRG_88_SkColorType. This is intended to help support YUV uses case (e.g., NV12).
|
||||
As such, the addition is focused on allowing creation of SkPixmaps and SkImages and not
|
||||
SkSurfaces (i.e., who wants to render to RG?)
|
||||
https://review.skia.org/239930
|
||||
https://review.skia.org/235797
|
||||
|
||||
* Make the size of program/pipeline caches configurable via
|
||||
GrContextOptions::fRuntimeProgramCacheSize
|
||||
https://review.skia.org/239756
|
||||
|
||||
* Added kAlpha_16_SkColorType and kRG_1616_SkColorType. This is intended to help support HDR YUV
|
||||
uses case (e.g., P010 and P016). As such, the addition is focused on allowing creation of
|
||||
SkPixmaps and SkImages and not SkSurfaces (i.e., who wants to render to render to these?)
|
||||
https://review.skia.org/239930
|
||||
|
||||
* Add GrContext::precompileShader to allow up-front compilation of previously-cached shaders.
|
||||
https://review.skia.org/239438
|
||||
|
||||
* * *
|
||||
|
||||
Milestone 78
|
||||
------------
|
||||
* Added RELEASE_NOTES.txt file
|
||||
https://review.skia.org/229760
|
||||
|
||||
* SkDrawLooper is no longer supported in SkPaint or SkCanvas.
|
||||
https://review.skia.org/230579
|
||||
https://review.skia.org/231736
|
||||
|
||||
* SkPath::Iter::next() now ignores its consumDegenerates bools. Those will so
|
||||
go away entirely
|
||||
https://review.skia.org/235104
|
||||
|
||||
* SkImage: new factories: DecodeToRaster, DecodeToTexture
|
||||
https://review.skia.org/234476
|
||||
|
||||
* SkImageFilter API refactor started:
|
||||
- Provide new factory API in include/effects/SkImageFilters
|
||||
- Consolidated enum types to use SkTileMode and SkColorChannel
|
||||
- Hide filter implementation classes
|
||||
- Hide previously public functions on SkImageFilter that were intended for
|
||||
internal use only
|
||||
https://review.skia.org/230198
|
||||
https://review.skia.org/230876
|
||||
https://review.skia.org/231256
|
||||
|
||||
* SkColorFilters::HSLAMatrix - new matrix color filter operating in HSLA
|
||||
space.
|
||||
https://review.skia.org/231736
|
||||
|
||||
* Modify GrBackendFormat getters to not return internal pointers. Use an enum
|
||||
class for GL formats.
|
||||
https://review.skia.org/233160
|
||||
|
||||
* Expose GrContext::dump() when SK_ENABLE_DUMP_GPU is defined.
|
||||
https://review.skia.org/233557
|
||||
|
||||
* Vulkan backend now supports YCbCr sampler for I420 Vulkan images that are
|
||||
not backed by external images.
|
||||
https://review.skia.org/233776
|
||||
|
||||
* Add SkCodec::SelectionPolicy for distinguishing between decoding a still
|
||||
image or an image sequence for a container format that has both (e.g. HEIF).
|
||||
https://review.skia.org/232839
|
||||
|
||||
* SkImage::makeTextureImage and SkImage::MakeCrossContextFromPixmap no longer
|
||||
take an SkColorSpace parameter. It was unused.
|
||||
https://review.skia.org/234579
|
||||
https://review.skia.org/234912
|
||||
|
||||
* SkImage::reinterpretColorSpace - to reinterpret image contents in a new
|
||||
color space.
|
||||
https://review.skia.org/234328
|
||||
|
||||
* Removed SkImage::MakeCrossContextFromEncoded.
|
||||
https://review.skia.org/234912
|
||||
|
||||
* Add Metal support for GrFence, GrSemaphore, and GrBackendSemaphore
|
||||
https://review.skia.org/233416
|
||||
|
||||
* SkMallocPixelRef: remove MakeDirect and MakeWithProc from API.
|
||||
https://review.skia.org/234660
|
||||
|
||||
* Remove 4-parameter variant of SkRect::join() and intersect(), and
|
||||
noemptycheck variants of intersect().
|
||||
https://review.skia.org/235832
|
||||
https://review.skia.org/237142
|
||||
|
||||
* Remove unused sk_sp comparison operators.
|
||||
https://review.skia.org/236942
|
||||
|
||||
* Add SkColor4f variant to experimental_DrawEdgeAAQuad for SkiaRenderer.
|
||||
https://review.skia.org/237492
|
||||
|
||||
* Deprecated maxCount resource cache limit for Ganesh.
|
||||
This hasn't been relevant for a long time.
|
||||
|
||||
* Changed GrContextOptions' fDisallowGLSLBinaryCaching to fShaderCacheStrategy,
|
||||
and allow caching SkSL.
|
||||
https://review.skia.org/238856
|
||||
|
||||
|
36
site2/docs/user/release/schedule.md
Normal file
36
site2/docs/user/release/schedule.md
Normal file
@ -0,0 +1,36 @@
|
||||
|
||||
---
|
||||
title: "Milestone Schedule"
|
||||
linkTitle: "Milestone Schedule"
|
||||
|
||||
---
|
||||
|
||||
|
||||
On a six week cadence aligned with Chromium, Skia cuts milestone branches.
|
||||
Clients wishing to stay on a relatively stable level of Skia often utilize these
|
||||
branches.
|
||||
|
||||
On the branch date, a healthy level of Skia near HEAD is chosen. After this
|
||||
point, for the next six weeks, only high priority fixes are checked into the branch.
|
||||
After the six week period, when another branch is cut, only critical (typically
|
||||
security) fixes will be committed to any previous branch.
|
||||
|
||||
Skia 2020 schedule:
|
||||
|
||||
Milestone | Branch Date (beginning of day)
|
||||
----------|-------------------------------
|
||||
81 | 01/30/20
|
||||
82 | 03/12/20
|
||||
83 | 04/02/20*
|
||||
84 | 05/14/20
|
||||
85 | 06/25/20
|
||||
86 | 08/20/20
|
||||
87 | 10/01/20
|
||||
88 | 11/12/20
|
||||
|
||||
Note that 82 was abandoned by Chromium and 83 schedule moved in due to COVID-19 impact.
|
||||
Skia is maintaining 82 for our customers. Future dates are subject to change.
|
||||
|
||||
The current milestone is included in the headers in
|
||||
[SkMilestone.h](https://skia.googlesource.com/skia/+/master/include/core/SkMilestone.h).
|
||||
|
10
site2/docs/user/sample/_index.md
Normal file
10
site2/docs/user/sample/_index.md
Normal file
@ -0,0 +1,10 @@
|
||||
|
||||
---
|
||||
title: "Samples and Tutorials"
|
||||
linkTitle: "Samples and Tutorials"
|
||||
|
||||
weight: 1
|
||||
|
||||
---
|
||||
|
||||
|
70
site2/docs/user/sample/pdf.md
Normal file
70
site2/docs/user/sample/pdf.md
Normal file
@ -0,0 +1,70 @@
|
||||
|
||||
---
|
||||
title: "Using Skia's PDF Backend"
|
||||
linkTitle: "Using Skia's PDF Backend"
|
||||
|
||||
---
|
||||
|
||||
|
||||
Here is an example of using Skia's PDF backend (SkPDF) via the
|
||||
SkDocument and SkCanvas APIs.
|
||||
|
||||
<fiddle-embed name='@PDF'></fiddle-embed>
|
||||
<!-- https://fiddle.skia.org/c/@PDF docs/examples/PDF.cpp -->
|
||||
|
||||
* * *
|
||||
|
||||
<span id="limits">SkPDF Limitations</span>
|
||||
------------------------------------------
|
||||
|
||||
There are several corners of Skia's public API that SkPDF currently
|
||||
does not handle because either no known client uses the feature or
|
||||
there is no simple PDF-ish way to handle it.
|
||||
|
||||
In this document:
|
||||
|
||||
+ **drop** means to draw nothing.
|
||||
|
||||
+ **ignore** means to draw without the effect
|
||||
|
||||
+ **expand** means to implement something in a non-PDF-ish way.
|
||||
This may mean to rasterize vector graphics, to expand paths with
|
||||
path effects into many individual paths, or to convert text to
|
||||
paths.
|
||||
|
||||
<style scoped><!--
|
||||
#pdftable {border-collapse:collapse;}
|
||||
#pdftable tr th, #pdftable tr td {border:#888888 2px solid;padding: 5px;}
|
||||
--></style>
|
||||
<table id="pdftable">
|
||||
<tr><th>Effect</th> <th>text</th> <th>images</th> <th>everything
|
||||
else</th></tr>
|
||||
<tr><th>SkMaskFilter</th> <td>drop</td> <td>ignore</td> <td>ignore</td></tr>
|
||||
<tr><th>SkPathEffect</th> <td>ignore</td> <td>n/a</td> <td>expand</td></tr>
|
||||
<tr><th>SkColorFilter</th> <td>ignore</td> <td>expand</td> <td>ignore</td></tr>
|
||||
<tr><th>SkImageFilter</th> <td>expand</td> <td>expand</td> <td>expand</td></tr>
|
||||
<tr><th>unsupported SkXferModes</th> <td>ignore</td> <td>ignore</td> <td>ignore</td></tr>
|
||||
<tr><th>non-gradient SkShader</th> <td>expand</td> <td>n/a</td> <td>expand</td></tr>
|
||||
</table>
|
||||
|
||||
Notes:
|
||||
|
||||
- *SkImageFilter*: When SkImageFilter is expanded, text-as-text is lost.
|
||||
|
||||
- *SkXferMode*: The following transfer modes are not natively
|
||||
supported by PDF: DstOver, SrcIn, DstIn, SrcOut, DstOut, SrcATop,
|
||||
DstATop, and Modulate.
|
||||
|
||||
Other limitations:
|
||||
|
||||
- *drawText with VerticalText* — drop. No known clients seem to make use
|
||||
of the VerticalText flag.
|
||||
|
||||
- *drawTextOnPath* — expand. (Text-as-text is lost.)
|
||||
|
||||
- *drawVertices* — drop.
|
||||
|
||||
- *drawPatch* — drop.
|
||||
|
||||
* * *
|
||||
|
104
site2/docs/user/sample/viewer.md
Normal file
104
site2/docs/user/sample/viewer.md
Normal file
@ -0,0 +1,104 @@
|
||||
|
||||
---
|
||||
title: "Skia Viewer"
|
||||
linkTitle: "Skia Viewer"
|
||||
|
||||
---
|
||||
|
||||
The Skia Viewer displays a series of slides that exhibit specific features of Skia, including the Skia GMs and programmed samples that allow interaction. In addition, the Viewer is used to debug and understand different parts of the Skia system:
|
||||
|
||||
* Observe rendering performance - placing the Viewer in stats mode displays average frame times.
|
||||
* Try different rendering methods - it's possible to cycle among the three rendering methods: raster, OpenGL and Vulkan (on supported platforms). You can use this with stats mode to see the effect that the different rendering methods have on drawing performance.
|
||||
* Display and manipulate your own pictures.
|
||||
|
||||
Some slides require resources stored outside the program. These resources are stored in the `<skia-path>/resources` directory.
|
||||
|
||||
Linux, Macintosh and Windows
|
||||
----------------------------
|
||||
|
||||
The Viewer can be built using the regular GN build process, e.g.
|
||||
|
||||
bin/gn gen out/Release --args='is_debug=false'
|
||||
ninja -C out/Release viewer
|
||||
|
||||
To load resources in the desktop Viewers, use the `--resourcePath` option:
|
||||
|
||||
<skia-path>/out/Release/viewer --resourcePath <skia-path>/resources
|
||||
|
||||
Similarly, `--skps <skp-file-path>` will load any `.skp` files in that directory
|
||||
for display within the Viewer.
|
||||
|
||||
Other useful command-line options: using `--match <pattern>` will load only SKPs or slides
|
||||
matching that name; using `--slide <name>` will launch at that slide; and you can start up
|
||||
with a particular rendering method by using `--backend`, i.e., `--backend sw`, `--backend gl`,
|
||||
`--backend vk`, or `--backend mtl`.
|
||||
|
||||
The desktop Viewers are controlled using the keyboard and mouse: left (←) and right
|
||||
(→) arrows to move from slide to slide; up (↑) and down (↓) arrows to
|
||||
zoom in and out; clicking and dragging will translate. Other display options and a slide
|
||||
picker can be found in the Tools UI, which can be toggled by hitting the spacebar.
|
||||
|
||||
Key | Action
|
||||
-------|-------------
|
||||
← → | Move between the slides
|
||||
↑ ↓ | Zoom in / out
|
||||
d | Change render methods among raster, OpenGL and Vulkan
|
||||
s | Display rendering times and graph
|
||||
Space | Toggle display of Tools UI
|
||||
|
||||
Android
|
||||
-------
|
||||
|
||||
To build Viewer as an Android App, first follow the
|
||||
[Android build instructions](https://skia.org/user/build#android) to set up the
|
||||
Android NDK and a ninja out directory. In addition, you will need the
|
||||
[Android SDK](https://developer.android.com/studio/#command-tools) installed and your
|
||||
`ANDROID_HOME` environment variable set.
|
||||
|
||||
mkdir ~/android-sdk
|
||||
( cd ~/android-sdk; unzip ~/Downloads/sdk-tools-*.zip )
|
||||
yes | ~/android-sdk/tools/bin/sdkmanager --licenses
|
||||
export ANDROID_HOME=~/android-sdk # Or wherever you installed the Android SDK.
|
||||
|
||||
If you are not using the NDK included with the Android SDK (at ~/android-sdk/ndk-bundle
|
||||
in this example) you'll need to set the environmental variable `ANDROID_NDK_HOME`, e.g.,
|
||||
|
||||
export ANDROID_NDK_HOME=/tmp/ndk
|
||||
|
||||
The Viewer APK must be built by gradle which can be invoked on the command line
|
||||
with the following script:
|
||||
|
||||
platform_tools/android/bin/android_build_app -C <out_dir> viewer
|
||||
|
||||
where `<out_dir>` is the ninja out directory (e.g., `out/arm64`)
|
||||
that you created. Upon completion of the script the APK
|
||||
can be found at `<out_dir>/viewer.apk`
|
||||
|
||||
To load resources in the Android Viewer place them in
|
||||
`/data/local/tmp/resources`; to load SKPs place them in `/data/local/tmp/skps`.
|
||||
|
||||
Swiping left and right will switch slides, pinch-zoom will zoom in and out, and
|
||||
display options are available in the UI.
|
||||
|
||||
iOS
|
||||
---
|
||||
|
||||
Viewer on iOS is built using the regular GN process, e.g.
|
||||
|
||||
bin/gn gen out/Release --args='target_os="ios" is_debug=false'
|
||||
ninja -C out/Release viewer
|
||||
|
||||
Like other iOS apps it can be deployed either by using something like
|
||||
[ios-deploy](https://github.com/ios-control/ios-deploy)
|
||||
or by building within Xcode and launching via the IDE. See the
|
||||
[iOS build instructions](https://skia.org/user/build#ios) for more information
|
||||
on managing provisioning profiles for signing and deployment.
|
||||
|
||||
Viewer will
|
||||
automatically bundle the `resources` directory in the top-level Skia directory,
|
||||
and will bundle an `skps` directory if also placed in the Skia directory.
|
||||
|
||||
On iOS the Viewer provides basic touch functionality: you can view slides,
|
||||
swipe between them, pinch-zoom to scale, and translate via panning. There is not
|
||||
yet support for display options or selecting from a list of slides.
|
||||
|
81
site2/docs/user/sksl.md
Normal file
81
site2/docs/user/sksl.md
Normal file
@ -0,0 +1,81 @@
|
||||
---
|
||||
title: 'SkSL & Runtime Effects'
|
||||
linkTitle: 'SkSL'
|
||||
---
|
||||
|
||||
# SkSL & Runtime Effects
|
||||
|
||||
- [Overview](#overview)
|
||||
- [Sampling Textures, etc...](#children)
|
||||
- [Premultiplied Alpha](#premul)
|
||||
- [Coordinate Spaces](#coords)
|
||||
|
||||
---
|
||||
|
||||
## <span id="overview">Overview</span>
|
||||
|
||||
**SkSL** is Skia's [shading language](https://en.wikipedia.org/wiki/Shading_language).
|
||||
**`SkRuntimeEffect`** is a Skia C++ object that can be used to create `SkShader` and
|
||||
`SkColorFilter` objects with behavior controlled by SkSL code.
|
||||
|
||||
You can experiment with SkSL at https://shaders.skia.org/. The syntax is very similar to GLSL.
|
||||
When using SkSL effects in your Skia application, there are important differences (from GLSL)
|
||||
to remember. Most of these differences are because of one basic fact: **With GPU shading languages,
|
||||
you are programming a stage of the [GPU pipeline](https://www.khronos.org/opengl/wiki/Rendering_Pipeline_Overview). With SkSL, you are programming a stage of
|
||||
the Skia pipeline.**
|
||||
|
||||
---
|
||||
|
||||
## <span id="children">Sampling other SkShaders</span>
|
||||
|
||||
In GLSL, a fragment shader can sample a texture. With runtime effects, the object that you bind
|
||||
(in C++) and sample (in SkSL) is an `SkShader`. Skia has simple methods for creating an `SkShader`
|
||||
from an `SkImage`, so it's easy to use images in your runtime effects:
|
||||
|
||||
<fiddle-embed name='194aa388494b7cdfa57a01968b5cf1ee'></fiddle-embed>
|
||||
|
||||
Because the object you bind and sample is an `SkShader`, you can directly use any Skia shader,
|
||||
without necessarily turning it into an image (texture) first. For example, you can sample a linear gradient:
|
||||
|
||||
<fiddle-embed name='381b785f1ca50a0335be1bfe74c2f421'></fiddle-embed>
|
||||
|
||||
You can even sample another runtime effect:
|
||||
|
||||
<fiddle-embed name='13b446d926326481b340842f05014a9c'></fiddle-embed>
|
||||
|
||||
---
|
||||
|
||||
## <span id="premul">Premultiplied Alpha</span>
|
||||
|
||||
When dealing with transparent colors, there are two (common)
|
||||
[possible representations](https://en.wikipedia.org/wiki/Alpha_compositing#Straight_versus_premultiplied). Skia calls these _unpremultiplied_ (what
|
||||
Wikipedia calls _straight_), and _premultiplied_. In the Skia pipeline, every `SkShader` returns
|
||||
premultiplied colors.
|
||||
|
||||
If you're familiar with OpenGL blending, you can think of it in terms of the blend equation.
|
||||
For common alpha blending (called [source-over](https://developer.android.com/reference/android/graphics/PorterDuff.Mode#SRC_OVER)), you would normally configure your blend function as
|
||||
`(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)`. Skia defines source-over blending as if the blend function
|
||||
were `(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)`.
|
||||
|
||||
Skia's use of premultiplied alpha implies:
|
||||
|
||||
- If you start with an unpremultiplied `SkImage` (like a PNG), turn that into an `SkImageShader`,
|
||||
and sample that shader... the resulting colors will be `[R*A, G*A, B*A, A]`, **not** `[R, G, B, A]`.
|
||||
- If your SkSL will return transparent colors, it must be sure to multiply the `RGB` by `A`.
|
||||
- For more complex shaders, you must understand which of your colors are premultiplied vs.
|
||||
unpremultiplied. Many operations don't make sense if you mix both kinds of color together.
|
||||
|
||||
Skia _enforces_ that the color produced by your SkSL is a valid premultiplied color.
|
||||
In other words, `RGB <= A`. If your SkSL returns colors where that is not true, they will be
|
||||
clamped, leading to incorrect colors. The image below demonstrates this: properly premultiplied
|
||||
colors produce a smooth gradient as alpha decreases. Unpremultipled colors cause the gradient to
|
||||
display incorrectly, with a shift in hue as the alpha changes. This hue shift is caused by Skia
|
||||
clamping the color's RGB values to its alpha.
|
||||
|
||||
<fiddle-embed name='e97da657941673896ea6b55703463d8a'></fiddle-embed>
|
||||
|
||||
---
|
||||
|
||||
## <span id="coords">Coordinate Spaces</span>
|
||||
|
||||
Under construction
|
11
site2/docs/user/special/_index.md
Normal file
11
site2/docs/user/special/_index.md
Normal file
@ -0,0 +1,11 @@
|
||||
|
||||
---
|
||||
title: "Specialized Builds"
|
||||
linkTitle: "Specialized Builds"
|
||||
|
||||
weight: 6
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
28
site2/docs/user/special/angle.md
Normal file
28
site2/docs/user/special/angle.md
Normal file
@ -0,0 +1,28 @@
|
||||
|
||||
---
|
||||
title: "ANGLE"
|
||||
linkTitle: "ANGLE"
|
||||
|
||||
---
|
||||
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
ANGLE converts OpenGL ES 2 or 3 calls to DirectX 9, 11, or OpenGL calls. These
|
||||
instructions document how to use ANGLE instead of the native OpenGL backend on
|
||||
Windows or Linux.
|
||||
|
||||
Details
|
||||
-------
|
||||
|
||||
`gclient sync` downloads ANGLE's source alongside Skia's other test-only dependencies.
|
||||
|
||||
To build Skia testing tools against ANGLE, add `skia_use_angle = true` to your
|
||||
`args.gn` file (or run `gn args` to edit it).
|
||||
|
||||
When running tools, use `--config angle_<backend>_<frontend>`, e.g.
|
||||
|
||||
out/Debug/dm --src gm --config angle_d3d11_es2
|
||||
out/Release/nanobench --config angle_gl_es2
|
||||
|
61
site2/docs/user/special/vulkan.md
Normal file
61
site2/docs/user/special/vulkan.md
Normal file
@ -0,0 +1,61 @@
|
||||
|
||||
---
|
||||
title: "Vulkan"
|
||||
linkTitle: "Vulkan"
|
||||
|
||||
---
|
||||
|
||||
|
||||
Skia has a Vulkan implementation of its GPU backend. The Vulkan backend can be
|
||||
built alongside the OpenGL backend. The client can select between the OpenGL
|
||||
and Vulkan implementation at runtime. The Vulkan backend has reached feature
|
||||
parity with the OpenGL backend. At this time we find that many Vulkan drivers
|
||||
have bugs that Skia triggers for which we have no workaround. We are reporting
|
||||
bugs to vendors as we find them.
|
||||
|
||||
Windows and Linux
|
||||
-----------------
|
||||
To build the Vulkan backend, set `skia_use_vulkan=true` in `args.gn`.
|
||||
|
||||
Android
|
||||
-------
|
||||
The Vulkan backend can run on any device with Vulkan drivers, including all Android N+ devices.
|
||||
To build the Vulkan backend, set `ndk_api = 24` in `args.gn` to target Android N.
|
||||
|
||||
Using the Vulkan Backend
|
||||
------------------------
|
||||
|
||||
To create a GrContext that is backed by Vulkan the client creates a Vulkan device and queue, initializes a GrVkBackendContext to describe the context, and then calls GrContext::MakeVulkan:
|
||||
|
||||
<!--?prettify lang=c++?-->
|
||||
sk_sp<GrVkBackendContext> vkContext = new GrVkBackendContext;
|
||||
vkBackendContext.fInstance = vkInstance;
|
||||
vkBackendContext.fPhysicalDevice = vkPhysDevice;
|
||||
...
|
||||
vkBackendContext.fInterface.reset(GrVkCreateInterface(instance, vkPhysDevice, extensionFlags);
|
||||
...
|
||||
|
||||
sk_sp<GrContext> context = GrContext::MakeVulkan(vkBackendContext);
|
||||
|
||||
When using the Vulkan backend, GrVkImageInfo is used to construct GrBackendTexture
|
||||
and GrBackendRenderTarget objects that in turn are used to create SkSurface and SkImage
|
||||
objects that refer to VkImages created by the Skia client.
|
||||
|
||||
The GrBackendObject returned by SkImage::getTextureHandle(),
|
||||
SkSurface::getTextureHandle(), and SkSurface::getRenderTargetHandle() should be
|
||||
interpreted as a GrVkImageInfo*. This allows a client to get the backing VkImage
|
||||
of a SkImage or SkSurface.
|
||||
|
||||
GrVkImageInfo specifies a VkImage and associated state (tiling, layout, format, etc).
|
||||
After getting a GrVkImageInfo* via getTextureHandle() or
|
||||
getRenderTargetHandle(), the client should check the fImageLayout field to know
|
||||
what layout Skia left the VkImage in before using the VkImage. If the client
|
||||
changes the layout of the VkImage,
|
||||
GrVkImageInfo::updateImageLayout(VkImageLayout layout) should be called before
|
||||
resuming Skia rendering.
|
||||
|
||||
The client is responsible for any synchronization or barriers needed before
|
||||
Skia performs I/O on a VkImage imported into Skia via GrVkImageInfo. Skia will
|
||||
assume it can start issuing commands referencing the VkImage without the need
|
||||
for additional synchronization.
|
||||
|
135
site2/docs/user/tips.md
Normal file
135
site2/docs/user/tips.md
Normal file
@ -0,0 +1,135 @@
|
||||
---
|
||||
title: 'Tips & FAQ'
|
||||
linkTitle: 'Tips & FAQ'
|
||||
---
|
||||
|
||||
- [Bitmap Subsetting](#bitmap-subsetting)
|
||||
- [Capture a `.skp` file on a web page in Chromium](#skp-capture)
|
||||
- [Capture a `.mskp` file on a web page in Chromium](#mskp-capture)
|
||||
- [How to add hardware acceleration in Skia](#hw-acceleration)
|
||||
- [Does Skia support Font hinting?](#font-hinting)
|
||||
- [Does Skia shape text (kerning)?](#kerning)
|
||||
- [How do I add drop shadow on text?](#text-shadow)
|
||||
|
||||
---
|
||||
|
||||
## <span id="skp-capture">Capture a `.skp` file on a web page in Chromium</span>
|
||||
|
||||
Use the script `experimental/tools/web_to_skp` , _or_ do the following:
|
||||
|
||||
1. Launch Chrome or Chromium with `--no-sandbox --enable-gpu-benchmarking`
|
||||
2. Open the JS console (Ctrl+Shift+J (Windows / Linux) or Cmd+Opt+J (MacOS))
|
||||
3. Execute: `chrome.gpuBenchmarking.printToSkPicture('/tmp')` This returns
|
||||
"undefined" on success.
|
||||
|
||||
Open the resulting file in the [Skia Debugger](/dev/tools/debugger), rasterize
|
||||
it with `dm`, or use Skia's `viewer` to view it:
|
||||
|
||||
<!--?prettify lang=sh?-->
|
||||
|
||||
out/Release/dm --src skp --skps /tmp/layer_0.skp -w /tmp \
|
||||
--config 8888 gpu pdf --verbose
|
||||
ls -l /tmp/*/skp/layer_0.skp.*
|
||||
|
||||
out/Release/viewer --skps /tmp --slide layer_0.skp
|
||||
|
||||
---
|
||||
|
||||
## <span id="mskp-capture">Capture a `.mskp` file on a web page in Chromium</span>
|
||||
|
||||
Multipage Skia Picture files capture the commands sent to produce PDFs and
|
||||
printed documents.
|
||||
|
||||
Use the script `experimental/tools/web_to_mskp` , _or_ do the following:
|
||||
|
||||
1. Launch Chrome or Chromium with `--no-sandbox --enable-gpu-benchmarking`
|
||||
2. Open the JS console (Ctrl+Shift+J (Windows / Linux) or Cmd+Opt+J (MacOS))
|
||||
3. Execute:
|
||||
`chrome.gpuBenchmarking.printPagesToSkPictures('/tmp/filename.mskp')` This
|
||||
returns "undefined" on success.
|
||||
|
||||
Open the resulting file in the [Skia Debugger](/dev/tools/debugger) or process
|
||||
it with `dm`.
|
||||
|
||||
<!--?prettify lang=sh?-->
|
||||
|
||||
experimental/tools/mskp_parser.py /tmp/filename.mskp /tmp/filename.mskp.skp
|
||||
ls -l /tmp/filename.mskp.skp
|
||||
# open filename.mskp.skp in the debugger.
|
||||
|
||||
out/Release/dm --src mskp --mskps /tmp/filename.mskp -w /tmp \
|
||||
--config pdf --verbose
|
||||
ls -l /tmp/pdf/mskp/filename.mskp.pdf
|
||||
|
||||
---
|
||||
|
||||
## <span id="hw-acceleration">How to add hardware acceleration in Skia</span>
|
||||
|
||||
There are two ways Skia takes advantage of specific hardware.
|
||||
|
||||
1. Custom bottleneck routines
|
||||
|
||||
There are sets of bottleneck routines inside the blits of Skia that can be
|
||||
replace on a platform in order to take advantage of specific CPU features.
|
||||
One such example is the NEON SIMD instructions on ARM v7 devices. See
|
||||
[src/opts/](https://skia.googlesource.com/skia/+/master/src/opts/)
|
||||
|
||||
---
|
||||
|
||||
## <span id="font-hinting">Does Skia support Font hinting?</span>
|
||||
|
||||
Skia has a built-in font cache, but it does not know how to actually render font
|
||||
files like TrueType into its cache. For that it relies on the platform to supply
|
||||
an instance of `SkScalerContext`. This is Skia's abstract interface for
|
||||
communicating with a font scaler engine. In src/ports you can see support files
|
||||
for FreeType, macOS, and Windows GDI font engines. Other font engines can easily
|
||||
be supported in a like manner.
|
||||
|
||||
---
|
||||
|
||||
## <span id="kerning">Does Skia shape text (kerning)?</span>
|
||||
|
||||
Shaping is the process that translates a span of Unicode text into a span of
|
||||
positioned glyphs with the appropriate typefaces.
|
||||
|
||||
Skia does not shape text. Skia provides interfaces to draw glyphs, but does not
|
||||
implement a text shaper. Skia's client's often use
|
||||
[HarfBuzz](http://www.freedesktop.org/wiki/Software/HarfBuzz/) to generate the
|
||||
glyphs and their positions, including kerning.
|
||||
|
||||
[Here is an example of how to use Skia and HarfBuzz together](https://github.com/aam/skiaex).
|
||||
In the example, a `SkTypeface` and a `hb_face_t` are created using the same
|
||||
`mmap()`ed `.ttf` font file. The HarfBuzz face is used to shape unicode text
|
||||
into a sequence of glyphs and positions, and the `SkTypeface` can then be used
|
||||
to draw those glyphs.
|
||||
|
||||
---
|
||||
|
||||
## <span id="text-shadow">How do I add drop shadow on text?</span>
|
||||
|
||||
<!--?prettify lang=cc?-->
|
||||
|
||||
void draw(SkCanvas* canvas) {
|
||||
const SkScalar sigma = 1.65f;
|
||||
const SkScalar xDrop = 2.0f;
|
||||
const SkScalar yDrop = 2.0f;
|
||||
const SkScalar x = 8.0f;
|
||||
const SkScalar y = 52.0f;
|
||||
const SkScalar textSize = 48.0f;
|
||||
const uint8_t blurAlpha = 127;
|
||||
auto blob = SkTextBlob::MakeFromString("Skia", SkFont(nullptr, textSize));
|
||||
SkPaint paint;
|
||||
paint.setAntiAlias(true);
|
||||
SkPaint blur(paint);
|
||||
blur.setAlpha(blurAlpha);
|
||||
blur.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, sigma, 0));
|
||||
canvas->drawColor(SK_ColorWHITE);
|
||||
canvas->drawTextBlob(blob.get(), x + xDrop, y + yDrop, blur);
|
||||
canvas->drawTextBlob(blob.get(), x, y, paint);
|
||||
}
|
||||
|
||||
<a href='https://fiddle.skia.org/c/@text_shadow'><img src='https://fiddle.skia.org/i/@text_shadow_raster.png'></a>
|
||||
|
||||
---
|
||||
|
||||
<div style="margin-bottom:99%"></div>
|
BIN
site2/featured-background.png
Normal file
BIN
site2/featured-background.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 35 KiB |
6
site2/search.md
Normal file
6
site2/search.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
title: Search Results
|
||||
layout: search
|
||||
|
||||
---
|
||||
|
Loading…
Reference in New Issue
Block a user