summaryrefslogtreecommitdiff
path: root/chromium/third_party/skia/modules
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/third_party/skia/modules')
-rw-r--r--chromium/third_party/skia/modules/canvaskit/BUILD.gn6
-rw-r--r--chromium/third_party/skia/modules/canvaskit/CHANGELOG.md104
-rw-r--r--chromium/third_party/skia/modules/canvaskit/Makefile5
-rw-r--r--chromium/third_party/skia/modules/canvaskit/README.md77
-rw-r--r--chromium/third_party/skia/modules/canvaskit/canvaskit/README.md14
-rw-r--r--chromium/third_party/skia/modules/canvaskit/canvaskit/example.html118
-rw-r--r--chromium/third_party/skia/modules/canvaskit/canvaskit/extra.html552
-rw-r--r--chromium/third_party/skia/modules/canvaskit/canvaskit/node.example.js4
-rw-r--r--chromium/third_party/skia/modules/canvaskit/canvaskit/package.json2
-rw-r--r--chromium/third_party/skia/modules/canvaskit/canvaskit/viewer.html213
-rw-r--r--chromium/third_party/skia/modules/canvaskit/canvaskit_bindings.cpp303
-rwxr-xr-xchromium/third_party/skia/modules/canvaskit/compile.sh25
-rw-r--r--chromium/third_party/skia/modules/canvaskit/cpu.js23
-rw-r--r--chromium/third_party/skia/modules/canvaskit/externs.js37
-rw-r--r--chromium/third_party/skia/modules/canvaskit/font.js8
-rw-r--r--chromium/third_party/skia/modules/canvaskit/future_apis/ImageDecoder.md62
-rw-r--r--chromium/third_party/skia/modules/canvaskit/future_apis/WebGPU.md17
-rw-r--r--chromium/third_party/skia/modules/canvaskit/gpu.js93
-rw-r--r--chromium/third_party/skia/modules/canvaskit/helper.js315
-rw-r--r--chromium/third_party/skia/modules/canvaskit/htmlcanvas/_namedcolors.js4
-rw-r--r--chromium/third_party/skia/modules/canvaskit/htmlcanvas/canvas2dcontext.js3
-rw-r--r--chromium/third_party/skia/modules/canvaskit/interface.js405
-rw-r--r--chromium/third_party/skia/modules/canvaskit/karma.conf.js13
-rw-r--r--chromium/third_party/skia/modules/canvaskit/package-lock.json3494
-rw-r--r--chromium/third_party/skia/modules/canvaskit/package.json1
-rw-r--r--chromium/third_party/skia/modules/canvaskit/paragraph.js109
-rw-r--r--chromium/third_party/skia/modules/canvaskit/paragraph_bindings.cpp22
-rw-r--r--chromium/third_party/skia/modules/canvaskit/particles.js8
-rw-r--r--chromium/third_party/skia/modules/canvaskit/perf/assets/test_1500x959.jpgbin0 -> 1004868 bytes
-rw-r--r--chromium/third_party/skia/modules/canvaskit/perf/assets/test_512x512.pngbin0 -> 625834 bytes
-rw-r--r--chromium/third_party/skia/modules/canvaskit/perf/assets/test_64x64.pngbin0 -> 896 bytes
-rw-r--r--chromium/third_party/skia/modules/canvaskit/perf/canvas.bench.js169
-rw-r--r--chromium/third_party/skia/modules/canvaskit/ready.js16
-rw-r--r--chromium/third_party/skia/modules/canvaskit/rt_shader.js18
-rw-r--r--chromium/third_party/skia/modules/canvaskit/skottie.js11
-rw-r--r--chromium/third_party/skia/modules/canvaskit/viewer_bindings.cpp58
-rw-r--r--chromium/third_party/skia/modules/particles/src/SkParticleEffect.cpp1
-rw-r--r--chromium/third_party/skia/modules/pathkit/CHANGELOG.md6
-rwxr-xr-xchromium/third_party/skia/modules/pathkit/compile.sh5
-rw-r--r--chromium/third_party/skia/modules/pathkit/npm-asmjs/README.md6
-rw-r--r--chromium/third_party/skia/modules/pathkit/npm-asmjs/example.html2
-rw-r--r--chromium/third_party/skia/modules/pathkit/npm-wasm/README.md6
-rw-r--r--chromium/third_party/skia/modules/pathkit/npm-wasm/example.html2
-rw-r--r--chromium/third_party/skia/modules/pathkit/pathkit_wasm_bindings.cpp21
-rw-r--r--chromium/third_party/skia/modules/pathkit/perf/perfReporter.js49
-rw-r--r--chromium/third_party/skia/modules/pathkit/ready.js16
-rw-r--r--chromium/third_party/skia/modules/skottie/gm/ExternalProperties.cpp9
-rw-r--r--chromium/third_party/skia/modules/skottie/gm/SkottieGM.cpp9
-rw-r--r--chromium/third_party/skia/modules/skottie/include/Skottie.h11
-rw-r--r--chromium/third_party/skia/modules/skottie/src/Layer.cpp9
-rw-r--r--chromium/third_party/skia/modules/skottie/src/Layer.h1
-rw-r--r--chromium/third_party/skia/modules/skottie/src/SkottiePriv.h21
-rw-r--r--chromium/third_party/skia/modules/skottie/src/SkottieTool.cpp13
-rw-r--r--chromium/third_party/skia/modules/skottie/src/Transform.cpp27
-rw-r--r--chromium/third_party/skia/modules/skottie/src/Transform.h6
-rw-r--r--chromium/third_party/skia/modules/skottie/src/animator/Animator.cpp7
-rw-r--r--chromium/third_party/skia/modules/skottie/src/animator/Animator.h13
-rw-r--r--chromium/third_party/skia/modules/skottie/src/animator/KeyframeAnimator.h6
-rw-r--r--chromium/third_party/skia/modules/skottie/src/animator/ScalarKeyframeAnimator.cpp19
-rw-r--r--chromium/third_party/skia/modules/skottie/src/animator/ShapeKeyframeAnimator.cpp4
-rw-r--r--chromium/third_party/skia/modules/skottie/src/animator/TextKeyframeAnimator.cpp17
-rw-r--r--chromium/third_party/skia/modules/skottie/src/animator/Vec2KeyframeAnimator.cpp70
-rw-r--r--chromium/third_party/skia/modules/skottie/src/animator/VectorKeyframeAnimator.cpp37
-rw-r--r--chromium/third_party/skia/modules/skottie/src/animator/VectorKeyframeAnimator.h11
-rw-r--r--chromium/third_party/skia/modules/skottie/src/effects/MotionTileEffect.cpp2
-rw-r--r--chromium/third_party/skia/modules/skottie/src/layers/TextLayer.cpp327
-rw-r--r--chromium/third_party/skia/modules/skottie/src/layers/shapelayer/Repeater.cpp2
-rw-r--r--chromium/third_party/skia/modules/skottie/src/text/TextValue.cpp33
-rw-r--r--chromium/third_party/skia/modules/skparagraph/BUILD.gn26
-rw-r--r--chromium/third_party/skia/modules/skparagraph/bench/ParagraphBench.cpp64
-rw-r--r--chromium/third_party/skia/modules/skparagraph/gm/simple_gm.cpp120
-rw-r--r--chromium/third_party/skia/modules/skparagraph/include/DartTypes.h4
-rw-r--r--chromium/third_party/skia/modules/skparagraph/include/Paragraph.h3
-rw-r--r--chromium/third_party/skia/modules/skparagraph/include/ParagraphCache.h12
-rw-r--r--chromium/third_party/skia/modules/skparagraph/include/ParagraphStyle.h10
-rw-r--r--chromium/third_party/skia/modules/skparagraph/include/TypefaceFontProvider.h4
-rw-r--r--chromium/third_party/skia/modules/skparagraph/samples/SampleParagraph.cpp2946
-rw-r--r--chromium/third_party/skia/modules/skparagraph/skparagraph.gni2
-rw-r--r--chromium/third_party/skia/modules/skparagraph/src/Decorations.cpp52
-rw-r--r--chromium/third_party/skia/modules/skparagraph/src/Decorations.h4
-rw-r--r--chromium/third_party/skia/modules/skparagraph/src/FontCollection.cpp4
-rw-r--r--chromium/third_party/skia/modules/skparagraph/src/Iterators.h12
-rw-r--r--chromium/third_party/skia/modules/skparagraph/src/OneLineShaper.cpp97
-rw-r--r--chromium/third_party/skia/modules/skparagraph/src/OneLineShaper.h8
-rw-r--r--chromium/third_party/skia/modules/skparagraph/src/ParagraphBuilderImpl.cpp23
-rw-r--r--chromium/third_party/skia/modules/skparagraph/src/ParagraphCache.cpp18
-rw-r--r--chromium/third_party/skia/modules/skparagraph/src/ParagraphImpl.cpp706
-rw-r--r--chromium/third_party/skia/modules/skparagraph/src/ParagraphImpl.h136
-rw-r--r--chromium/third_party/skia/modules/skparagraph/src/ParagraphStyle.cpp11
-rw-r--r--chromium/third_party/skia/modules/skparagraph/src/ParagraphUtil.cpp34
-rw-r--r--chromium/third_party/skia/modules/skparagraph/src/ParagraphUtil.h14
-rw-r--r--chromium/third_party/skia/modules/skparagraph/src/Run.cpp176
-rw-r--r--chromium/third_party/skia/modules/skparagraph/src/Run.h132
-rw-r--r--chromium/third_party/skia/modules/skparagraph/src/TextLine.cpp224
-rw-r--r--chromium/third_party/skia/modules/skparagraph/src/TextLine.h28
-rw-r--r--chromium/third_party/skia/modules/skparagraph/src/TextStyle.cpp3
-rw-r--r--chromium/third_party/skia/modules/skparagraph/src/TextWrapper.cpp4
-rw-r--r--chromium/third_party/skia/modules/skparagraph/src/TextWrapper.h1
-rw-r--r--chromium/third_party/skia/modules/skparagraph/src/TypefaceFontProvider.cpp8
-rw-r--r--chromium/third_party/skia/modules/skplaintexteditor/BUILD.gn3
-rw-r--r--chromium/third_party/skia/modules/skplaintexteditor/src/word_boundaries.cpp55
-rw-r--r--chromium/third_party/skia/modules/skresources/include/SkResources.h16
-rw-r--r--chromium/third_party/skia/modules/skresources/src/SkResources.cpp5
-rw-r--r--chromium/third_party/skia/modules/sksg/include/SkSGGroup.h1
-rw-r--r--chromium/third_party/skia/modules/sksg/include/SkSGTransform.h2
-rw-r--r--chromium/third_party/skia/modules/sksg/src/SkSGGroup.cpp2
-rw-r--r--chromium/third_party/skia/modules/skshaper/BUILD.gn13
-rw-r--r--chromium/third_party/skia/modules/skshaper/include/SkShaper.h3
-rw-r--r--chromium/third_party/skia/modules/skshaper/skshaper.gni2
-rw-r--r--chromium/third_party/skia/modules/skshaper/src/SkShaper_coretext.cpp74
-rw-r--r--chromium/third_party/skia/modules/skshaper/src/SkShaper_harfbuzz.cpp106
111 files changed, 10237 insertions, 2008 deletions
diff --git a/chromium/third_party/skia/modules/canvaskit/BUILD.gn b/chromium/third_party/skia/modules/canvaskit/BUILD.gn
index f227840a4f6..861db54b1a8 100644
--- a/chromium/third_party/skia/modules/canvaskit/BUILD.gn
+++ b/chromium/third_party/skia/modules/canvaskit/BUILD.gn
@@ -6,6 +6,10 @@
component("viewer_wasm") {
testonly = true
include_dirs = [ "../.." ]
- sources = [ "../../tools/viewer/SampleSlide.cpp" ]
+ sources = [
+ "../../tools/viewer/SKPSlide.cpp",
+ "../../tools/viewer/SampleSlide.cpp",
+ "../../tools/viewer/SvgSlide.cpp",
+ ]
deps = [ "../..:samples" ]
}
diff --git a/chromium/third_party/skia/modules/canvaskit/CHANGELOG.md b/chromium/third_party/skia/modules/canvaskit/CHANGELOG.md
index 51f388f1112..7facb95265b 100644
--- a/chromium/third_party/skia/modules/canvaskit/CHANGELOG.md
+++ b/chromium/third_party/skia/modules/canvaskit/CHANGELOG.md
@@ -7,9 +7,103 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Added
+ - Added `CanvasKit.MakeImageFromCanvasImageSource` which takes either an HTMLImageElement,
+ SVGImageElement, HTMLVideoElement, HTMLCanvasElement, ImageBitmap, or OffscreenCanvas and returns
+ an SkImage. This function is an alternative to `CanvasKit.MakeImageFromEncoded` for creating
+ SkImages when loading and decoding images. In the future, codesize of CanvasKit may be able to be
+ reduced by removing image codecs in wasm, if browser APIs for decoding images are used along with
+ `CanvasKit.MakeImageFromCanvasImageSource` instead of `CanvasKit.MakeImageFromEncoded`.
+ - Three usage examples of `CanvasKit.MakeImageFromCanvasImageSource` in core.spec.ts.
+ - Added support for asynchronous callbacks in perfs and tests.
+ - `CanvasKit.SkPath.MakeFromVerbsPointsWeights` and `CanvasKit.SkPath.addVerbsPointsWeights` for
+ supplying many path operations (e.g. moveTo, cubicTo) at once.
+ - The object returned by `CanvasKit.malloc` now has a `subarray` method which works exactly like
+ the normal TypedArray version. The TypedArray which it returns is also backed by WASM memory
+ and when passed into CanvasKit will be used w/o copying the data (just like
+ `Malloc.toTypedArray`).
+
+### Changed
+ - In all places where color arrays are accepted (gradient makers, drawAtlas, and MakeSkVertices),
+ You can now provide either flat Float32Arrays of float colors, Uint32Arrays of int colors, or
+ 2d Arrays of Float32Array(4) colors. The one thing you should not pass is an Array of numbers,
+ since canvaskit wouldn't be able to tell whether they're ints or floats without checking them all.
+ The fastest choice for gradients is the flat Float32Array, the fastest choice for drawAtlas and
+ MakeSkVertices is the flat Uint32Array.
+ - Color arrays may also be objects created with CanvasKit.Malloc
+
+### Fixed
+ - `TextStyle.color` can correctly be a Malloc'd Float32Array.
+
+### Deprecated
+ - `CanvasKit.MakePathFromCmds` has been renamed to `CanvasKit.SkPath.MakeFromCmds`. The alias
+ will be removed in an upcoming release.
+
+## [0.16.2] - 2020-06-05
+
+### Fixed
+ - A bug where loading fonts (and other memory intensive calls) would cause CanvasKit
+ to infrequently crash with
+ `TypeError: Cannot perform %TypedArray%.prototype.set on a neutered ArrayBuffer`.
+ - Incorrectly freeing Malloced colors passed into computeTonalColors.
+
+## [0.16.1] - 2020-06-04
+
+### Fixed
+ - Colors are unsigned to be compatible with Flutter Web and previous behavior, not
+ signed ints.
+
+## [0.16.0] - 2020-06-03
+
+### Added
+ - Support for wide-gamut color spaces DisplayP3 and AdobeRGB. However, correct representation on a
+ WCG monitor requires that the browser is rendering everything to the DisplayP3 or AdobeRGB
+ profile, since there is not yet any way to indicate to the browser that a canvas element has a
+ non-sRGB color space. See color support example in extra.html. Only supported for WebGL2 backed
+ surfaces.
+ - Added `SkSurface.reportBackendType` which returns either 'CPU' or 'GPU'.
+ - Added `SkSurface.imageInfo` which returns an ImageInfo object describing the size and color
+ properties of the surface. colorSpace is added to ImageInfo everywhere it is used.
+ - `CanvasKit.Free` to explicitly clean up memory after `CanvasKit.Malloc`. All memory allocated
+ with `CanvasKit.Malloc` must be released with `CanvasKit.Free` or it will be leaked. This can
+ improve performance by reducing the copying of data between the JS and WASM side.
+ - `CanvasKit.ColorAsInt`, `SkPaint.setColorComponents`, `SkPaint.setColorInt`,
+ `SkCanvas.drawColorComponents`, `SkCanvas.drawColorInt` for when clients want
+ to avoid the overhead of allocating an array for color components and only need 8888 color.
+
+### Changed
+ - We now compile/ship with Emscripten v1.39.16.
+ - `CanvasKit.MakeCanvasSurface` accepts a new enum specifying one of the three color space and
+ pixel format combinations supported by CanvasKit.
+ - all `_Make*Shader` functions now accept a color space argument at the end. leaving it off or
+ passing null makes it behave as it did before, defaulting to sRGB
+ - `SkPaint.setColor` accepts a new color space argument, defaulting to sRGB.
+ - Fewer allocations required to send Color and Matrices between JS and WASM layer.
+ - All APIs that take a 1 dimensional array should also accept the object returned by Malloc. It is
+ recommended to pass the Malloc object, as the TypedArray could be invalidated any time
+ CanvasKit needs to allocate memory and needs to resize to accommodate.
+
+### Breaking
+ - `CanvasKitInit(...)` now directly returns a Promise. As such, `CanvasKitInit(...).ready()`
+ has been removed.
+ - `CanvasKit.MakeCanvasSurface` no longer accepts width/height arguments to override those on
+ the canvas element. Use the canvas element's width/height attributes to dictate the size of
+ the drawing area, and use CSS width/height to set the size it will appear on the page
+ (it is rescaled after drawing when css sizing applies).
+ - Memory returned by `CanvasKit.Malloc` will no longer be automatically cleaned up. Clients
+ must use `CanvasKit.Free` to release the memory.
+ - `CanvasKit.Malloc` no longer directly returns a TypedArray, but an object that can produce
+ them with toTypedArray(). This is to avoid "detached ArrayBuffer" errors:
+ <https://github.com/emscripten-core/emscripten/issues/6747>
+
+### Fixed
+ - WebGL context is no longer created with "antialias" flag. Using "antialias" caused poor AA
+ quality in Ganesh when trying to do coverage-based AA with MSAA unknowingly enabled. It also
+ reduced performance.
+
+## [0.15.0] - 2020-05-14
+
+### Added
- Support for DOMMatrix on all APIs that take SkMatrix (i.e. arrays or Float32Arrays of length 6/9/16).
- - `CanvasKit.MakeWebGLCanvasSurface` takes an option for WebGL version to make it easier to specify
- v1 or v2.
- setEdging and setEmbeddedBitmaps to SkFont. You can disable the ability to draw aliased fonts (and save some code
size) with the compile.sh argument `no_alias_font`.
@@ -20,7 +114,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- CanvasKit colors are now represented with a TypedArray of four floats.
- - Safari now defaults to using WebGL1 instead of WebGL2 (skbug.com/10171)
+ - Calls to `getError` should be disabled. This may cause a performance improvement in some scenarios.
### Removed
- SkPaint.setColorf is obsolete and removed. setColor accepts a CanvasKit color which is
@@ -31,6 +125,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `SkCanvas.concat44` has been folded into concat (which now takes 3x2, 3x3, or 4x4 matrices). It will
be removed soon.
+### Fixed
+ - Memory leak in paragraph binding code (https://github.com/flutter/flutter/issues/56938)
+ - Safari now properly uses WebGL1 instead of WebGL2 when WebGL2 is not available (skbug.com/10171).
+
## [0.14.0] - 2020-03-18
### Added
diff --git a/chromium/third_party/skia/modules/canvaskit/Makefile b/chromium/third_party/skia/modules/canvaskit/Makefile
index 006771cd143..772a4a39b0e 100644
--- a/chromium/third_party/skia/modules/canvaskit/Makefile
+++ b/chromium/third_party/skia/modules/canvaskit/Makefile
@@ -80,6 +80,9 @@ test-continuous:
echo "Also assuming make debug or release has also been run by a user (if needed)"
npx karma start ./karma.conf.js --no-single-run --watch-poll
+test-continuous-headless:
+ npx karma start ./karma.conf.js --no-single-run --watch-poll --headless
+
.PHONY: perf
perf:
npx karma start ./karma.bench.conf.js --single-run
@@ -91,5 +94,5 @@ docker-compile:
mkdir -p ${SKIA_ROOT}/out/canvaskit_wasm_docker
docker run --rm --volume ${SKIA_ROOT}:/SRC \
--volume ${SKIA_ROOT}/out/canvaskit_wasm_docker:/OUT \
- gcr.io/skia-public/canvaskit-emsdk:1.39.6_v1 \
+ gcr.io/skia-public/canvaskit-emsdk:1.39.16_v1 \
/SRC/infra/canvaskit/build_canvaskit.sh
diff --git a/chromium/third_party/skia/modules/canvaskit/README.md b/chromium/third_party/skia/modules/canvaskit/README.md
index 32e5e1097d4..48a197021f7 100644
--- a/chromium/third_party/skia/modules/canvaskit/README.md
+++ b/chromium/third_party/skia/modules/canvaskit/README.md
@@ -4,7 +4,16 @@ To compile CanvasKit, you will first need to [install `emscripten`][1]. This
will set the environment `EMSDK` (among others) which is required for
compilation.
-# Compile and Test Locally
+[1]: https://emscripten.org/docs/getting_started/downloads.html
+
+## MacOS specific notes
+Make sure you have Python3 installed, otherwise the downloading emscripten toolchain
+can fail with errors about SSL certificates. <https://github.com/emscripten-core/emsdk/pull/273>
+
+See also <https://github.com/emscripten-core/emscripten/issues/9036#issuecomment-532092743>
+for a solution to Python3 using the wrong certificates.
+
+# Compile and Run Local Example
```
make release # make debug is much faster and has better error messages
@@ -23,7 +32,67 @@ any of the "extras", one might run:
Such a stripped-down version is about half the size of the default release build.
-[1]: https://emscripten.org/docs/getting_started/downloads.html
+# Unit tests, performance tests, and coverage.
+
+To run unit tests and compute test coverage on a debug gpu build
+
+```
+make debug
+make test-continuous
+```
+
+This reads karma.conf.js, and opens a chrome browser and begins running all the test
+in `test/` it will detect changes to the tests in that directory and automatically
+run again, however it will automatically rebuild and reload canvaskit. Closing the
+chrome window will just cause it to re-opened. Kill the karma process to stop continuous
+monitoring for changes.
+
+The tests are run with whichever build of canvaskit you last made. be sure to also
+test with `release`, `debug_cpu`, and `release_cpu`. testing with release builds will
+expose problems in closure compilation and usually forgotten externs.
+
+## Coverage
+
+Coverage will be automatically computed when running test-continuous locally. Note that
+the results will only be useful when testing a debug build. Open
+`coverage/<browser version>/index.html` For a summary and detailed line-by-line result.
+
+## Measuring Performance
+
+To measure the runtime of all benchmarks in `perf/`
+
+```
+make release
+make perf
+```
+
+Performacnce benchmarks also use karma, with a different config `karma.bench.conf.js`.
+It will run once and print results.
+
+Typically, you'd want to run these at head, and with your CL to observe the effect of some
+optimization.
+
+## Adding tests
+
+The tests in `tests/` and `perf/` are grouped into files by topic.
+Within each file there are `describe` blocks further organizing the tests, and within those
+`it()` functions which test particular behaviors. `describe` and `it` are jasmine methods
+which can both be temporarily renamed `fdescribe` and `fit`. Which causes jasmine to only those.
+
+We have also defined `gm` which is a method for defining a test which draws something to a canvas
+that is shapshotted and reported to gold.skia.org, where you can compare it with the snapshot at
+head.
+
+## Testing from Gerrit
+
+When submitting a CL in gerrit, click "choose tryjobs" and type canvaskit to filter them.
+select all of them, which at the time of this writing is four jobs, for each combination
+of perf/test gpu/cpu.
+
+The performance results are reported to perf.skia.org
+gold results are reported to gold.skia.org
+
+Coverage is not measured while running tests this way.
# Infrastructure Playbook
@@ -51,9 +120,9 @@ sdk and verified/fixed any build issues that have arisen.
5. Edit `$SKIA_ROOT/infra/canvaskit/docker/Makefile` to have the same version
from step 2. It's easiest to keep the `emsdk-base` and `canvaskit-emsdk` versions
be in lock-step.
- 6. In `$SKIA_ROOT/infra/canvaskit/docker/`, make `publish_canvaskit_emsdk`.
+ 6. In `$SKIA_ROOT/infra/canvaskit/docker/`, run `make publish_canvaskit_emsdk`.
7. In `$SKIA_ROOT/infra/bots/recipe_modules/build/`, update `canvaskit.py`
- and `pathkit.py` to have `DOCKER_IMAAGE` point to the desired tagged Docker
+ and `pathkit.py` to have `DOCKER_IMAGE` point to the desired tagged Docker
containers from steps 2 and 5 (which should be the same).
9. In `$SKIA_ROOT/infra/bots/`, run `make train` to re-train the recipes.
10. Optional: Run something like `git grep 1\\.38\\.` in `$SKIA_ROOT` to see if
diff --git a/chromium/third_party/skia/modules/canvaskit/canvaskit/README.md b/chromium/third_party/skia/modules/canvaskit/canvaskit/README.md
index d71507e24af..6288299f5fd 100644
--- a/chromium/third_party/skia/modules/canvaskit/canvaskit/README.md
+++ b/chromium/third_party/skia/modules/canvaskit/canvaskit/README.md
@@ -10,16 +10,16 @@ To use the library, run `npm install canvaskit-wasm` and then simply include it:
<script src="/node_modules/canvaskit-wasm/bin/canvaskit.js"></script>
CanvasKitInit({
locateFile: (file) => '/node_modules/canvaskit-wasm/bin/'+file,
- }).ready().then((CanvasKit) => {
+ }).then((CanvasKit) => {
// Code goes here using CanvasKit
});
As with all npm packages, there's a freely available CDN via unpkg.com:
- <script src="https://unpkg.com/canvaskit-wasm@0.3.0/bin/canvaskit.js"></script>
+ <script src="https://unpkg.com/canvaskit-wasm@0.16.0/bin/canvaskit.js"></script>
CanvasKitInit({
- locateFile: (file) => 'https://unpkg.com/canvaskit-wasm@0.3.0/bin/'+file,
- }).ready().then(...)
+ locateFile: (file) => 'https://unpkg.com/canvaskit-wasm@0.16.0/bin/'+file,
+ }).then(...)
## Node
To use CanvasKit in Node, it's similar to the browser:
@@ -27,7 +27,7 @@ To use CanvasKit in Node, it's similar to the browser:
const CanvasKitInit = require('/node_modules/canvaskit-wasm/bin/canvaskit.js');
CanvasKitInit({
locateFile: (file) => __dirname + '/bin/'+file,
- }).ready().then((CanvasKit) => {
+ }).then((CanvasKit) => {
// Code goes here using CanvasKit
});
@@ -41,7 +41,7 @@ used with a few configuration changes.
In the JS code, use require():
const CanvasKitInit = require('canvaskit-wasm/bin/canvaskit.js')
- CanvasKitInit().ready().then((CanvasKit) => {
+ CanvasKitInit().then((CanvasKit) => {
// Code goes here using CanvasKit
});
@@ -106,4 +106,4 @@ See more examples in `example.html` and `node.example.js`.
Please file bugs at [skbug.com](skbug.com).
It may be convenient to use [our online fiddle](jsfiddle.skia.org/canvaskit) to demonstrate any issues encountered.
-See CONTRIBUTING.md for more information on sending pull requests. \ No newline at end of file
+See CONTRIBUTING.md for more information on sending pull requests.
diff --git a/chromium/third_party/skia/modules/canvaskit/canvaskit/example.html b/chromium/third_party/skia/modules/canvaskit/canvaskit/example.html
index d52b74fed77..68f343f05de 100644
--- a/chromium/third_party/skia/modules/canvaskit/canvaskit/example.html
+++ b/chromium/third_party/skia/modules/canvaskit/canvaskit/example.html
@@ -36,9 +36,11 @@
<canvas id=gradient1 width=300 height=300></canvas>
<canvas id=patheffect width=300 height=300></canvas>
<canvas id=paths width=200 height=200></canvas>
+<canvas id=pathperson width=300 height=300></canvas>
<canvas id=ink width=300 height=300></canvas>
<canvas id=surfaces width=300 height=300></canvas>
<canvas id=atlas width=300 height=300></canvas>
+<canvas id=decode width=300 height=300></canvas>
<h2> CanvasKit can allow for text shaping (e.g. breaking, kerning)</h2>
<canvas id=shape1 width=600 height=600></canvas>
@@ -50,20 +52,24 @@
<script type="text/javascript" charset="utf-8">
var CanvasKit = null;
+ var cdn = 'https://storage.googleapis.com/skia-cdn/misc/';
- var robotoData = null;
- var notoserifData = null;
+ const ckLoaded = CanvasKitInit({locateFile: (file) => '/node_modules/canvaskit/bin/'+file});
- var mandrillData = null;
- var cdn = 'https://storage.googleapis.com/skia-cdn/misc/';
- CanvasKitInit({
- locateFile: (file) => '/node_modules/canvaskit/bin/'+file,
- }).ready().then((CK) => {
+ const loadRoboto = fetch(cdn + 'Roboto-Regular.ttf').then((response) => response.arrayBuffer());
+ const loadNotoSerif = fetch(cdn + 'NotoSerif-Regular.ttf').then((response) => response.arrayBuffer());
+ const loadTestImage = fetch(cdn + 'test.png').then((response) => response.arrayBuffer());
+
+ // Examples which only require canvaskit
+ ckLoaded.then((CK) => {
CanvasKit = CK;
- DrawingExample(CanvasKit, robotoData);
PathExample(CanvasKit);
InkExample(CanvasKit);
-
+ PathPersonExample(CanvasKit);
+ VertexAPI1(CanvasKit);
+ GradiantAPI1(CanvasKit);
+ TextOnPathAPI1(CanvasKit);
+ SurfaceAPI1(CanvasKit);
CanvasAPI1(CanvasKit);
CanvasAPI2(CanvasKit);
CanvasAPI3(CanvasKit);
@@ -72,40 +78,16 @@
CanvasAPI6(CanvasKit);
CanvasAPI7(CanvasKit);
CanvasAPI8(CanvasKit);
-
- VertexAPI1(CanvasKit);
-
- GradiantAPI1(CanvasKit);
-
- TextShapingAPI1(CanvasKit, notoserifData);
- TextShapingAPI2(CanvasKit, notoserifData);
- TextOnPathAPI1(CanvasKit);
-
- SurfaceAPI1(CanvasKit);
-
- AtlasAPI1(CanvasKit, mandrillData);
- });
-
- fetch(cdn + 'Roboto-Regular.ttf').then((resp) => {
- resp.arrayBuffer().then((buffer) => {
- robotoData = buffer;
- DrawingExample(CanvasKit, robotoData);
- });
- });
-
- fetch(cdn + 'NotoSerif-Regular.ttf').then((resp) => {
- resp.arrayBuffer().then((buffer) => {
- notoserifData = buffer;
- TextShapingAPI1(CanvasKit, notoserifData);
- TextShapingAPI2(CanvasKit, notoserifData);
- });
});
- // Mandrill test image
- fetch(cdn + 'test.png').then((response) => response.arrayBuffer()).then((buffer) => {
- mandrillData = buffer;
- AtlasAPI1(CanvasKit, mandrillData);
+ // Examples requiring external resources
+ Promise.all([ckLoaded, loadRoboto]).then((results) => {DrawingExample(...results)});
+ Promise.all([ckLoaded, loadNotoSerif]).then((results) => {
+ TextShapingAPI1(...results);
+ TextShapingAPI2(...results);
});
+ Promise.all([ckLoaded, loadTestImage]).then((results) => {AtlasAPI1(...results)});
+ Promise.all([ckLoaded, loadTestImage]).then((results) => {DecodeAPI(...results)});
function DrawingExample(CanvasKit, robotoData) {
if (!robotoData || !CanvasKit) {
@@ -175,6 +157,38 @@
// textFont.delete();
}
+ function PathPersonExample(CanvasKit) {
+ const surface = CanvasKit.MakeSWCanvasSurface('pathperson');
+ if (!surface) {
+ console.error('Could not make surface');
+ return;
+ }
+
+ function drawFrame(canvas) {
+ const paint = new CanvasKit.SkPaint();
+ paint.setStrokeWidth(1.0);
+ paint.setAntiAlias(true);
+ paint.setColor(CanvasKit.Color(0, 0, 0, 1.0));
+ paint.setStyle(CanvasKit.PaintStyle.Stroke);
+
+ const path = new CanvasKit.SkPath();
+ path.moveTo(10, 10);
+ path.lineTo(100, 10);
+ path.moveTo(10, 10);
+ path.lineTo(10, 200);
+ path.moveTo(10, 100);
+ path.lineTo(100,100);
+ path.moveTo(10, 200);
+ path.lineTo(100, 200);
+
+ canvas.drawPath(path, paint);
+ path.delete();
+ paint.delete();
+ }
+ // Intentionally just draw frame once
+ surface.drawOnce(drawFrame);
+ }
+
function PathExample(CanvasKit) {
const surface = CanvasKit.MakeSWCanvasSurface('paths');
if (!surface) {
@@ -1091,6 +1105,7 @@
height: 50,
alphaType: CanvasKit.AlphaType.Premul,
colorType: CanvasKit.ColorType.RGBA_8888,
+ colorSpace: CanvasKit.SkColorSpace.SRGB,
});
if (!subSurface) {
@@ -1167,8 +1182,8 @@
dsts.push(0, .8, 200, 100);
const colors = new CanvasKit.SkColorBuilder();
- colors.push(CanvasKit.Color(85, 170, 10, 0.5)); // light green
- colors.push(CanvasKit.Color(51, 51, 191, 0.5)); // light blue
+ colors.push(CanvasKit.ColorAsInt(85, 170, 10, 128)); // light green
+ colors.push(CanvasKit.ColorAsInt(51, 51, 191, 128)); // light blue
let i = 0;
@@ -1200,4 +1215,23 @@
surface.requestAnimationFrame(drawFrame);
}
+
+ async function DecodeAPI(CanvasKit, imgData) {
+ if (!CanvasKit || !imgData) {
+ return;
+ }
+ const surface = CanvasKit.MakeCanvasSurface('decode');
+ if (!surface) {
+ console.error('Could not make surface');
+ return;
+ }
+ const blob = new Blob([ imgData ]);
+ // ImageBitmap is not supported in Safari
+ const imageBitmap = await createImageBitmap(blob);
+ const img = await CanvasKit.MakeImageFromCanvasImageSource(imageBitmap);
+
+ surface.drawOnce((canvas) => {
+ canvas.drawImage(img, 0, 0, null);
+ });
+ }
</script>
diff --git a/chromium/third_party/skia/modules/canvaskit/canvaskit/extra.html b/chromium/third_party/skia/modules/canvaskit/canvaskit/extra.html
index 8b0d08a1c7e..720273cf175 100644
--- a/chromium/third_party/skia/modules/canvaskit/canvaskit/extra.html
+++ b/chromium/third_party/skia/modules/canvaskit/canvaskit/extra.html
@@ -34,118 +34,117 @@
<h2> 3D perspective transformations </h2>
<canvas id=camera3d width=500 height=500></canvas>
+<canvas id=glyphgame width=500 height=500></canvas>
-<h2> Use of offscreen surfaces </h2>
-<canvas id=surfaces width=500 height=500></canvas>
+<h2> Support for extended color spaces </h2>
+<a href="chrome://flags/#force-color-profile">Force P3 profile</a>
+<canvas id=colorsupport width=300 height=300></canvas>
<script type="text/javascript" src="/node_modules/canvaskit/bin/canvaskit.js"></script>
<script type="text/javascript" charset="utf-8">
var CanvasKit = null;
- var legoJSON = null;
- var drinksJSON = null;
- var confettiJSON = null;
- var onboardingJSON = null;
- var multiFrameJSON = null;
- var fullBounds = {fLeft: 0, fTop: 0, fRight: 500, fBottom: 500};
-
- var robotoData = null;
- var notoserifData = null;
-
- var flightAnimGif = null;
- var skpData = null;
var cdn = 'https://storage.googleapis.com/skia-cdn/misc/';
- const ckLoaded = CanvasKitInit({
- locateFile: (file) => '/node_modules/canvaskit/bin/'+file,
- }).ready();
- ckLoaded.then((CK) => {
- CanvasKit = CK;
- // Set bounds to fix the 4:3 resolution of the legos
- SkottieExample(CanvasKit, 'sk_legos', legoJSON,
- {fLeft: -50, fTop: 0, fRight: 350, fBottom: 300});
- // Re-size to fit
- SkottieExample(CanvasKit, 'sk_drinks', drinksJSON, fullBounds);
- SkottieExample(CanvasKit, 'sk_party', confettiJSON, fullBounds);
- SkottieExample(CanvasKit, 'sk_onboarding', onboardingJSON, fullBounds);
- SkottieExample(CanvasKit, 'sk_animated_gif', multiFrameJSON, fullBounds, {
- 'image_0.png': flightAnimGif,
- });
- ParticlesAPI1(CanvasKit);
- ParagraphAPI1(CanvasKit, robotoData);
+ const ckLoaded = CanvasKitInit({locateFile: (file) => '/node_modules/canvaskit/bin/'+file});
- RTShaderAPI1(CanvasKit);
+ const loadLegoJSON = fetch(cdn + 'lego_loader.json').then((response) => response.text());
+ const loadDrinksJSON = fetch(cdn + 'drinks.json').then((response) => response.text());
+ const loadConfettiJSON = fetch(cdn + 'confetti.json').then((response) => response.text());
+ const loadOnboardingJSON = fetch(cdn + 'onboarding.json').then((response) => response.text());
+ const loadMultiframeJSON = fetch(cdn + 'skottie_sample_multiframe.json').then((response) => response.text());
- SkpExample(CanvasKit, skpData);
-
- SurfaceAPI1(CanvasKit);
- });
+ const loadFlightGif = fetch(cdn + 'flightAnim.gif').then((response) => response.arrayBuffer());
+ const loadSkp = fetch(cdn + 'picture2.skp').then((response) => response.arrayBuffer());
+ const loadFont = fetch(cdn + 'Roboto-Regular.ttf').then((response) => response.arrayBuffer());
+ const loadDog = fetch(cdn + 'dog.jpg').then((response) => response.arrayBuffer());
+ const loadMandrill = fetch(cdn + 'mandrill_256.png').then((response) => response.arrayBuffer());
+ const loadBrickTex = fetch(cdn + 'brickwork-texture.jpg').then((response) => response.arrayBuffer());
+ const loadBrickBump = fetch(cdn + 'brickwork_normal-map.jpg').then((response) => response.arrayBuffer());
- fetch(cdn + 'lego_loader.json').then((resp) => {
- resp.text().then((str) => {
- legoJSON = str;
- SkottieExample(CanvasKit, 'sk_legos', legoJSON,
- {fLeft: -50, fTop: 0, fRight: 350, fBottom: 300});
- });
- });
+ const curves = {
+ "MaxCount": 1000,
+ "Drawable": {
+ "Type": "SkCircleDrawable",
+ "Radius": 2
+ },
+ "EffectCode": [`
+ void effectSpawn(inout Effect effect) {
+ effect.rate = 200;
+ effect.color = float4(1, 0, 0, 1);
+ }
+ `
+ ],
+ "Code": [`
+ 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": []
+ };
- fetch(cdn + 'drinks.json').then((resp) => {
- resp.text().then((str) => {
- drinksJSON = str;
- SkottieExample(CanvasKit, 'sk_drinks', drinksJSON, fullBounds);
- });
+ const spiralSkSL = `
+ uniform float rad_scale;
+ uniform float2 in_center;
+ uniform float4 in_colors0;
+ uniform float4 in_colors1;
+
+ void main(float2 p, inout half4 color) {
+ 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);
+ color = half4(mix(in_colors0, in_colors1, t));
+ }`;
+
+ // Examples which only require canvaskit
+ ckLoaded.then((CK) => {
+ CanvasKit = CK;
+ ParticlesAPI1(CanvasKit);
+ RTShaderAPI1(CanvasKit);
+ ColorSupport(CanvasKit);
});
- fetch(cdn + 'confetti.json').then((resp) => {
- resp.text().then((str) => {
- confettiJSON = str;
- SkottieExample(CanvasKit, 'sk_party', confettiJSON, fullBounds);
- });
+ // Examples requiring external resources.
+ // Set bounds to fix the 4:3 resolution of the legos
+ Promise.all([ckLoaded, loadLegoJSON]).then(([ck, jsonstr]) => {
+ SkottieExample(ck, 'sk_legos', jsonstr, {fLeft: -50, fTop: 0, fRight: 350, fBottom: 300});
});
-
- fetch(cdn + 'onboarding.json').then((resp) => {
- resp.text().then((str) => {
- onboardingJSON = str;
- SkottieExample(CanvasKit, 'sk_onboarding', onboardingJSON, fullBounds);
- });
+ // Re-size to fit
+ let fullBounds = {fLeft: 0, fTop: 0, fRight: 500, fBottom: 500};
+ Promise.all([ckLoaded, loadDrinksJSON]).then(([ck, jsonstr]) => {
+ SkottieExample(ck, 'sk_drinks', jsonstr, fullBounds);
});
-
- fetch(cdn + 'skottie_sample_multiframe.json').then((resp) => {
- resp.text().then((str) => {
- multiFrameJSON = str;
- SkottieExample(CanvasKit, 'sk_animated_gif', multiFrameJSON, fullBounds, {
- 'image_0.png': flightAnimGif,
- });
- });
+ Promise.all([ckLoaded, loadConfettiJSON]).then(([ck, jsonstr]) => {
+ SkottieExample(ck, 'sk_party', jsonstr, fullBounds);
});
-
- fetch(cdn + 'flightAnim.gif').then((resp) => {
- resp.arrayBuffer().then((buffer) => {
- flightAnimGif = buffer;
- SkottieExample(CanvasKit, 'sk_animated_gif', multiFrameJSON, fullBounds, {
- 'image_0.png': flightAnimGif,
- });
- });
+ Promise.all([ckLoaded, loadOnboardingJSON]).then(([ck, jsonstr]) => {
+ SkottieExample(ck, 'sk_onboarding', jsonstr, fullBounds);
});
-
- fetch(cdn + 'Roboto-Regular.ttf').then((resp) => {
- resp.arrayBuffer().then((buffer) => {
- robotoData = buffer;
- ParagraphAPI1(CanvasKit, robotoData);
- });
+ Promise.all([ckLoaded, loadMultiframeJSON, loadFlightGif]).then(([ck, jsonstr, gif]) => {
+ SkottieExample(ck, 'sk_animated_gif', jsonstr, fullBounds, {'image_0.png': gif});
});
- fetch(cdn + 'picture2.skp').then((response) => response.arrayBuffer()).then((buffer) => {
- skpData = buffer;
- SkpExample(CanvasKit, skpData);
+ Promise.all([ckLoaded, loadFont]).then((results) => {
+ ParagraphAPI1(...results);
+ GlyphGame(...results)
});
-
- const loadDog = fetch(cdn + 'dog.jpg').then((response) => response.arrayBuffer());
- const loadMandrill = fetch(cdn + 'mandrill_256.png').then((response) => response.arrayBuffer());
- const loadBrickTex = fetch(cdn + 'brickwork-texture.jpg').then((response) => response.arrayBuffer());
- const loadBrickBump = fetch(cdn + 'brickwork_normal-map.jpg').then((response) => response.arrayBuffer());
- Promise.all([ckLoaded, loadBrickTex, loadBrickBump]).then((results) => {Camera3D(...results)});
+ Promise.all([ckLoaded, loadSkp]).then((results) => {SkpExample(...results)});
+ Promise.all([ckLoaded, loadBrickTex, loadBrickBump, loadFont]).then((results) => {Camera3D(...results)});
function SkottieExample(CanvasKit, id, jsonStr, bounds, assets) {
if (!CanvasKit || !jsonStr) {
@@ -209,107 +208,6 @@
surface.requestAnimationFrame(drawFrame);
}
-const curves = {
- "MaxCount": 1000,
- "Drawable": {
- "Type": "SkCircleDrawable",
- "Radius": 2
- },
- "EffectCode": [
- "void effectSpawn(inout Effect effect) {",
- " effect.rate = 200;",
- " effect.color = float4(1, 0, 0, 1);",
- "}",
- ""
- ],
- "Code": [
- "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": []
-};
-
- function SurfaceAPI1(CanvasKit) {
- const surface = CanvasKit.MakeCanvasSurface('surfaces');
- if (!surface) {
- console.error('Could not make surface');
- return;
- }
- console.log('SurfaceAPI1 top surface type = '+surface.reportBackendType() );
- const context = CanvasKit.currentContext();
- const canvas = surface.getCanvas();
-
- //create a subsurface as a temporary workspace.
- const subSurface = surface.makeSurface({
- width: 50,
- height: 50,
- alphaType: CanvasKit.AlphaType.Premul,
- colorType: CanvasKit.ColorType.RGBA_8888,
- });
-
- if (!subSurface) {
- console.error('Could not make subsurface');
- return;
- }
- console.log('SurfaceAPI1 subSurface type = '+subSurface.reportBackendType() );
-
- // draw a small "scene"
- const paint = new CanvasKit.SkPaint();
- paint.setColor(CanvasKit.Color(139, 228, 135, 0.95)); // greenish
- paint.setStyle(CanvasKit.PaintStyle.Fill);
- paint.setAntiAlias(true);
-
- const subCanvas = subSurface.getCanvas();
- subCanvas.clear(CanvasKit.BLACK);
- subCanvas.drawRect(CanvasKit.LTRBRect(5, 15, 45, 40), paint);
-
- paint.setColor(CanvasKit.Color(214, 93, 244)); // purplish
- for (let i = 0; i < 10; i++) {
- const x = Math.random() * 50;
- const y = Math.random() * 50;
-
- subCanvas.drawOval(CanvasKit.XYWHRect(x, y, 6, 6), paint);
- }
-
- // Snap it off as an SkImage - this image will be in the form the
- // parent surface prefers (e.g. Texture for GPU / Raster for CPU).
- const img = subSurface.makeImageSnapshot();
-
- // clean up the temporary surface
- subSurface.delete();
- paint.delete();
-
- // Make it repeat a bunch with a shader
- const pattern = img.makeShader(CanvasKit.TileMode.Repeat, CanvasKit.TileMode.Mirror);
- const patternPaint = new CanvasKit.SkPaint();
- patternPaint.setShader(pattern);
-
- let i = 0;
-
- function drawFrame() {
- i++;
- CanvasKit.setCurrentContext(context);
- canvas.clear(CanvasKit.WHITE);
-
- canvas.drawOval(CanvasKit.LTRBRect(i % 60, i % 60, 300 - (i% 60), 300 - (i % 60)), patternPaint);
- surface.flush();
- window.requestAnimationFrame(drawFrame);
- }
- window.requestAnimationFrame(drawFrame);
-
- }
-
function ParagraphAPI1(CanvasKit, fontData) {
if (!CanvasKit || !fontData) {
return;
@@ -372,23 +270,6 @@ const curves = {
return surface;
}
- const spiralSkSL = `
- uniform float rad_scale;
- uniform float2 in_center;
- uniform float4 in_colors0;
- uniform float4 in_colors1;
-
- void main(float2 p, inout half4 color) {
- 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);
- color = half4(mix(in_colors0, in_colors1, t));
- }`;
-
function RTShaderAPI1(CanvasKit) {
if (!CanvasKit) {
return;
@@ -448,9 +329,9 @@ const curves = {
}
const prog = `
- in fragmentProcessor before_map;
- in fragmentProcessor after_map;
- in fragmentProcessor threshold_map;
+ in shader before_map;
+ in shader after_map;
+ in shader threshold_map;
uniform float cutoff;
uniform float slope;
@@ -557,10 +438,37 @@ const curves = {
surface.drawOnce(drawFrame);
}
- function Camera3D(canvas, textureImgData, normalImgData) {
- if (!canvas) {
-
+ // Return the inverse of an SkM44. throw an error if it's not invertible
+ function mustInvert(m) {
+ const m2 = CanvasKit.SkM44.invert(m);
+ if (m2 === null) {
+ throw "Matrix not invertible";
}
+ return m2;
+ }
+
+ // TODO(nifong): This function is in desperate need of some explanation of what it does
+ // cam is a object having eye, coa, up, near, far, angle
+ function saveCamera(canvas, /* rect */ area, /* scalar */ zscale, cam) {
+ const camera = CanvasKit.SkM44.lookat(cam.eye, cam.coa, cam.up);
+ const perspective = CanvasKit.SkM44.perspective(cam.near, cam.far, cam.angle);
+ // Calculate viewport scale. Even through we know these values are all constants in this
+ // example it might be handy to change the size later.
+ const center = [(area.fLeft + area.fRight)/2, (area.fTop + area.fBottom)/2, 0];
+ const viewScale = [(area.fRight - area.fLeft)/2, (area.fBottom - area.fTop)/2, zscale];
+ const viewport = CanvasKit.SkM44.multiply(
+ CanvasKit.SkM44.translated(center),
+ CanvasKit.SkM44.scaled(viewScale));
+
+ // want "world" to be in our big coordinates (e.g. area), so apply this inverse
+ // as part of our "camera".
+ canvas.concat(CanvasKit.SkM44.multiply(viewport, perspective));
+ canvas.concat(CanvasKit.SkM44.multiply(camera, mustInvert(viewport)));
+ // Mark the matrix to make it available to the shader by this name.
+ canvas.markCTM('local_to_world');
+ }
+
+ function Camera3D(canvas, textureImgData, normalImgData, robotoData) {
const surface = CanvasKit.MakeCanvasSurface('camera3d');
if (!surface) {
console.error('Could not make surface');
@@ -586,13 +494,15 @@ const curves = {
const rr = CanvasKit.RRectXY(CanvasKit.LTRBRect(margin, margin,
vSphereRadius - margin, vSphereRadius - margin), margin*2.5, margin*2.5);
- const camNear = 0.05;
- const camFar = 4;
const camAngle = Math.PI / 12;
-
- const camEye = [0, 0, 1 / Math.tan(camAngle/2) - 1];
- const camCOA = [0, 0, 0];
- const camUp = [0, 1, 0];
+ 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
@@ -619,8 +529,8 @@ const curves = {
const children = [textureShader, normalShader];
const prog = `
- in fragmentProcessor color_map;
- in fragmentProcessor normal_map;
+ in shader color_map;
+ in shader normal_map;
uniform float3 lightPos;
layout (marker=local_to_world) uniform float4x4 localToWorld;
@@ -706,34 +616,6 @@ const curves = {
return m2[10]
}
- // Return the inverse of an SkM44. throw an error if it's not invertible
- function mustInvert(m) {
- var m2 = CanvasKit.SkM44.invert(m);
- if (m2 === null) {
- throw "Matrix not invertible";
- }
- return m2;
- }
-
- function saveCamera(canvas, /* rect */ area, /* scalar */ zscale) {
- const camera = CanvasKit.SkM44.lookat(camEye, camCOA, camUp);
- const perspective = CanvasKit.SkM44.perspective(camNear, camFar, camAngle);
- // Calculate viewport scale. Even through we know these values are all constants in this
- // example it might be handy to change the size later.
- const center = [(area.fLeft + area.fRight)/2, (area.fTop + area.fBottom)/2, 0];
- const viewScale = [(area.fRight - area.fLeft)/2, (area.fBottom - area.fTop)/2, zscale];
- const viewport = CanvasKit.SkM44.multiply(
- CanvasKit.SkM44.translated(center),
- CanvasKit.SkM44.scaled(viewScale));
-
- // want "world" to be in our big coordinates (e.g. area), so apply this inverse
- // as part of our "camera".
- canvas.concat(CanvasKit.SkM44.multiply(viewport, perspective));
- canvas.concat(CanvasKit.SkM44.multiply(camera, mustInvert(viewport)));
- // Mark the matrix to make it available to the shader by this name.
- canvas.markCTM('local_to_world');
- }
-
function setClickToWorld(canvas, matrix) {
const l2d = canvas.getLocalToDevice();
worldToClick = CanvasKit.SkM44.multiply(mustInvert(matrix), l2d);
@@ -763,7 +645,7 @@ const curves = {
canvas.save();
canvas.translate(vSphereCenter[0] - vSphereRadius/2, vSphereCenter[1] - vSphereRadius/2);
// pass surface dimensions as viewport size.
- saveCamera(canvas, CanvasKit.LTRBRect(0, 0, vSphereRadius, vSphereRadius), vSphereRadius/2);
+ saveCamera(canvas, CanvasKit.LTRBRect(0, 0, vSphereRadius, vSphereRadius), vSphereRadius/2, cam);
setClickToWorld(canvas, clickM);
for (let f of faces) {
const saveCount = canvas.getSaveCount();
@@ -890,4 +772,172 @@ const curves = {
surface.requestAnimationFrame(drawFrame);
}
+
+ // Shows a hidden message by rotating all the characters in a kind of way that makes you
+ // search with your mouse.
+ function GlyphGame(canvas, robotoData) {
+ const surface = CanvasKit.MakeCanvasSurface('glyphgame');
+ if (!surface) {
+ console.error('Could not make surface');
+ return;
+ }
+ const sizeX = document.getElementById('glyphgame').width;
+ const sizeY = document.getElementById('glyphgame').height;
+ const halfDim = Math.min(sizeX, sizeY) / 2;
+ const margin = 50;
+ const marginTop = 25;
+ let rotX = 0; // expected to be updated in interact()
+ let rotY = 0;
+ let pointer = [500, 450];
+ const radPerPixel = 0.005; // radians of subject rotation per pixel distance moved by mouse.
+
+ 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.02,
+ 'far' : 4,
+ 'angle': camAngle,
+ };
+
+ let lastImage = null;
+
+ const fontMgr = CanvasKit.SkFontMgr.FromData([robotoData]);
+
+ const paraStyle = new CanvasKit.ParagraphStyle({
+ textStyle: {
+ color: CanvasKit.Color(105, 56, 16), // brown
+ fontFamilies: ['Roboto'],
+ fontSize: 28,
+ },
+ textAlign: CanvasKit.TextAlign.Left,
+ });
+ const hStyle = CanvasKit.RectHeightStyle.Max;
+ const wStyle = CanvasKit.RectWidthStyle.Tight;
+
+ const quotes = [
+ 'Some activities superficially familiar to you are merely stupid and should be avoided for your safety, although they are not illegal as such. These include: giving your bank account details to the son of the Nigerian Minister of Finance; buying title to bridges, skyscrapers, spacecraft, planets, or other real assets; murder; selling your identity; and entering into financial contracts with entities running Economics 2.0 or higher.',
+ // Charles Stross - Accelerando
+ 'If only there were evil people somewhere insidiously committing evil deeds, and it were necessary only to separate them from the rest of us and destroy them. But the line dividing good and evil cuts through the heart of every human being. And who is willing to destroy a piece of his own heart?',
+ // Aleksandr Solzhenitsyn - The Gulag Archipelago
+ 'There is one metaphor of which the moderns are very fond; they are always saying, “You can’t put the clock back.” The simple and obvious answer is “You can.” A clock, being a piece of human construction, can be restored by the human finger to any figure or hour. In the same way society, being a piece of human construction, can be reconstructed upon any plan that has ever existed.',
+ // G. K. Chesterton - What's Wrong With The World?
+ ];
+
+ // pick one at random
+ const text = quotes[Math.floor(Math.random()*3)];
+ const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
+ builder.addText(text);
+ const paragraph = builder.build();
+ const font = new CanvasKit.SkFont(null, 18);
+ // wrap the text to a given width.
+ paragraph.layout(sizeX - margin*2);
+
+ // to rotate every glyph individually, calculate the bounding rect of each one,
+ // construct an array of rects and paragraphs that would draw each glyph individually.
+ const letters = Array(text.length);
+ for (let i = 0; i < text.length; i++) {
+ const r = paragraph.getRectsForRange(i, i+1, hStyle, wStyle)[0];
+ // The character is drawn with drawParagraph so we can pass the paraStyle,
+ // and have our character be the exact size and shape the paragraph expected
+ // when it wrapped the text. canvas.drawText wouldn't cut it.
+ const tmpbuilder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
+ tmpbuilder.addText(text[i]);
+ const para = tmpbuilder.build();
+ para.layout(100);
+ letters[i] = {
+ 'r': r,
+ 'para': para,
+ };
+ }
+
+ function drawFrame(canvas) {
+ // persistence of vision effect is done by drawing the past frame as an image,
+ // then covering with semitransparent background color.
+ if (lastImage) {
+ canvas.drawImage(lastImage, 0, 0, null);
+ canvas.drawColor(CanvasKit.Color(171, 244, 255, 0.1)); // sky blue, almost transparent
+ } else {
+ canvas.clear(CanvasKit.Color(171, 244, 255)); // sky blue, opaque
+ }
+ canvas.save();
+ // Set up 3D view enviroment
+ saveCamera(canvas, CanvasKit.LTRBRect(0, 0, sizeX, sizeY), halfDim, cam);
+
+ // Rotate the whole paragraph as a unit.
+ const paraRotPoint = [halfDim, halfDim, 1];
+ canvas.concat(CanvasKit.SkM44.multiply(
+ CanvasKit.SkM44.translated(paraRotPoint),
+ CanvasKit.SkM44.rotated([0,1,0], rotX),
+ CanvasKit.SkM44.rotated([1,0,0], rotY * 0.2),
+ CanvasKit.SkM44.translated(CanvasKit.SkVector.mulScalar(paraRotPoint, -1)),
+ ));
+
+ // Rotate every glyph in the paragraph individually.
+ let i = 0;
+ for (const letter of letters) {
+ canvas.save();
+ let r = letter['r'];
+ // rotate about the center of the glyph's rect.
+ rotationPoint = [
+ margin + r.fLeft + (r.fRight - r.fLeft) / 2,
+ marginTop + r.fTop + (r.fBottom - r.fTop) / 2,
+ 0
+ ];
+ distanceFromPointer = CanvasKit.SkVector.dist(pointer, rotationPoint.slice(0, 2));
+ // Rotate more around the Y-axis depending on the glyph's distance from the pointer.
+ canvas.concat(CanvasKit.SkM44.multiply(
+ CanvasKit.SkM44.translated(rotationPoint),
+ // note that I'm rotating around the x axis first, undoing some of the rotation done to the whole
+ // paragraph above, where x came second. If I rotated y first, a lot of letters would end up
+ // upside down, which is a bit too hard to unscramble.
+ CanvasKit.SkM44.rotated([1,0,0], rotY * -0.6),
+ CanvasKit.SkM44.rotated([0,1,0], distanceFromPointer * -0.035),
+ CanvasKit.SkM44.translated(CanvasKit.SkVector.mulScalar(rotationPoint, -1)),
+ ));
+ canvas.drawParagraph(letter['para'], margin + r.fLeft, marginTop + r.fTop);
+ i++;
+ canvas.restore();
+ }
+ canvas.restore();
+ lastImage = surface.makeImageSnapshot();
+ }
+
+ function interact(e) {
+ pointer = [e.offsetX, e.offsetY]
+ rotX = (pointer[0] - halfDim) * radPerPixel;
+ rotY = (pointer[1] - halfDim) * radPerPixel * -1;
+ surface.requestAnimationFrame(drawFrame);
+ };
+
+ document.getElementById('glyphgame').addEventListener('pointermove', interact);
+ surface.requestAnimationFrame(drawFrame);
+ }
+
+ function ColorSupport(CanvasKit) {
+ const surface = CanvasKit.MakeCanvasSurface('colorsupport', CanvasKit.SkColorSpace.ADOBE_RGB);
+ if (!surface) {
+ console.error('Could not make surface');
+ return;
+ }
+ const canvas = surface.getCanvas();
+
+ // If the surface is correctly initialized with a higher bit depth color type,
+ // And chrome is compositing it into a buffer with the P3 color space,
+ // then the inner round rect should be distinct and less saturated than the full red background.
+ // Even if the monitor it is viewed on cannot accurately represent that color space.
+
+ let red = CanvasKit.Color4f(1, 0, 0, 1);
+ let paint = new CanvasKit.SkPaint();
+ paint.setColor(red, CanvasKit.SkColorSpace.ADOBE_RGB);
+ canvas.drawPaint(paint);
+ paint.setColor(red, CanvasKit.SkColorSpace.DISPLAY_P3);
+ canvas.drawRoundRect(CanvasKit.LTRBRect(50, 50, 250, 250), 30, 30, paint);
+ paint.setColor(red, CanvasKit.SkColorSpace.SRGB);
+ canvas.drawRoundRect(CanvasKit.LTRBRect(100, 100, 200, 200), 30, 30, paint);
+
+ surface.flush();
+ surface.delete();
+ }
</script>
diff --git a/chromium/third_party/skia/modules/canvaskit/canvaskit/node.example.js b/chromium/third_party/skia/modules/canvaskit/canvaskit/node.example.js
index 04e7950c24c..557dc09ff7c 100644
--- a/chromium/third_party/skia/modules/canvaskit/canvaskit/node.example.js
+++ b/chromium/third_party/skia/modules/canvaskit/canvaskit/node.example.js
@@ -4,7 +4,7 @@ const path = require('path');
CanvasKitInit({
locateFile: (file) => __dirname + '/bin/'+file,
-}).ready().then((CanvasKit) => {
+}).then((CanvasKit) => {
let canvas = CanvasKit.MakeCanvas(300, 300);
let img = fs.readFileSync(path.join(__dirname, 'test.png'));
@@ -111,4 +111,4 @@ function starPath(CanvasKit, X=128, Y=128, R=116) {
p.lineTo(X + R * Math.cos(a), Y + R * Math.sin(a));
}
return p;
-} \ No newline at end of file
+}
diff --git a/chromium/third_party/skia/modules/canvaskit/canvaskit/package.json b/chromium/third_party/skia/modules/canvaskit/canvaskit/package.json
index d94c602495b..8577a172204 100644
--- a/chromium/third_party/skia/modules/canvaskit/canvaskit/package.json
+++ b/chromium/third_party/skia/modules/canvaskit/canvaskit/package.json
@@ -1,6 +1,6 @@
{
"name": "canvaskit-wasm",
- "version": "0.14.0",
+ "version": "0.16.2",
"description": "A WASM version of Skia's Canvas API",
"main": "bin/canvaskit.js",
"homepage": "https://github.com/google/skia/tree/master/modules/canvaskit",
diff --git a/chromium/third_party/skia/modules/canvaskit/canvaskit/viewer.html b/chromium/third_party/skia/modules/canvaskit/canvaskit/viewer.html
index f201f82a050..69c2a69a3a9 100644
--- a/chromium/third_party/skia/modules/canvaskit/canvaskit/viewer.html
+++ b/chromium/third_party/skia/modules/canvaskit/canvaskit/viewer.html
@@ -15,63 +15,200 @@
<script type="text/javascript" src="/node_modules/canvaskit/bin/canvaskit.js"></script>
<script type="text/javascript" charset="utf-8">
- var CanvasKit = null;
+ const flags = {};
+ for (pair of location.hash.substring(1).split(',')) {
+ // Parse "values" as an array in case the value has a colon (e.g., "slide:http://...").
+ const [key, ...values] = pair.split(':');
+ flags[key] = values.join(':');
+ }
+ window.onhashchange = function() {
+ location.reload();
+ };
+
CanvasKitInit({
locateFile: (file) => '/node_modules/canvaskit/bin/'+file,
- }).ready().then((CK) => {
- CanvasKit = CK;
- ViewerMain(CanvasKit);
+ }).then((CK) => {
+ if (!CK) {
+ throw 'CanvasKit not available.';
+ }
+ LoadSlide(CK);
});
- function ViewerMain(CanvasKit) {
- if (!CanvasKit) {
- console.error('CanvasKit not available.');
- return;
+ function LoadSlide(CanvasKit) {
+ if (!CanvasKit.MakeSlide || !CanvasKit.MakeSkpSlide || !CanvasKit.MakeSvgSlide) {
+ throw 'Not compiled with Viewer.';
}
- if (!CanvasKit.MakeSlide) {
- console.error('Not compiled with Viewer.');
- return;
+ const slideName = flags.slide || 'PathText';
+ if (slideName.endsWith('.skp') || slideName.endsWith('.svg')) {
+ fetch(slideName).then(function(response) {
+ if (response.status != 200) {
+ throw 'Error fetching ' + slideName;
+ }
+ if (slideName.endsWith('.skp')) {
+ response.arrayBuffer().then((data) => ViewerMain(
+ CanvasKit, CanvasKit.MakeSkpSlide(slideName, data)));
+ } else {
+ response.text().then((text) => ViewerMain(
+ CanvasKit, CanvasKit.MakeSvgSlide(slideName, text)));
+ }
+ });
+ } else {
+ ViewerMain(CanvasKit, CanvasKit.MakeSlide(slideName));
}
+ }
+ function ViewerMain(CanvasKit, slide) {
+ if (!slide) {
+ throw 'Failed to parse slide.'
+ }
+ const width = window.innerWidth;
+ const height = window.innerHeight;
const htmlCanvas = document.getElementById('viewer_canvas');
- htmlCanvas.width = window.innerWidth;
- htmlCanvas.height = window.innerHeight;
+ htmlCanvas.width = width;
+ htmlCanvas.height = height;
+ slide.load(width, height);
- const surface = CanvasKit.MakeCanvasSurface(htmlCanvas);
+ // For the msaa flag, only check if the key exists in flags. That way we don't need to assign it
+ // a value in the location hash. i.e.,: http://.../viewer.html#msaa
+ const doMSAA = ('msaa' in flags);
+ // Create the WebGL context with our desired attribs before calling MakeWebGLCanvasSurface.
+ CanvasKit.GetWebGLContext(htmlCanvas, {antialias: doMSAA});
+ const surface = CanvasKit.MakeWebGLCanvasSurface(htmlCanvas, null);
if (!surface) {
- console.error('Could not make surface');
- return;
+ throw 'Could not make canvas surface';
+ }
+ if (doMSAA && surface.sampleCnt() <= 1) {
+ // We requested antialias on the canvas but did not get MSAA. Since we don't know what type of
+ // AA is in use right now (if any), this surface is unusable.
+ throw 'MSAA rendering to the on-screen canvas is not supported. ' +
+ 'Please try again without MSAA.';
}
- const slideName = 'WavyPathText';
- const slide = CanvasKit.MakeSlide(slideName);
- if (!slide) {
- console.error('Could not make slide ' + slideName);
- return;
+ window.onmousedown = (event) => (event.button === 0) && Mouse(CanvasKit.InputState.Down, event);
+ window.onmouseup = (event) => (event.button === 0) && Mouse(CanvasKit.InputState.Up, event);
+ window.onmousemove = (event) => Mouse(CanvasKit.InputState.Move, event);
+ window.onkeypress = function(event) {
+ if (slide.onChar(event.keyCode)) {
+ ScheduleDraw();
+ return false;
+ } else {
+ switch (event.keyCode) {
+ case 's'.charCodeAt(0):
+ // 's' is the magic key in the native viewer app that turns on FPS monitoring. Toggle
+ // forced animation when it is pressed in order to get fps logs.
+ // HINT: Launch chrome with --disable-frame-rate-limit and --disable-gpu-vsync in order
+ // to measure frame rates above 60.
+ ScheduleDraw.forceAnimation = !ScheduleDraw.forceAnimation;
+ ScheduleDraw();
+ break;
+ }
+ }
+ return true;
+ }
+ window.onkeydown = function(event) {
+ const upArrowCode = 38;
+ if (event.keyCode === upArrowCode) {
+ ScaleCanvas((event.shiftKey) ? Infinity : 1.1);
+ return false;
+ }
+ const downArrowCode = 40;
+ if (event.keyCode === downArrowCode) {
+ ScaleCanvas((event.shiftKey) ? 0 : 1/1.1);
+ return false;
+ }
+ return true;
}
- slide.load(htmlCanvas.width, htmlCanvas.height);
+ let [canvasScale, canvasTranslateX, canvasTranslateY] = [1, 0, 0];
+ function ScaleCanvas(factor) {
+ factor = Math.min(Math.max(1/(5*canvasScale), factor), 5/canvasScale);
+ canvasTranslateX *= factor;
+ canvasTranslateY *= factor;
+ canvasScale *= factor;
+ ScheduleDraw();
+ }
+ function TranslateCanvas(dx, dy) {
+ canvasTranslateX += dx;
+ canvasTranslateY += dy;
+ ScheduleDraw();
+ }
- const fps = {
- frames: 0,
- startMs: window.performance.now()
- };
+ function Mouse(state, event) {
+ let modifierKeys = CanvasKit.ModifierKey.None;
+ if (event.shiftKey) {
+ modifierKeys |= CanvasKit.ModifierKey.Shift;
+ }
+ if (event.altKey) {
+ modifierKeys |= CanvasKit.ModifierKey.Option;
+ }
+ if (event.ctrlKey) {
+ modifierKeys |= CanvasKit.ModifierKey.Ctrl;
+ }
+ if (event.metaKey) {
+ modifierKeys |= CanvasKit.ModifierKey.Command;
+ }
+ let [dx, dy] = [event.pageX - this.lastX, event.pageY - this.lastY];
+ this.lastX = event.pageX;
+ this.lastY = event.pageY;
+ if (slide.onMouse(event.pageX, event.pageY, state, modifierKeys)) {
+ ScheduleDraw();
+ return false;
+ } else if (event.buttons & 1) { // Left-button pressed.
+ TranslateCanvas(dx, dy);
+ return false;
+ }
+ return true;
+ }
+
+ function ScheduleDraw() {
+ if (ScheduleDraw.hasPendingAnimationRequest) {
+ // It's possible for this ScheduleDraw() method to be called multiple times before an
+ // animation callback actually gets invoked. Make sure we only ever have one single
+ // requestAnimationFrame scheduled at a time, because otherwise we can get stuck in a
+ // position where multiple callbacks are coming in on a single compositing frame, and then
+ // rescheduling multiple more for the next frame.
+ return;
+ }
+ ScheduleDraw.hasPendingAnimationRequest = true;
+ surface.requestAnimationFrame((canvas) => {
+ ScheduleDraw.hasPendingAnimationRequest = false;
- surface.requestAnimationFrame(function(canvas) {
- slide.draw(canvas);
- ++fps.frames;
+ canvas.save();
+ canvas.translate(canvasTranslateX, canvasTranslateY);
+ canvas.scale(canvasScale, canvasScale);
+ canvas.clear(CanvasKit.WHITE);
+ slide.draw(canvas);
+ canvas.restore();
+ // HINT: Launch chrome with --disable-frame-rate-limit and --disable-gpu-vsync in order to
+ // allow this to go faster than 60fps.
+ const ms = (ScheduleDraw.fps && ScheduleDraw.fps.markFrameComplete()) ||
+ window.performance.now();
+ if (slide.animate(ms * 1e6) || ScheduleDraw.forceAnimation) {
+ ScheduleDraw.fps = ScheduleDraw.fps || new FPSMeter(ms);
+ ScheduleDraw();
+ } else {
+ delete ScheduleDraw.fps;
+ }
+ });
+ }
+
+ ScheduleDraw();
+ }
+
+ function FPSMeter(startMs) {
+ this.frames = 0;
+ this.startMs = startMs;
+ this.markFrameComplete = () => {
+ ++this.frames;
const ms = window.performance.now();
- const sec = (ms - fps.startMs) / 1000;
+ const sec = (ms - this.startMs) / 1000;
if (sec > 2) {
- console.log(Math.round(fps.frames / sec) + ' fps');
- fps.frames = 0;
- fps.startMs = ms;
+ console.log(Math.round(this.frames / sec) + ' fps');
+ this.frames = 0;
+ this.startMs = ms;
}
-
- if (slide.animate(ms * 1e6)) {
- surface.requestAnimationFrame(arguments.callee);
- }
- });
+ return ms;
+ };
}
</script>
diff --git a/chromium/third_party/skia/modules/canvaskit/canvaskit_bindings.cpp b/chromium/third_party/skia/modules/canvaskit/canvaskit_bindings.cpp
index 5823f0abadd..e9c195ee117 100644
--- a/chromium/third_party/skia/modules/canvaskit/canvaskit_bindings.cpp
+++ b/chromium/third_party/skia/modules/canvaskit/canvaskit_bindings.cpp
@@ -12,6 +12,7 @@
#include "include/core/SkCanvas.h"
#include "include/core/SkColor.h"
#include "include/core/SkColorFilter.h"
+#include "include/core/SkColorSpace.h"
#include "include/core/SkData.h"
#include "include/core/SkDrawable.h"
#include "include/core/SkEncodedImageFormat.h"
@@ -66,7 +67,7 @@
#include "include/gpu/gl/GrGLInterface.h"
#include "include/gpu/gl/GrGLTypes.h"
-#include <GL/gl.h>
+#include <GLES3/gl3.h>
#include <emscripten/html5.h>
#endif
@@ -99,7 +100,8 @@ struct OptionalMatrix : SkMatrix {
SkColor4f ptrToSkColor4f(uintptr_t /* float* */ cPtr) {
float* fourFloats = reinterpret_cast<float*>(cPtr);
- SkColor4f color = { fourFloats[0], fourFloats[1], fourFloats[2], fourFloats[3] };
+ SkColor4f color;
+ memcpy(&color, fourFloats, 4 * sizeof(float));
return color;
}
@@ -109,14 +111,31 @@ struct SimpleImageInfo {
int height;
SkColorType colorType;
SkAlphaType alphaType;
- // TODO color spaces?
+ sk_sp<SkColorSpace> colorSpace;
};
SkImageInfo toSkImageInfo(const SimpleImageInfo& sii) {
- return SkImageInfo::Make(sii.width, sii.height, sii.colorType, sii.alphaType);
+ return SkImageInfo::Make(sii.width, sii.height, sii.colorType, sii.alphaType, sii.colorSpace);
}
#ifdef SK_GL
+
+// Set the pixel format based on the colortype.
+// These degrees of freedom are removed from canvaskit only to keep the interface simpler.
+struct ColorSettings {
+ ColorSettings(sk_sp<SkColorSpace> colorSpace) {
+ if (colorSpace == nullptr || colorSpace->isSRGB()) {
+ colorType = kRGBA_8888_SkColorType;
+ pixFormat = GL_RGBA8;
+ } else {
+ colorType = kRGBA_F16_SkColorType;
+ pixFormat = GL_RGBA16F;
+ }
+ };
+ SkColorType colorType;
+ GrGLenum pixFormat;
+};
+
sk_sp<GrContext> MakeGrContext(EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context)
{
EMSCRIPTEN_RESULT r = emscripten_webgl_make_context_current(context);
@@ -131,31 +150,31 @@ sk_sp<GrContext> MakeGrContext(EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context)
return grContext;
}
-sk_sp<SkSurface> MakeOnScreenGLSurface(sk_sp<GrContext> grContext, int width, int height) {
+sk_sp<SkSurface> MakeOnScreenGLSurface(sk_sp<GrContext> grContext, int width, int height,
+ sk_sp<SkColorSpace> colorSpace) {
+ // WebGL should already be clearing the color and stencil buffers, but do it again here to
+ // ensure Skia receives them in the expected state.
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
glClearColor(0, 0, 0, 0);
glClearStencil(0);
glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+ grContext->resetContext(kRenderTarget_GrGLBackendState | kMisc_GrGLBackendState);
-
- // Wrap the frame buffer object attached to the screen in a Skia render
- // target so Skia can render to it
- GrGLint buffer;
- glGetIntegerv(GL_FRAMEBUFFER_BINDING, &buffer);
+ // The on-screen canvas is FBO 0. Wrap it in a Skia render target so Skia can render to it.
GrGLFramebufferInfo info;
- info.fFBOID = (GrGLuint) buffer;
- SkColorType colorType;
+ info.fFBOID = 0;
+
+ GrGLint sampleCnt;
+ glGetIntegerv(GL_SAMPLES, &sampleCnt);
GrGLint stencil;
glGetIntegerv(GL_STENCIL_BITS, &stencil);
- info.fFormat = GL_RGBA8;
- colorType = kRGBA_8888_SkColorType;
-
- GrBackendRenderTarget target(width, height, 0, stencil, info);
-
+ const auto colorSettings = ColorSettings(colorSpace);
+ info.fFormat = colorSettings.pixFormat;
+ GrBackendRenderTarget target(width, height, sampleCnt, stencil, info);
sk_sp<SkSurface> surface(SkSurface::MakeFromBackendRenderTarget(grContext.get(), target,
- kBottomLeft_GrSurfaceOrigin,
- colorType, nullptr, nullptr));
+ kBottomLeft_GrSurfaceOrigin, colorSettings.colorType, colorSpace, nullptr));
return surface;
}
@@ -469,6 +488,76 @@ SkPathOrNull MakePathFromCmds(uintptr_t /* float* */ cptr, int numCmds) {
return emscripten::val(path);
}
+void PathAddVerbsPointsWeights(SkPath& path, uintptr_t /* uint8_t* */ verbsPtr, int numVerbs,
+ uintptr_t /* float* */ ptsPtr, int numPts,
+ uintptr_t /* float* */ wtsPtr, int numWts) {
+ const uint8_t* verbs = reinterpret_cast<const uint8_t*>(verbsPtr);
+ const float* pts = reinterpret_cast<const float*>(ptsPtr);
+ const float* weights = reinterpret_cast<const float*>(wtsPtr);
+
+ #define CHECK_NUM_POINTS(n) \
+ if ((ptIdx + n) > numPts) { \
+ SkDebugf("Not enough points to match the verbs. Saw %d points\n", numPts); \
+ return; \
+ }
+ #define CHECK_NUM_WEIGHTS(n) \
+ if ((wtIdx + n) > numWts) { \
+ SkDebugf("Not enough weights to match the verbs. Saw %d weights\n", numWts); \
+ return; \
+ }
+
+ path.incReserve(numPts);
+ int ptIdx = 0;
+ int wtIdx = 0;
+ for (int v = 0; v < numVerbs; ++v) {
+ switch (verbs[v]) {
+ case MOVE:
+ CHECK_NUM_POINTS(2);
+ path.moveTo(pts[ptIdx], pts[ptIdx+1]);
+ ptIdx += 2;
+ break;
+ case LINE:
+ CHECK_NUM_POINTS(2);
+ path.lineTo(pts[ptIdx], pts[ptIdx+1]);
+ ptIdx += 2;
+ break;
+ case QUAD:
+ CHECK_NUM_POINTS(4);
+ path.quadTo(pts[ptIdx], pts[ptIdx+1], pts[ptIdx+2], pts[ptIdx+3]);
+ ptIdx += 4;
+ break;
+ case CONIC:
+ CHECK_NUM_POINTS(4);
+ CHECK_NUM_WEIGHTS(1);
+ path.conicTo(pts[ptIdx], pts[ptIdx+1], pts[ptIdx+2], pts[ptIdx+3],
+ weights[wtIdx]);
+ ptIdx += 4;
+ wtIdx++;
+ break;
+ case CUBIC:
+ CHECK_NUM_POINTS(6);
+ path.cubicTo(pts[ptIdx ], pts[ptIdx+1],
+ pts[ptIdx+2], pts[ptIdx+3],
+ pts[ptIdx+4], pts[ptIdx+5]);
+ ptIdx += 6;
+ break;
+ case CLOSE:
+ path.close();
+ break;
+ }
+ }
+ #undef CHECK_NUM_POINTS
+ #undef CHECK_NUM_WEIGHTS
+}
+
+SkPath MakePathFromVerbsPointsWeights(uintptr_t /* uint8_t* */ verbsPtr, int numVerbs,
+ uintptr_t ptsPtr, int numPts,
+ uintptr_t wtsPtr, int numWts) {
+ SkPath path;
+ PathAddVerbsPointsWeights(path, verbsPtr, numVerbs, ptsPtr, numPts, wtsPtr, numWts);
+ return path;
+}
+
//========================================================================================
// Path Effects
//========================================================================================
@@ -740,7 +829,6 @@ EMSCRIPTEN_BINDINGS(Skia) {
// Adds a little helper because emscripten doesn't expose default params.
return SkMaskFilter::MakeBlur(style, sigma, respectCTM);
}), allow_raw_pointers());
- function("_MakePathFromCmds", &MakePathFromCmds);
#ifdef SK_INCLUDE_PATHOPS
function("MakePathFromOp", &MakePathFromOp);
#endif
@@ -757,18 +845,33 @@ EMSCRIPTEN_BINDINGS(Skia) {
return SkImage::MakeRasterData(info, pixelData, rowBytes);
}), allow_raw_pointers());
+
+ // Here and in other gradient functions, cPtr is a pointer to an array of data
+ // representing colors. whether this is an array of SkColor or SkColor4f is indicated
+ // by the colorType argument. Only RGBA_8888 and RGBA_F32 are accepted.
function("_MakeLinearGradientShader", optional_override([](SkPoint start, SkPoint end,
- uintptr_t /* SkColor4f* */ cPtr, uintptr_t /* SkScalar* */ pPtr,
+ uintptr_t cPtr, SkColorType colorType,
+ uintptr_t /* SkScalar* */ pPtr,
int count, SkTileMode mode, uint32_t flags,
- uintptr_t /* SkScalar* */ mPtr)->sk_sp<SkShader> {
+ uintptr_t /* SkScalar* */ mPtr,
+ sk_sp<SkColorSpace> colorSpace)->sk_sp<SkShader> {
SkPoint points[] = { start, end };
// See comment above for uintptr_t explanation
- const SkColor4f* colors = reinterpret_cast<const SkColor4f*>(cPtr);
const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
OptionalMatrix localMatrix(mPtr);
- // TODO(nifong): do not assume color space. Support and test wide gamut color gradients
- return SkGradientShader::MakeLinear(points, colors, SkColorSpace::MakeSRGB(), positions, count,
+
+ if (colorType == SkColorType::kRGBA_F32_SkColorType) {
+ const SkColor4f* colors = reinterpret_cast<const SkColor4f*>(cPtr);
+ return SkGradientShader::MakeLinear(points, colors, colorSpace, positions, count,
+ mode, flags, &localMatrix);
+ } else if (colorType == SkColorType::kRGBA_8888_SkColorType) {
+ const SkColor* colors = reinterpret_cast<const SkColor*>(cPtr);
+ return SkGradientShader::MakeLinear(points, colors, positions, count,
mode, flags, &localMatrix);
+ } else {
+ SkDebugf("%d is not an accepted colorType\n", colorType);
+ return nullptr;
+ }
}), allow_raw_pointers());
#ifdef SK_SERIALIZE_SKP
function("_MakeSkPicture", optional_override([](uintptr_t /* unint8_t* */ dPtr,
@@ -781,43 +884,79 @@ EMSCRIPTEN_BINDINGS(Skia) {
}), allow_raw_pointers());
#endif
function("_MakeRadialGradientShader", optional_override([](SkPoint center, SkScalar radius,
- uintptr_t /* SkColor4f* */ cPtr, uintptr_t /* SkScalar* */ pPtr,
+ uintptr_t cPtr, SkColorType colorType,
+ uintptr_t /* SkScalar* */ pPtr,
int count, SkTileMode mode, uint32_t flags,
- uintptr_t /* SkScalar* */ mPtr)->sk_sp<SkShader> {
+ uintptr_t /* SkScalar* */ mPtr,
+ sk_sp<SkColorSpace> colorSpace)->sk_sp<SkShader> {
// See comment above for uintptr_t explanation
- const SkColor4f* colors = reinterpret_cast<const SkColor4f*>(cPtr);
const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
OptionalMatrix localMatrix(mPtr);
- return SkGradientShader::MakeRadial(center, radius, colors, SkColorSpace::MakeSRGB(), positions, count,
- mode, flags, &localMatrix);
+ if (colorType == SkColorType::kRGBA_F32_SkColorType) {
+ const SkColor4f* colors = reinterpret_cast<const SkColor4f*>(cPtr);
+ return SkGradientShader::MakeRadial(center, radius, colors, colorSpace, positions, count,
+ mode, flags, &localMatrix);
+ } else if (colorType == SkColorType::kRGBA_8888_SkColorType) {
+ const SkColor* colors = reinterpret_cast<const SkColor*>(cPtr);
+ return SkGradientShader::MakeRadial(center, radius, colors, positions, count,
+ mode, flags, &localMatrix);
+ } else {
+ SkDebugf("%d is not an accepted colorType\n", colorType);
+ return nullptr;
+ }
}), allow_raw_pointers());
function("_MakeSweepGradientShader", optional_override([](SkScalar cx, SkScalar cy,
- uintptr_t /* SkColor4f* */ cPtr, uintptr_t /* SkScalar* */ pPtr,
+ uintptr_t cPtr, SkColorType colorType,
+ uintptr_t /* SkScalar* */ pPtr,
int count, SkTileMode mode,
SkScalar startAngle, SkScalar endAngle,
uint32_t flags,
- uintptr_t /* SkScalar* */ mPtr)->sk_sp<SkShader> {
+ uintptr_t /* SkScalar* */ mPtr,
+ sk_sp<SkColorSpace> colorSpace)->sk_sp<SkShader> {
// See comment above for uintptr_t explanation
- const SkColor4f* colors = reinterpret_cast<const SkColor4f*>(cPtr);
const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
OptionalMatrix localMatrix(mPtr);
- return SkGradientShader::MakeSweep(cx, cy, colors, SkColorSpace::MakeSRGB(), positions, count,
- mode, startAngle, endAngle, flags,
- &localMatrix);
+ if (colorType == SkColorType::kRGBA_F32_SkColorType) {
+ const SkColor4f* colors = reinterpret_cast<const SkColor4f*>(cPtr);
+ return SkGradientShader::MakeSweep(cx, cy, colors, colorSpace, positions, count,
+ mode, startAngle, endAngle, flags,
+ &localMatrix);
+ } else if (colorType == SkColorType::kRGBA_8888_SkColorType) {
+ const SkColor* colors = reinterpret_cast<const SkColor*>(cPtr);
+ return SkGradientShader::MakeSweep(cx, cy, colors, positions, count,
+ mode, startAngle, endAngle, flags,
+ &localMatrix);
+ } else {
+ SkDebugf("%d is not an accepted colorType\n", colorType);
+ return nullptr;
+ }
}), allow_raw_pointers());
function("_MakeTwoPointConicalGradientShader", optional_override([](
SkPoint start, SkScalar startRadius,
SkPoint end, SkScalar endRadius,
- uintptr_t /* SkColor4f* */ cPtr, uintptr_t /* SkScalar* */ pPtr,
+ uintptr_t cPtr, SkColorType colorType,
+ uintptr_t /* SkScalar* */ pPtr,
int count, SkTileMode mode, uint32_t flags,
- uintptr_t /* SkScalar* */ mPtr)->sk_sp<SkShader> {
+ uintptr_t /* SkScalar* */ mPtr,
+ sk_sp<SkColorSpace> colorSpace)->sk_sp<SkShader> {
// See comment above for uintptr_t explanation
- const SkColor4f* colors = reinterpret_cast<const SkColor4f*> (cPtr);
const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
OptionalMatrix localMatrix(mPtr);
- return SkGradientShader::MakeTwoPointConical(start, startRadius, end, endRadius,
- colors, SkColorSpace::MakeSRGB(), positions, count, mode,
- flags, &localMatrix);
+
+ if (colorType == SkColorType::kRGBA_F32_SkColorType) {
+ const SkColor4f* colors = reinterpret_cast<const SkColor4f*>(cPtr);
+ return SkGradientShader::MakeTwoPointConical(start, startRadius, end, endRadius,
+ colors, colorSpace, positions, count, mode,
+ flags, &localMatrix);
+ } else if (colorType == SkColorType::kRGBA_8888_SkColorType) {
+ const SkColor* colors = reinterpret_cast<const SkColor*>(cPtr);
+ return SkGradientShader::MakeTwoPointConical(start, startRadius, end, endRadius,
+ colors, positions, count, mode,
+ flags, &localMatrix);
+ } else {
+ SkDebugf("%d is not an accepted colorType\n", colorType);
+ return nullptr;
+ }
}), allow_raw_pointers());
#ifdef SK_GL
@@ -861,7 +1000,7 @@ EMSCRIPTEN_BINDINGS(Skia) {
class_<SkCanvas>("SkCanvas")
.constructor<>()
.function("_clear", optional_override([](SkCanvas& self, uintptr_t /* float* */ cPtr) {
- self.clear(ptrToSkColor4f(cPtr).toSkColor());
+ self.clear(ptrToSkColor4f(cPtr));
}))
.function("clipPath", select_overload<void (const SkPath&, SkClipOp, bool)>(&SkCanvas::clipPath))
.function("clipRRect", optional_override([](SkCanvas& self, const SimpleRRect& r, SkClipOp op, bool doAntiAlias) {
@@ -876,9 +1015,7 @@ EMSCRIPTEN_BINDINGS(Skia) {
self.concat(m);
}))
.function("drawArc", &SkCanvas::drawArc)
- // _drawAtlas takes an SkColor, unlike most private functions handling color.
- // This is because it takes an array of colors. Converting it on the Javascript side allows
- // an allocation to be avoided here.
+ // _drawAtlas takes an array of SkColor. There is no SkColor4f override.
.function("_drawAtlas", optional_override([](SkCanvas& self,
const sk_sp<SkImage>& atlas, uintptr_t /* SkRSXform* */ xptr,
uintptr_t /* SkRect* */ rptr, uintptr_t /* SkColor* */ cptr, int count,
@@ -894,10 +1031,16 @@ EMSCRIPTEN_BINDINGS(Skia) {
}), allow_raw_pointers())
.function("drawCircle", select_overload<void (SkScalar, SkScalar, SkScalar, const SkPaint& paint)>(&SkCanvas::drawCircle))
.function("_drawColor", optional_override([](SkCanvas& self, uintptr_t /* float* */ cPtr) {
- self.drawColor(ptrToSkColor4f(cPtr).toSkColor());
+ self.drawColor(ptrToSkColor4f(cPtr));
}))
.function("_drawColor", optional_override([](SkCanvas& self, uintptr_t /* float* */ cPtr, SkBlendMode mode) {
- self.drawColor(ptrToSkColor4f(cPtr).toSkColor(), mode);
+ self.drawColor(ptrToSkColor4f(cPtr), mode);
+ }))
+ .function("drawColorInt", optional_override([](SkCanvas& self, SkColor color) {
+ self.drawColor(color);
+ }))
+ .function("drawColorInt", optional_override([](SkCanvas& self, SkColor color, SkBlendMode mode) {
+ self.drawColor(color, mode);
}))
.function("drawDRRect",optional_override([](SkCanvas& self, const SimpleRRect& o, const SimpleRRect& i, const SkPaint& paint) {
self.drawDRRect(toRRect(o), toRRect(i), paint);
@@ -1257,8 +1400,16 @@ EMSCRIPTEN_BINDINGS(Skia) {
.function("setAntiAlias", &SkPaint::setAntiAlias)
.function("setAlphaf", &SkPaint::setAlphaf)
.function("setBlendMode", &SkPaint::setBlendMode)
- .function("_setColor", optional_override([](SkPaint& self, uintptr_t /* float* */ cPtr) {
- self.setColor(ptrToSkColor4f(cPtr));
+ .function("_setColor", optional_override([](SkPaint& self, uintptr_t /* float* */ cPtr,
+ sk_sp<SkColorSpace> colorSpace) {
+ self.setColor(ptrToSkColor4f(cPtr), colorSpace.get());
+ }))
+ .function("setColorInt", optional_override([](SkPaint& self, SkColor color) {
+ self.setColor(SkColor4f::FromColor(color), nullptr);
+ }))
+ .function("setColorInt", optional_override([](SkPaint& self, SkColor color,
+ sk_sp<SkColorSpace> colorSpace) {
+ self.setColor(SkColor4f::FromColor(color), colorSpace.get());
}))
.function("setColorFilter", &SkPaint::setColorFilter)
.function("setFilterQuality", &SkPaint::setFilterQuality)
@@ -1272,6 +1423,21 @@ EMSCRIPTEN_BINDINGS(Skia) {
.function("setStrokeWidth", &SkPaint::setStrokeWidth)
.function("setStyle", &SkPaint::setStyle);
+ class_<SkColorSpace>("SkColorSpace")
+ .smart_ptr<sk_sp<SkColorSpace>>("sk_sp<SkColorSpace>")
+ .class_function("Equals", optional_override([](sk_sp<SkColorSpace> a, sk_sp<SkColorSpace> b)->bool {
+ return SkColorSpace::Equals(a.get(), b.get());
+ }))
+ // These are private because they are to be called once in interface.js to
+ // avoid clients having to delete the returned objects.
+ .class_function("_MakeSRGB", &SkColorSpace::MakeSRGB)
+ .class_function("_MakeDisplayP3", optional_override([]()->sk_sp<SkColorSpace> {
+ return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDisplayP3);
+ }))
+ .class_function("_MakeAdobeRGB", optional_override([]()->sk_sp<SkColorSpace> {
+ return SkColorSpace::MakeRGB(SkNamedTransferFn::k2Dot2, SkNamedGamut::kAdobeRGB);
+ }));
+
class_<SkPathEffect>("SkPathEffect")
.smart_ptr<sk_sp<SkPathEffect>>("sk_sp<SkPathEffect>")
.class_function("MakeCorner", &SkCornerPathEffect::Make)
@@ -1286,6 +1452,8 @@ EMSCRIPTEN_BINDINGS(Skia) {
class_<SkPath>("SkPath")
.constructor<>()
.constructor<const SkPath&>()
+ .class_function("_MakeFromCmds", &MakePathFromCmds)
+ .class_function("_MakeFromVerbsPointsWeights", &MakePathFromVerbsPointsWeights)
.function("_addArc", &ApplyAddArc)
// interface.js has 3 overloads of addPath
.function("_addOval", &ApplyAddOval)
@@ -1301,6 +1469,7 @@ EMSCRIPTEN_BINDINGS(Skia) {
.function("_addRect", &ApplyAddRect)
// interface.js has 4 overloads of addRoundRect
.function("_addRoundRect", &ApplyAddRoundRect)
+ .function("_addVerbsPointsWeights", &PathAddVerbsPointsWeights)
.function("_arcTo", &ApplyArcTo)
.function("_arcTo", &ApplyArcToAngle)
.function("_arcTo", &ApplyArcToArcSize)
@@ -1405,8 +1574,8 @@ EMSCRIPTEN_BINDINGS(Skia) {
.smart_ptr<sk_sp<SkShader>>("sk_sp<SkShader>")
.class_function("Blend", select_overload<sk_sp<SkShader>(SkBlendMode, sk_sp<SkShader>, sk_sp<SkShader>)>(&SkShaders::Blend))
.class_function("_Color",
- optional_override([](uintptr_t /* float* */ cPtr)->sk_sp<SkShader> {
- return SkShaders::Color(ptrToSkColor4f(cPtr), SkColorSpace::MakeSRGB());
+ optional_override([](uintptr_t /* float* */ cPtr, sk_sp<SkColorSpace> colorSpace)->sk_sp<SkShader> {
+ return SkShaders::Color(ptrToSkColor4f(cPtr), colorSpace);
})
)
.class_function("Lerp", select_overload<sk_sp<SkShader>(float, sk_sp<SkShader>, sk_sp<SkShader>)>(&SkShaders::Lerp));
@@ -1455,18 +1624,32 @@ EMSCRIPTEN_BINDINGS(Skia) {
class_<SkSurface>("SkSurface")
.smart_ptr<sk_sp<SkSurface>>("sk_sp<SkSurface>")
- .function("_flush", select_overload<void()>(&SkSurface::flush))
+ .function("_flush", select_overload<void()>(&SkSurface::flushAndSubmit))
.function("getCanvas", &SkSurface::getCanvas, allow_raw_pointers())
+ .function("imageInfo", optional_override([](SkSurface& self)->SimpleImageInfo {
+ const auto& ii = self.imageInfo();
+ return {ii.width(), ii.height(), ii.colorType(), ii.alphaType(), ii.refColorSpace()};
+ }))
.function("height", &SkSurface::height)
.function("makeImageSnapshot", select_overload<sk_sp<SkImage>()>(&SkSurface::makeImageSnapshot))
.function("makeImageSnapshot", select_overload<sk_sp<SkImage>(const SkIRect& bounds)>(&SkSurface::makeImageSnapshot))
.function("makeSurface", optional_override([](SkSurface& self, SimpleImageInfo sii)->sk_sp<SkSurface> {
return self.makeSurface(toSkImageInfo(sii));
}), allow_raw_pointers())
- .function("width", &SkSurface::width)
+#ifdef SK_GL
.function("reportBackendType", optional_override([](SkSurface& self)->std::string {
return self.getCanvas()->getGrContext() == nullptr ? "CPU" : "GPU";
- }));
+ }))
+ .function("sampleCnt", optional_override([](SkSurface& self)->int {
+ auto backendRT = self.getBackendRenderTarget(SkSurface::kFlushRead_BackendHandleAccess);
+ return (backendRT.isValid()) ? backendRT.sampleCnt() : 0;
+ }))
+#else
+ .function("reportBackendType", optional_override([](SkSurface& self)->std::string {
+ return "CPU";
+ }))
+#endif
+ .function("width", &SkSurface::width);
#ifndef SK_NO_FONTS
class_<SkTextBlob>("SkTextBlob")
@@ -1654,7 +1837,6 @@ EMSCRIPTEN_BINDINGS(Skia) {
.value("TrianglesStrip", SkVertices::VertexMode::kTriangleStrip_VertexMode)
.value("TriangleFan", SkVertices::VertexMode::kTriangleFan_VertexMode);
-
// A value object is much simpler than a class - it is returned as a JS
// object and does not require delete().
// https://emscripten.org/docs/porting/connecting_cpp_and_javascript/embind.html#value-types
@@ -1690,10 +1872,11 @@ EMSCRIPTEN_BINDINGS(Skia) {
.field("fBottom", &SkIRect::fBottom);
value_object<SimpleImageInfo>("SkImageInfo")
- .field("width", &SimpleImageInfo::width)
- .field("height", &SimpleImageInfo::height)
- .field("colorType", &SimpleImageInfo::colorType)
- .field("alphaType", &SimpleImageInfo::alphaType);
+ .field("width", &SimpleImageInfo::width)
+ .field("height", &SimpleImageInfo::height)
+ .field("colorType", &SimpleImageInfo::colorType)
+ .field("alphaType", &SimpleImageInfo::alphaType)
+ .field("colorSpace", &SimpleImageInfo::colorSpace);
// SkPoints can be represented by [x, y]
value_array<SkPoint>("SkPoint")
diff --git a/chromium/third_party/skia/modules/canvaskit/compile.sh b/chromium/third_party/skia/modules/canvaskit/compile.sh
index 93734dc3fc0..bc880df3881 100755
--- a/chromium/third_party/skia/modules/canvaskit/compile.sh
+++ b/chromium/third_party/skia/modules/canvaskit/compile.sh
@@ -72,8 +72,6 @@ SKP_JS="--pre-js $BASE_DIR/skp.js"
GN_SKP_FLAGS=""
WASM_SKP="-DSK_SERIALIZE_SKP"
if [[ $@ == *no_skp* ]]; then
- GN_SKP_FLAGS="\"-DSK_DISABLE_READBUFFER\","
- WASM_SKP="-DSK_DISABLE_READBUFFER"
SKP_JS=""
fi
@@ -90,11 +88,13 @@ if [[ $@ == *no_skottie* ]]; then
SKOTTIE_BINDINGS=""
fi
+GN_VIEWER="skia_use_expat=false skia_enable_ccpr=false"
VIEWER_BINDINGS=""
VIEWER_LIB=""
if [[ $@ == *viewer* ]]; then
echo "Including viewer"
+ GN_VIEWER="skia_use_expat=true skia_enable_ccpr=true"
VIEWER_BINDINGS="$BASE_DIR/viewer_bindings.cpp"
VIEWER_LIB="$BUILD_DIR/libviewer_wasm.a"
IS_OFFICIAL_BUILD="false"
@@ -156,20 +156,18 @@ if [[ $@ == *no_canvas* ]]; then
HTML_CANVAS_API=""
fi
-GN_FONT="skia_enable_fontmgr_empty=false skia_enable_fontmgr_custom_empty=false"
+GN_FONT="skia_enable_fontmgr_custom_directory=false "
FONT_CFLAGS=""
-BUILTIN_FONT="$BASE_DIR/fonts/NotoMono-Regular.ttf.cpp"
+BUILTIN_FONT=""
FONT_JS="--pre-js $BASE_DIR/font.js"
if [[ $@ == *no_font* ]]; then
echo "Omitting the built-in font(s), font manager and all code dealing with fonts"
- BUILTIN_FONT=""
FONT_CFLAGS="-DSK_NO_FONTS"
FONT_JS=""
- GN_FONT="skia_enable_fontmgr_empty=true skia_enable_fontmgr_custom_empty=false"
+ GN_FONT+="skia_enable_fontmgr_custom_embedded=false skia_enable_fontmgr_custom_empty=false"
elif [[ $@ == *no_embedded_font* ]]; then
echo "Omitting the built-in font(s)"
- BUILTIN_FONT=""
- GN_FONT="skia_enable_fontmgr_empty=false skia_enable_fontmgr_custom_empty=true"
+ GN_FONT+="skia_enable_fontmgr_custom_embedded=false skia_enable_fontmgr_custom_empty=true"
else
# Generate the font's binary file (which is covered by .gitignore)
python tools/embed_resources.py \
@@ -177,10 +175,12 @@ else
--input $BASE_DIR/fonts/NotoMono-Regular.ttf \
--output $BASE_DIR/fonts/NotoMono-Regular.ttf.cpp \
--align 4
+ BUILTIN_FONT="$BASE_DIR/fonts/NotoMono-Regular.ttf.cpp"
+ GN_FONT+="skia_enable_fontmgr_custom_embedded=true skia_enable_fontmgr_custom_empty=false"
fi
if [[ $@ == *no_alias_font* ]]; then
-EXTRA_CFLAGS+=" -DCANVASKIT_NO_ALIAS_FONT"
+EXTRA_CFLAGS+=" \"-DCANVASKIT_NO_ALIAS_FONT\""
FONT_CFLAGS+=" -DCANVASKIT_NO_ALIAS_FONT"
fi
@@ -268,7 +268,6 @@ echo "Compiling bitcode"
skia_use_angle=false \
skia_use_dng_sdk=false \
skia_use_egl=true \
- skia_use_expat=false \
skia_use_fontconfig=false \
skia_use_freetype=true \
skia_use_libheif=false \
@@ -293,9 +292,9 @@ echo "Compiling bitcode"
${GN_GPU} \
${GN_FONT} \
${GN_PARTICLES} \
+ ${GN_VIEWER} \
\
skia_enable_skshaper=true \
- skia_enable_ccpr=false \
skia_enable_nvpr=false \
skia_enable_skparagraph=true \
skia_enable_pdf=false"
@@ -331,6 +330,7 @@ ${EMCXX} \
$FONT_CFLAGS \
-std=c++17 \
--bind \
+ --no-entry \
--pre-js $BASE_DIR/preamble.js \
--pre-js $BASE_DIR/helper.js \
--pre-js $BASE_DIR/interface.js \
@@ -343,7 +343,6 @@ ${EMCXX} \
$RT_SHADER_JS \
$HTML_CANVAS_API \
--pre-js $BASE_DIR/postamble.js \
- --post-js $BASE_DIR/ready.js \
$BASE_DIR/canvaskit_bindings.cpp \
$PARTICLES_BINDINGS \
$SKOTTIE_BINDINGS \
@@ -365,7 +364,7 @@ ${EMCXX} \
-s MODULARIZE=1 \
-s NO_EXIT_RUNTIME=1 \
-s STRICT=1 \
- -s TOTAL_MEMORY=128MB \
+ -s INITIAL_MEMORY=128MB \
-s WARN_UNALIGNED=1 \
-s WASM=1 \
-o $BUILD_DIR/canvaskit.js
diff --git a/chromium/third_party/skia/modules/canvaskit/cpu.js b/chromium/third_party/skia/modules/canvaskit/cpu.js
index d2e543cd552..229a6c7355a 100644
--- a/chromium/third_party/skia/modules/canvaskit/cpu.js
+++ b/chromium/third_party/skia/modules/canvaskit/cpu.js
@@ -1,5 +1,7 @@
// Adds compile-time JS functions to augment the CanvasKit interface.
-// Specifically, anything that should only be on the CPU version of canvaskit.
+// Implementations in this file are considerate of GPU builds, i.e. some
+// behavior is predicated on whether or not this is being compiled alongside
+// gpu.js.
(function(CanvasKit){
CanvasKit._extraInitializations = CanvasKit._extraInitializations || [];
CanvasKit._extraInitializations.push(function() {
@@ -7,6 +9,7 @@
CanvasKit.MakeSWCanvasSurface = function(idOrElement) {
var canvas = idOrElement;
if (canvas.tagName !== 'CANVAS') {
+ // TODO(nifong): unit test
canvas = document.getElementById(idOrElement);
if (!canvas) {
throw 'Canvas with id ' + idOrElement + ' was not found';
@@ -26,6 +29,10 @@
CanvasKit.MakeCanvasSurface = CanvasKit.MakeSWCanvasSurface;
}
+ // Note that color spaces are currently not supported in CPU surfaces. due to the limitation
+ // canvas.getContext('2d').putImageData imposes a limitatin of using an RGBA_8888 color type.
+ // TODO(nifong): support WGC color spaces while still using an RGBA_8888 color type when
+ // on a cpu backend.
CanvasKit.MakeSurface = function(width, height) {
/* @dict */
var imageInfo = {
@@ -35,6 +42,7 @@
// Since we are sending these pixels directly into the HTML canvas,
// (and those pixels are un-premultiplied, i.e. straight r,g,b,a)
'alphaType': CanvasKit.AlphaType.Unpremul,
+ 'colorSpace': CanvasKit.SkColorSpace.SRGB,
}
var pixelLen = width * height * 4; // it's 8888, so 4 bytes per pixel
// Allocate the buffer of pixels to be drawn into.
@@ -55,7 +63,9 @@
return surface;
};
- CanvasKit.SkSurface.prototype.flush = function() {
+ // For GPU builds, simply proxies to native code flush. For CPU builds,
+ // also updates the underlying HTML canvas, optionally with dirtyRect.
+ CanvasKit.SkSurface.prototype.flush = function(dirtyRect) {
this._flush();
// Do we have an HTML canvas to write the pixels to?
// We will not if this a GPU build or a raster surface, for example.
@@ -63,7 +73,14 @@
var pixels = new Uint8ClampedArray(CanvasKit.HEAPU8.buffer, this._pixelPtr, this._pixelLen);
var imageData = new ImageData(pixels, this._width, this._height);
- this._canvas.getContext('2d').putImageData(imageData, 0, 0);
+ if (!dirtyRect) {
+ this._canvas.getContext('2d').putImageData(imageData, 0, 0);
+ } else {
+ this._canvas.getContext('2d').putImageData(imageData, 0, 0,
+ dirtyRect.fLeft, dirtyRect.fTop,
+ dirtyRect.fRight - dirtyRect.fLeft,
+ dirtyRect.fBottom - dirtyRect.fTop);
+ }
}
};
diff --git a/chromium/third_party/skia/modules/canvaskit/externs.js b/chromium/third_party/skia/modules/canvaskit/externs.js
index 7b62777927c..81a3778a707 100644
--- a/chromium/third_party/skia/modules/canvaskit/externs.js
+++ b/chromium/third_party/skia/modules/canvaskit/externs.js
@@ -26,6 +26,7 @@ var CanvasKit = {
// public API (i.e. things we declare in the pre-js file or in the cpp bindings)
Color: function() {},
Color4f: function() {},
+ ColorAsInt: function() {},
/** @return {CanvasKit.SkRect} */
LTRBRect: function() {},
/** @return {CanvasKit.SkRect} */
@@ -46,6 +47,7 @@ var CanvasKit = {
MakeImage: function() {},
/** @return {CanvasKit.SkImage} */
MakeImageFromEncoded: function() {},
+ MakeImageFromCanvasImageSource: function() {},
MakeOnScreenGLSurface: function() {},
MakePathFromCmds: function() {},
MakePathFromOp: function() {},
@@ -60,6 +62,7 @@ var CanvasKit = {
MakeWebGLCanvasSurface: function() {},
/** @return {TypedArray} */
Malloc: function() {},
+ Free: function() {},
computeTonalColors: function() {},
currentContext: function() {},
getColorComponents: function() {},
@@ -71,12 +74,14 @@ var CanvasKit = {
setCurrentContext: function() {},
setDecodeCacheLimitBytes: function() {},
+ // Defined by emscripten.
+ createContext:function() {},
+
// private API (i.e. things declared in the bindings that we use
// in the pre-js file)
_computeTonalColors: function() {},
_MakeImage: function() {},
_MakeLinearGradientShader: function() {},
- _MakePathFromCmds: function() {},
_MakeRadialGradientShader: function() {},
_MakeSweepGradientShader: function() {},
_MakeManagedAnimation: function() {},
@@ -181,6 +186,7 @@ var CanvasKit = {
drawAnimatedImage: function() {},
drawArc: function() {},
drawCircle: function() {},
+ drawColorInt: function() {},
drawDRRect: function() {},
drawImage: function() {},
drawImageNine: function() {},
@@ -214,6 +220,7 @@ var CanvasKit = {
prototype: {
clear: function() {},
drawColor: function() {},
+ drawColorComponents: function() {},
drawShadow: function() {},
},
@@ -256,6 +263,17 @@ var CanvasKit = {
scaled: function() {},
},
+ SkColorSpace: {
+ Equals: function() {},
+ SRGB: {},
+ DISPLAY_P3: {},
+ ADOBE_RGB: {},
+ // private API (from C++ bindings)
+ _MakeSRGB: function() {},
+ _MakeDisplayP3: function() {},
+ _MakeAdobeRGB: function() {},
+ },
+
SkContourMeasureIter: {
next: function() {},
},
@@ -360,6 +378,7 @@ var CanvasKit = {
getStrokeWidth: function() {},
setAntiAlias: function() {},
setBlendMode: function() {},
+ setColorInt: function() {},
setFilterQuality: function() {},
setImageFilter: function() {},
setMaskFilter: function() {},
@@ -373,6 +392,8 @@ var CanvasKit = {
prototype: {
setColor: function() {},
+ setColorComponents: function() {},
+ setColorInt: function() {},
},
// Private API
@@ -412,7 +433,9 @@ var CanvasKit = {
},
SkPath: {
- // public API (from C++ bindings)
+ // public API (from C++ and JS bindings)
+ MakeFromCmds: function() {},
+ MakeFromVerbsPointsWeights: function() {},
computeTightBounds: function() {},
contains: function() {},
/** @return {CanvasKit.SkPath} */
@@ -428,15 +451,19 @@ var CanvasKit = {
rewind: function() {},
setFillType: function() {},
setIsVolatile: function() {},
+ toCmds: function() {},
toSVGString: function() {},
// private API
+ _MakeFromCmds: function() {},
+ _MakeFromVerbsPointsWeights: function() {},
_addArc: function() {},
_addOval: function() {},
_addPath: function() {},
_addRect: function() {},
_addPoly: function() {},
_addRoundRect: function() {},
+ _addVerbsPointsWeights: function() {},
_arc: function() {},
_arcTo: function() {},
_close: function() {},
@@ -517,9 +544,11 @@ var CanvasKit = {
// public API (from C++ bindings)
/** @return {CanvasKit.SkCanvas} */
getCanvas: function() {},
+ imageInfo: function() {},
/** @return {CanvasKit.SkImage} */
makeImageSnapshot: function() {},
makeSurface: function() {},
+ sampleCnt: function() {},
reportBackendType: function() {},
grContext: {},
@@ -846,6 +875,7 @@ CanvasKit.SkPath.prototype.addPath = function() {};
CanvasKit.SkPath.prototype.addPoly = function() {};
CanvasKit.SkPath.prototype.addRect = function() {};
CanvasKit.SkPath.prototype.addRoundRect = function() {};
+CanvasKit.SkPath.prototype.addVerbsPointsWeights = function() {};
CanvasKit.SkPath.prototype.arc = function() {};
CanvasKit.SkPath.prototype.arcTo = function() {};
CanvasKit.SkPath.prototype.close = function() {};
@@ -930,6 +960,9 @@ HTMLCanvas.prototype.loadFont = function() {};
HTMLCanvas.prototype.makePath2D = function() {};
HTMLCanvas.prototype.toDataURL = function() {};
+var ImageBitmapRenderingContext = {};
+ImageBitmapRenderingContext.prototype.transferFromImageBitmap = function() {};
+
var CanvasRenderingContext2D = {};
CanvasRenderingContext2D.prototype.addHitRegion = function() {};
CanvasRenderingContext2D.prototype.arc = function() {};
diff --git a/chromium/third_party/skia/modules/canvaskit/font.js b/chromium/third_party/skia/modules/canvaskit/font.js
index eb127bf8c27..845a8c2f032 100644
--- a/chromium/third_party/skia/modules/canvaskit/font.js
+++ b/chromium/third_party/skia/modules/canvaskit/font.js
@@ -65,13 +65,13 @@ CanvasKit._extraInitializations.push(function() {
var sizes = [];
for (var i = 0; i < fonts.length; i++) {
var data = new Uint8Array(fonts[i]);
- var dptr = copy1dArray(data, CanvasKit.HEAPU8);
+ var dptr = copy1dArray(data, "HEAPU8");
dPtrs.push(dptr);
sizes.push(data.byteLength);
}
// Pointers are 32 bit unsigned ints
- var datasPtr = copy1dArray(dPtrs, CanvasKit.HEAPU32);
- var sizesPtr = copy1dArray(sizes, CanvasKit.HEAPU32);
+ var datasPtr = copy1dArray(dPtrs, "HEAPU32");
+ var sizesPtr = copy1dArray(sizes, "HEAPU32");
var fm = CanvasKit.SkFontMgr._fromData(datasPtr, sizesPtr, fonts.length);
// The SkFontMgr has taken ownership of the bytes we allocated in the for loop.
CanvasKit._free(datasPtr);
@@ -83,7 +83,7 @@ CanvasKit._extraInitializations.push(function() {
CanvasKit.SkFontMgr.prototype.MakeTypefaceFromData = function(fontData) {
var data = new Uint8Array(fontData);
- var fptr = copy1dArray(data, CanvasKit.HEAPU8);
+ var fptr = copy1dArray(data, "HEAPU8");
var font = this._makeTypefaceFromData(fptr, data.byteLength);
if (!font) {
SkDebug('Could not decode font data');
diff --git a/chromium/third_party/skia/modules/canvaskit/future_apis/ImageDecoder.md b/chromium/third_party/skia/modules/canvaskit/future_apis/ImageDecoder.md
new file mode 100644
index 00000000000..a8092de5646
--- /dev/null
+++ b/chromium/third_party/skia/modules/canvaskit/future_apis/ImageDecoder.md
@@ -0,0 +1,62 @@
+# ImageDecoder API
+
+Date Updated: June 16, 2020
+
+## Summary and Links
+
+The [ImageDecoder API](https://github.com/dalecurtis/image-decoder-api/blob/master/explainer.md)
+handles decoding of both still and animated images.
+Similar to the larger [web codecs](https://github.com/WICG/web-codecs/blob/master/explainer.md)
+proposal which is focused more on video and audio.
+The ImageDecoder API could be used with `CanvasKit.MakeImageFromCanvasImageSource`
+for creating CanvasKit compatible `SkImage`s.
+For still images, the `createImageBitmap(blob)` API achieves the same result.
+
+- [Explainer](https://github.com/dalecurtis/image-decoder-api/blob/master/explainer.md)
+- [Prototype](https://chromium-review.googlesource.com/c/chromium/src/+/2145133)
+- [Discourse](https://discourse.wicg.io/t/proposal-imagedecoder-api-extension-for-webcodecs/4418)
+
+Currently available as a prototype behind the `--enable-blink-features=WebCodecs` flag
+in Chrome Canary, works in version 85.0.4175.0.
+
+## Running the prototype
+
+1. Download and install [Chrome Canary](https://www.google.com/chrome/canary/). Verify that you
+have version 85.0.4175.0 or later.
+2. Close ALL open instances of chromium browsers, including chrome.
+2. Run Chrome Canary with the `--enable-blink-features=WebCodecs` flag.
+
+**MacOS**: Run `/applications/Google\ Chrome\ Canary.app/Contents/MacOS/Google\ Chrome\ Canary --enable-blink-features=WebCodecs`
+
+**Windows, Linux**: [https://www.chromium.org/developers/how-tos/run-chromium-with-flags](https://www.chromium.org/developers/how-tos/run-chromium-with-flags)
+
+3. Navigate to: [http://storage.googleapis.com/dalecurtis/test-gif.html?src=giphy.gif](http://storage.googleapis.com/dalecurtis/test-gif.html?src=giphy.gif)
+4. You should see a cute animated cat illustration.
+
+## Example API Usage with CanvasKit
+
+With a still image:
+```jsx
+const response = await fetch(stillImageUrl); // e.g. png or jpeg
+const data = await response.arrayBuffer();
+
+const imageDecoder = new ImageDecoder({ data });
+const imageBitmap = await imageDecoder.decode();
+
+const skImage = CanvasKit.MakeImageFromCanvasImageSource(imageBitmap);
+// do something with skImage, such as drawing it
+```
+
+With an animated image:
+```jsx
+const response = await fetch(animatedImageUrl); // e.g. gif or mjpeg
+const data = await response.arrayBuffer();
+
+const imageDecoder = new ImageDecoder({ data });
+
+for (let frame = 0; frame < imageDecoder.frameCount; frame++) {
+ const imageBitmap = await imageDecoder.decode(frame);
+ const skImage = CanvasKit.MakeImageFromCanvasImageSource(imageBitmap);
+ // do something with skImage, such as drawing it
+}
+```
diff --git a/chromium/third_party/skia/modules/canvaskit/future_apis/WebGPU.md b/chromium/third_party/skia/modules/canvaskit/future_apis/WebGPU.md
new file mode 100644
index 00000000000..f141f05c8a8
--- /dev/null
+++ b/chromium/third_party/skia/modules/canvaskit/future_apis/WebGPU.md
@@ -0,0 +1,17 @@
+# WebGPU API
+
+Date Updated: June 16, 2020
+
+## Summary and Links
+
+WebGPU exposes an API for performing operations, such as rendering and computation,
+on a Graphics Processing Unit. [Dawn](https://dawn.googlesource.com/dawn) is the underlying
+implementation of WebGPU in chromium. In the future, with
+[WebGPU bindings provided by emscripten](https://github.com/emscripten-core/emscripten/pull/10218),
+CanvasKit should be able to use a WebGPU rendering device.
+
+- [W.I.P. Specification](https://gpuweb.github.io/gpuweb/)
+- [WebGPU Samples](https://austineng.github.io/webgpu-samples/)
+- [Implementation Status](https://github.com/gpuweb/gpuweb/wiki/Implementation-Status)
+
+Some features are currently available in Chrome Canary behind the `--enable-unsafe-webgpu` flag.
diff --git a/chromium/third_party/skia/modules/canvaskit/gpu.js b/chromium/third_party/skia/modules/canvaskit/gpu.js
index adfc7a172b2..20b5553fa20 100644
--- a/chromium/third_party/skia/modules/canvaskit/gpu.js
+++ b/chromium/third_party/skia/modules/canvaskit/gpu.js
@@ -1,5 +1,6 @@
// Adds compile-time JS functions to augment the CanvasKit interface.
// Specifically, anything that should only be on the GPU version of canvaskit.
+// Functions in this file are supplemented by cpu.js.
(function(CanvasKit){
CanvasKit._extraInitializations = CanvasKit._extraInitializations || [];
CanvasKit._extraInitializations.push(function() {
@@ -10,89 +11,69 @@
return defaultValue;
}
- function makeWebGLContext(canvas, attrs) {
- var contextAttributes = {
- alpha: get(attrs, 'alpha', 1),
- depth: get(attrs, 'depth', 1),
- stencil: get(attrs, 'stencil', 8),
- antialias: get(attrs, 'antialias', 1),
- premultipliedAlpha: get(attrs, 'premultipliedAlpha', 1),
- preserveDrawingBuffer: get(attrs, 'preserveDrawingBuffer', 0),
- preferLowPowerToHighPerformance: get(attrs, 'preferLowPowerToHighPerformance', 0),
- failIfMajorPerformanceCaveat: get(attrs, 'failIfMajorPerformanceCaveat', 0),
- majorVersion: get(attrs, 'majorVersion', 2),
- minorVersion: get(attrs, 'minorVersion', 0),
- enableExtensionsByDefault: get(attrs, 'enableExtensionsByDefault', 1),
- explicitSwapControl: get(attrs, 'explicitSwapControl', 0),
- renderViaOffscreenBackBuffer: get(attrs, 'renderViaOffscreenBackBuffer', 0),
- };
+ CanvasKit.makeWebGLContextAsCurrent = function(canvas, attrs) {
if (!canvas) {
- SkDebug('null canvas passed into makeWebGLContext');
- return 0;
+ throw 'null canvas passed into makeWebGLContext';
}
+ var contextAttributes = {
+ 'alpha': get(attrs, 'alpha', 1),
+ 'depth': get(attrs, 'depth', 1),
+ 'stencil': get(attrs, 'stencil', 8),
+ 'antialias': get(attrs, 'antialias', 0),
+ 'premultipliedAlpha': get(attrs, 'premultipliedAlpha', 1),
+ 'preserveDrawingBuffer': get(attrs, 'preserveDrawingBuffer', 0),
+ 'preferLowPowerToHighPerformance': get(attrs, 'preferLowPowerToHighPerformance', 0),
+ 'failIfMajorPerformanceCaveat': get(attrs, 'failIfMajorPerformanceCaveat', 0),
+ 'enableExtensionsByDefault': get(attrs, 'enableExtensionsByDefault', 1),
+ 'explicitSwapControl': get(attrs, 'explicitSwapControl', 0),
+ 'renderViaOffscreenBackBuffer': get(attrs, 'renderViaOffscreenBackBuffer', 0),
+ };
+
// This check is from the emscripten version
if (contextAttributes['explicitSwapControl']) {
- SkDebug('explicitSwapControl is not supported');
- return 0;
+ throw 'explicitSwapControl is not supported';
}
- // GL is an enscripten provided helper
- // See https://github.com/emscripten-core/emscripten/blob/incoming/src/library_webgl.js
- return GL.createContext(canvas, contextAttributes);
+ // Creates a WebGL context and sets it to be the current context.
+ this.createContext(canvas, true, true, contextAttributes);
}
CanvasKit.GetWebGLContext = function(canvas, attrs) {
- return makeWebGLContext(canvas, attrs);
+ this.makeWebGLContextAsCurrent(canvas, attrs);
+ return CanvasKit.currentContext() || 0;
};
- // arg can be of types:
+ // idOrElement can be of types:
// - String - in which case it is interpreted as an id of a
// canvas element.
// - HTMLCanvasElement - in which the provided canvas element will
// be used directly.
- // Width and height can be provided to override those on the canvas
- // element, or specify a height for when a context is provided.
- CanvasKit.MakeWebGLCanvasSurface = function(arg, width, height, webGLVersion) {
- var canvas = arg;
+ // colorSpace - sk_sp<SkColorSpace> - one of the supported color spaces:
+ // CanvasKit.SkColorSpace.SRGB
+ // CanvasKit.SkColorSpace.DISPLAY_P3
+ // CanvasKit.SkColorSpace.ADOBE_RGB
+ CanvasKit.MakeWebGLCanvasSurface = function(idOrElement, colorSpace) {
+ colorSpace = colorSpace || null;
+ var canvas = idOrElement;
if (canvas.tagName !== 'CANVAS') {
- canvas = document.getElementById(arg);
+ canvas = document.getElementById(idOrElement);
if (!canvas) {
- throw 'Canvas with id ' + arg + ' was not found';
- }
- }
- if (!webGLVersion) {
- if (!!window['safari']) {
- // Safari, by default, should use WebGL 1. skbug.com/10171
- webGLVersion = 1;
- } else {
- webGLVersion = 2;
+ throw 'Canvas with id ' + idOrElement + ' was not found';
}
}
// we are ok with all the other defaults.
- var ctx = this.GetWebGLContext(canvas, {majorVersion: webGLVersion});
+ var ctx = this.GetWebGLContext(canvas);
if (!ctx || ctx < 0) {
throw 'failed to create webgl context: err ' + ctx;
}
- if (!canvas && (!width || !height)) {
- throw 'height and width must be provided with context';
- }
-
var grcontext = this.MakeGrContext(ctx);
- if (grcontext) {
- // Bump the default resource cache limit.
- var RESOURCE_CACHE_BYTES = 256 * 1024 * 1024;
- grcontext.setResourceCacheLimitBytes(RESOURCE_CACHE_BYTES);
- }
-
-
- // Maybe better to use clientWidth/height. See:
- // https://webglfundamentals.org/webgl/lessons/webgl-anti-patterns.html
- var surface = this.MakeOnScreenGLSurface(grcontext,
- width || canvas.width,
- height || canvas.height);
+ // Note that canvas.width/height here is used because it gives the size of the buffer we're
+ // rendering into. This may not be the same size the element is displayed on the page, which
+ // constrolled by css, and available in canvas.clientWidth/height.
+ var surface = this.MakeOnScreenGLSurface(grcontext, canvas.width, canvas.height, colorSpace);
if (!surface) {
SkDebug('falling back from GPU implementation to a SW based one');
// we need to throw away the old canvas (which was locked to
diff --git a/chromium/third_party/skia/modules/canvaskit/helper.js b/chromium/third_party/skia/modules/canvaskit/helper.js
index 78e62f3c30a..230b1a23062 100644
--- a/chromium/third_party/skia/modules/canvaskit/helper.js
+++ b/chromium/third_party/skia/modules/canvaskit/helper.js
@@ -17,6 +17,21 @@ CanvasKit.Color = function(r, g, b, a) {
return CanvasKit.Color4f(clamp(r)/255, clamp(g)/255, clamp(b)/255, a);
}
+// Constructs a Color as a 32 bit unsigned integer, with 8 bits assigned to each channel.
+// Channels are expected to be between 0 and 255 and will be clamped as such.
+CanvasKit.ColorAsInt = function(r, g, b, a) {
+ // default to opaque
+ if (a === undefined) {
+ a = 255;
+ }
+ // This is consistent with how Skia represents colors in C++, as an unsigned int.
+ // This is also consistent with how Flutter represents colors:
+ // https://github.com/flutter/engine/blob/243bb59c7179a7e701ce478080d6ce990710ae73/lib/web_ui/lib/src/ui/painting.dart#L50
+ return (((clamp(a) << 24) | (clamp(r) << 16) | (clamp(g) << 8) | (clamp(b) << 0)
+ & 0xFFFFFFF) // This truncates the unsigned to 32 bits and signals to JS engines they can
+ // represent the number with an int instead of a double.
+ >>> 0); // This makes the value an unsigned int.
+}
// Construct a 4-float color.
// Opaque if opacity is omitted.
CanvasKit.Color4f = function(r, g, b, a) {
@@ -138,6 +153,23 @@ function isCanvasKitColor(ob) {
function toUint32Color(c) {
return ((clamp(c[3]*255) << 24) | (clamp(c[0]*255) << 16) | (clamp(c[1]*255) << 8) | (clamp(c[2]*255) << 0)) >>> 0;
}
+// Accepts various colors representations and converts them to an array of int colors.
+// Does not handle builders.
+function assureIntColors(arr) {
+ if (arr instanceof Float32Array) {
+ var count = Math.floor(arr.length / 4);
+ var result = new Uint32Array(count);
+ for (var i = 0; i < count; i ++) {
+ result[i] = toUint32Color(arr.slice(i*4, (i+1)*4));
+ }
+ return result;
+ } else if (arr instanceof Uint32Array) {
+ return arr;
+ } else if (arr instanceof Array && arr[0] instanceof Float32Array) {
+ return arr.map(toUint32Color);
+ }
+}
+
function uIntColorToCanvasKitColor(c) {
return CanvasKit.Color(
(c >> 16) & 0xFF,
@@ -183,11 +215,11 @@ function almostEqual(floata, floatb) {
return Math.abs(floata - floatb) < 0.00001;
}
-
var nullptr = 0; // emscripten doesn't like to take null as uintptr_t
// arr can be a normal JS array or a TypedArray
-// dest is something like CanvasKit.HEAPF32
+// dest is a string like "HEAPU32" that specifies the type the src array
+// should be copied into.
// ptr can be optionally provided if the memory was already allocated.
function copy1dArray(arr, dest, ptr) {
if (!arr || !arr.length) {
@@ -197,8 +229,9 @@ function copy1dArray(arr, dest, ptr) {
if (arr['_ck']) {
return arr.byteOffset;
}
+ var bytesPerElement = CanvasKit[dest].BYTES_PER_ELEMENT;
if (!ptr) {
- ptr = CanvasKit._malloc(arr.length * dest.BYTES_PER_ELEMENT);
+ ptr = CanvasKit._malloc(arr.length * bytesPerElement);
}
// In c++ terms, the WASM heap is a uint8_t*, a long buffer/array of single
// byte elements. When we run _malloc, we always get an offset/pointer into
@@ -209,7 +242,10 @@ function copy1dArray(arr, dest, ptr) {
// Concretely, we need to convert ptr to go from an index into a 1-byte-wide
// buffer to an index into a 4-byte-wide buffer (in the case of HEAPF32)
// and thus we divide ptr by 4.
- dest.set(arr, ptr / dest.BYTES_PER_ELEMENT);
+ // It is important to make sure we are grabbing the freshest view of the
+ // memory possible because if we call _malloc and the heap needs to grow,
+ // the TypedArrayView will no longer be valid.
+ CanvasKit[dest].set(arr, ptr / bytesPerElement);
return ptr;
}
@@ -217,15 +253,19 @@ function copy1dArray(arr, dest, ptr) {
// inside themselves). A common use case is points.
// dest is something like CanvasKit.HEAPF32
// ptr can be optionally provided if the memory was already allocated.
+// TODO(kjlubick): Remove 2d arrays - everything should be flat.
function copy2dArray(arr, dest, ptr) {
if (!arr || !arr.length) {
return nullptr;
}
+ var bytesPerElement = CanvasKit[dest].BYTES_PER_ELEMENT;
if (!ptr) {
- ptr = CanvasKit._malloc(arr.length * arr[0].length * dest.BYTES_PER_ELEMENT);
+ ptr = CanvasKit._malloc(arr.length * arr[0].length * bytesPerElement);
}
+ // Make sure we have a fresh view of the heap after we malloc.
+ dest = CanvasKit[dest];
var idx = 0;
- var adjustedPtr = ptr / dest.BYTES_PER_ELEMENT;
+ var adjustedPtr = ptr / bytesPerElement;
for (var r = 0; r < arr.length; r++) {
for (var c = 0; c < arr[0].length; c++) {
dest[adjustedPtr + idx] = arr[r][c];
@@ -235,32 +275,41 @@ function copy2dArray(arr, dest, ptr) {
return ptr;
}
-// arr should be a non-jagged 3d JS array (TypedArrays can't be nested
-// inside themselves.)
-// dest is something like CanvasKit.HEAPF32
-// ptr can be optionally provided if the memory was already allocated.
-function copy3dArray(arr, dest, ptr) {
- if (!arr || !arr.length || !arr[0].length) {
- return nullptr;
- }
- if (!ptr) {
- ptr = CanvasKit._malloc(arr.length * arr[0].length * arr[0][0].length * dest.BYTES_PER_ELEMENT);
- }
- var idx = 0;
- var adjustedPtr = ptr / dest.BYTES_PER_ELEMENT;
- for (var x = 0; x < arr.length; x++) {
- for (var y = 0; y < arr[0].length; y++) {
- for (var z = 0; z < arr[0][0].length; z++) {
- dest[adjustedPtr + idx] = arr[x][y][z];
- idx++;
- }
- }
+// Copies an array of colors to wasm, returning an object with the pointer
+// and info necessary to use the copied colors.
+// Accepts either a flat Float32Array, flat Uint32Array or Array of Float32Arrays.
+// If color is an object that was allocated with CanvasKit.Malloc, it's pointer is
+// returned and no extra copy is performed.
+// Array of Float32Arrays is deprecated and planned to be removed, prefer flat
+// Float32Array
+// TODO(nifong): have this accept color builders.
+function copyFlexibleColorArray(colors) {
+ var result = {
+ colorPtr: nullptr,
+ count: colors.length,
+ colorType: CanvasKit.ColorType.RGBA_F32,
+ }
+ if (colors instanceof Float32Array) {
+ result.colorPtr = copy1dArray(colors, "HEAPF32");
+ result.count = colors.length / 4;
+
+ } else if (colors instanceof Uint32Array) {
+ result.colorPtr = copy1dArray(colors, "HEAPU32");
+ result.colorType = CanvasKit.ColorType.RGBA_8888;
+
+ } else if (colors instanceof Array && colors[0] instanceof Float32Array) {
+ result.colorPtr = copy2dArray(colors, "HEAPF32");
+ } else {
+ throw('Invalid argument to copyFlexibleColorArray, Not a color array '+typeof(colors));
}
- return ptr;
+ return result;
}
var defaultPerspective = Float32Array.of(0, 0, 1);
+var _scratch3x3MatrixPtr = nullptr;
+var _scratch3x3Matrix; // the result from CanvasKit.Malloc
+
// Copies the given DOMMatrix/Array/TypedArray to the CanvasKit heap and
// returns a pointer to the memory. This memory is a float* of length 9.
// If the passed in matrix is null/undefined, we return 0 (nullptr). All calls
@@ -271,7 +320,7 @@ function copy3x3MatrixToWasm(matr) {
if (!matr) {
return nullptr;
}
- var mPtr = CanvasKit._malloc(9 * 4); // 9 matrix scalars, each at 4 bytes.
+
if (matr.length) {
// TODO(kjlubick): Downsample a 16 length (4x4 matrix)
if (matr.length !== 6 && matr.length !== 9) {
@@ -279,27 +328,38 @@ function copy3x3MatrixToWasm(matr) {
}
// This should be an array or typed array.
// have to divide the pointer by 4 to "cast" it from bytes to float.
- CanvasKit.HEAPF32.set(matr, mPtr / 4);
+ var mPtr = copy1dArray(matr, "HEAPF32", _scratch3x3MatrixPtr);
if (matr.length === 6) {
- CanvasKit.HEAPF32.set(defaultPerspective, 6 + mPtr / 4);
+ // Overwrite the last 3 floats with the default perspective. The divide
+ // by 4 casts the pointer into a float pointer.
+ CanvasKit.HEAPF32.set(defaultPerspective, 6 + mPtr / 4);
}
- } else {
- // Try as if it's a DOMMatrix. Reminder that DOMMatrix is column-major.
- var floats = Float32Array.of(
- matr.m11, matr.m21, matr.m41,
- matr.m12, matr.m22, matr.m42,
- matr.m14, matr.m24, matr.m44);
- // have to divide the pointer by 4 to "cast" it from bytes to float.
- CanvasKit.HEAPF32.set(floats, mPtr / 4);
+ return mPtr;
}
- return mPtr;
+ var wasm3x3Matrix = _scratch3x3Matrix['toTypedArray']();
+ // Try as if it's a DOMMatrix. Reminder that DOMMatrix is column-major.
+ wasm3x3Matrix[0] = matr.m11;
+ wasm3x3Matrix[1] = matr.m21;
+ wasm3x3Matrix[2] = matr.m41;
+
+ wasm3x3Matrix[3] = matr.m12;
+ wasm3x3Matrix[4] = matr.m22;
+ wasm3x3Matrix[5] = matr.m42;
+
+ wasm3x3Matrix[6] = matr.m14;
+ wasm3x3Matrix[7] = matr.m24;
+ wasm3x3Matrix[8] = matr.m44;
+ return _scratch3x3MatrixPtr;
}
+var _scratch4x4MatrixPtr = nullptr;
+var _scratch4x4Matrix; // the result from CanvasKit.Malloc
+
function copy4x4MatrixToWasm(matr) {
if (!matr) {
return nullptr;
}
- var mPtr = CanvasKit._malloc(16 * 4); // 9 matrix scalars, each at 4 bytes.
+ var wasm4x4Matrix = _scratch4x4Matrix['toTypedArray']();
if (matr.length) {
if (matr.length !== 16 && matr.length !== 6 && matr.length !== 9) {
throw 'invalid matrix size';
@@ -307,39 +367,64 @@ function copy4x4MatrixToWasm(matr) {
if (matr.length === 16) {
// This should be an array or typed array.
// have to divide the pointer by 4 to "cast" it from bytes to float.
- CanvasKit.HEAPF32.set(matr, mPtr / 4);
- } else {
- // Upscale the row-major 3x3 or 3x2 matrix into a 4x4 row-major matrix
- // TODO(skbug.com/10108) This will need to change when we convert our
- // JS 4x4 to be column-major.
- var floats = Float32Array.of(
- matr[0], matr[1], 0, matr[2],
- matr[3], matr[4], 0, matr[5],
- 0, 0, 0, 0,
- matr[6], matr[7], 0, matr[8]);
- if (matr.length === 6) {
- // fix perspective for the 3x2 case (from above, they will be undefined).
- floats[4*3+0]=0;
- floats[4*3+1]=0;
- floats[4*3+3]=1;
- }
- CanvasKit.HEAPF32.set(floats, mPtr / 4);
+ return copy1dArray(matr, "HEAPF32", _scratch4x4MatrixPtr);
}
- } else {
- // Try as if it's a DOMMatrix. Reminder that DOMMatrix is column-major.
- // TODO(skbug.com/10108) use toFloat32Array().
- var floats = Float32Array.of(
- matr.m11, matr.m21, matr.m31, matr.m41,
- matr.m12, matr.m22, matr.m32, matr.m42,
- matr.m13, matr.m23, matr.m33, matr.m43,
- matr.m14, matr.m24, matr.m34, matr.m44);
- // have to divide the pointer by 4 to "cast" it from bytes to float.
- CanvasKit.HEAPF32.set(floats, mPtr / 4);
+ // Upscale the row-major 3x3 or 3x2 matrix into a 4x4 row-major matrix
+ // TODO(skbug.com/10108) This will need to change when we convert our
+ // JS 4x4 to be column-major.
+ // When upscaling, we need to overwrite the 3rd column and the 3rd row with
+ // 0s. It's easiest to just do that with a fill command.
+ wasm4x4Matrix.fill(0);
+ wasm4x4Matrix[0] = matr[0];
+ wasm4x4Matrix[1] = matr[1];
+ // skip col 2
+ wasm4x4Matrix[3] = matr[2];
+
+ wasm4x4Matrix[4] = matr[3];
+ wasm4x4Matrix[5] = matr[4];
+ // skip col 2
+ wasm4x4Matrix[7] = matr[5];
+
+ // skip row 2
+
+ wasm4x4Matrix[12] = matr[6];
+ wasm4x4Matrix[13] = matr[7];
+ // skip col 2
+ wasm4x4Matrix[15] = matr[8];
+
+ if (matr.length === 6) {
+ // fix perspective for the 3x2 case (from above, they will be undefined).
+ wasm4x4Matrix[12]=0;
+ wasm4x4Matrix[13]=0;
+ wasm4x4Matrix[15]=1;
+ }
+ return _scratch4x4MatrixPtr;
}
- return mPtr;
+ // Try as if it's a DOMMatrix. Reminder that DOMMatrix is column-major.
+ wasm4x4Matrix[0] = matr.m11;
+ wasm4x4Matrix[1] = matr.m21;
+ wasm4x4Matrix[2] = matr.m31;
+ wasm4x4Matrix[3] = matr.m41;
+
+ wasm4x4Matrix[4] = matr.m12;
+ wasm4x4Matrix[5] = matr.m22;
+ wasm4x4Matrix[6] = matr.m32;
+ wasm4x4Matrix[7] = matr.m42;
+
+ wasm4x4Matrix[8] = matr.m13;
+ wasm4x4Matrix[9] = matr.m23;
+ wasm4x4Matrix[10] = matr.m33;
+ wasm4x4Matrix[11] = matr.m43;
+
+ wasm4x4Matrix[12] = matr.m14;
+ wasm4x4Matrix[13] = matr.m24;
+ wasm4x4Matrix[14] = matr.m34;
+ wasm4x4Matrix[15] = matr.m44;
+ return _scratch4x4MatrixPtr;
}
-// copies a 4x4 matrix at the given pointer into a JS array.
+// copies a 4x4 matrix at the given pointer into a JS array. It is the caller's
+// responsibility to free the matrPtr if needed.
function copy4x4MatrixFromWasm(matrPtr) {
// read them out into an array. TODO(kjlubick): If we change SkMatrix to be
// typedArrays, then we should return a typed array here too.
@@ -347,19 +432,38 @@ function copy4x4MatrixFromWasm(matrPtr) {
for (var i = 0; i < 16; i++) {
rv[i] = CanvasKit.HEAPF32[matrPtr/4 + i]; // divide by 4 to "cast" to float.
}
- CanvasKit._free(matrPtr);
return rv;
}
+var _scratchColorPtr = nullptr;
+var _scratchColor; // the result from CanvasKit.Malloc
+
+function copyColorToWasm(color4f, ptr) {
+ return copy1dArray(color4f, "HEAPF32", ptr || _scratchColorPtr);
+}
+
+function copyColorComponentsToWasm(r, g, b, a) {
+ var colors = _scratchColor['toTypedArray']();
+ colors[0] = r;
+ colors[1] = g;
+ colors[2] = b;
+ colors[3] = a;
+ return _scratchColorPtr;
+}
+
+function copyColorToWasmNoScratch(color4f) {
+ // TODO(kjlubick): accept 4 floats or int color
+ return copy1dArray(color4f, "HEAPF32");
+}
+
// copies the four floats at the given pointer in a js Float32Array
function copyColorFromWasm(colorPtr) {
var rv = new Float32Array(4);
for (var i = 0; i < 4; i++) {
rv[i] = CanvasKit.HEAPF32[colorPtr/4 + i]; // divide by 4 to "cast" to float.
}
- CanvasKit._free(colorPtr);
return rv;
-}
+}
// Caching the Float32Arrays can save having to reallocate them
// over and over again.
@@ -377,6 +481,7 @@ var Float32ArrayCache = {};
// [CanvasKit.LINE_VERB, 30, 40],
// [CanvasKit.QUAD_VERB, 20, 50, 45, 60],
// ];
+// TODO(kjlubick) remove this and Float32ArrayCache (superceded by Malloc).
function loadCmdsTypedArray(arr) {
var len = 0;
for (var r = 0; r < arr.length; r++) {
@@ -400,7 +505,7 @@ function loadCmdsTypedArray(arr) {
}
}
- var ptr = copy1dArray(ta, CanvasKit.HEAPF32);
+ var ptr = copy1dArray(ta, "HEAPF32");
return [ptr, len];
}
@@ -490,7 +595,7 @@ CanvasKit.FourFloatArrayHelper.prototype.build = function() {
if (this._ptr) {
return this._ptr;
}
- this._ptr = copy1dArray(this._floats, CanvasKit.HEAPF32);
+ this._ptr = copy1dArray(this._floats, "HEAPF32");
return this._ptr;
}
@@ -563,7 +668,7 @@ CanvasKit.OneUIntArrayHelper.prototype.build = function() {
if (this._ptr) {
return this._ptr;
}
- this._ptr = copy1dArray(this._uints, CanvasKit.HEAPU32);
+ this._ptr = copy1dArray(this._uints, "HEAPU32");
return this._ptr;
}
@@ -630,20 +735,64 @@ CanvasKit.SkColorBuilder = CanvasKit.OneUIntArrayHelper;
* can manage memory and initialize values properly. When used
* correctly, it can save copying of data between JS and C++.
* When used incorrectly, it can lead to memory leaks.
+ * Any memory allocated by CanvasKit.Malloc needs to be released with CanvasKit.Free.
*
- * const ta = CanvasKit.Malloc(Float32Array, 20);
+ * const mObj = CanvasKit.Malloc(Float32Array, 20);
+ * Get a TypedArray view around the malloc'd memory (this does not copy anything).
+ * const ta = mObj.toTypedArray();
* // store data into ta
- * const cf = CanvasKit.SkColorFilter.MakeMatrix(ta);
- * // MakeMatrix cleans up the ptr automatically.
+ * const cf = CanvasKit.SkColorFilter.MakeMatrix(ta); // mObj could also be used.
+ *
+ * // eventually...
+ * CanvasKit.Free(mObj);
*
* @param {TypedArray} typedArray - constructor for the typedArray.
- * @param {number} len - number of elements to store.
+ * @param {number} len - number of *elements* to store.
*/
CanvasKit.Malloc = function(typedArray, len) {
var byteLen = len * typedArray.BYTES_PER_ELEMENT;
var ptr = CanvasKit._malloc(byteLen);
- var ta = new typedArray(CanvasKit.HEAPU8.buffer, ptr, len);
- // add a marker that this was allocated in C++ land
- ta['_ck'] = true;
- return ta;
+ return {
+ '_ck': true,
+ 'length': len,
+ 'byteOffset': ptr,
+ typedArray: null,
+ 'subarray': function(start, end) {
+ var sa = this['toTypedArray']().subarray(start, end);
+ sa['_ck'] = true;
+ return sa;
+ },
+ 'toTypedArray': function() {
+ // Check if the previously allocated array is still usable.
+ // If it's falsey, then we haven't created an array yet.
+ // If it's empty, then WASM resized memory and emptied the array.
+ if (this.typedArray && this.typedArray.length) {
+ return this.typedArray;
+ }
+ this.typedArray = new typedArray(CanvasKit.HEAPU8.buffer, ptr, len);
+ // add a marker that this was allocated in C++ land
+ this.typedArray['_ck'] = true;
+ return this.typedArray;
+ },
+ };
+};
+
+/**
+ * Free frees the memory returned by Malloc.
+ * Any memory allocated by CanvasKit.Malloc needs to be released with CanvasKit.Free.
+ */
+CanvasKit.Free = function(mallocObj) {
+ CanvasKit._free(mallocObj['byteOffset']);
+ mallocObj['byteOffset'] = nullptr;
+ // Set these to null to make sure the TypedArrays can be garbage collected.
+ mallocObj['toTypedArray'] = null;
+ mallocObj.typedArray = null;
+};
+
+// This helper will free the given pointer unless the provided array is one
+// that was returned by CanvasKit.Malloc.
+function freeArraysThatAreNotMallocedByUsers(ptr, arr) {
+ if (arr && !arr['_ck']) {
+ CanvasKit._free(ptr);
+ }
}
diff --git a/chromium/third_party/skia/modules/canvaskit/htmlcanvas/_namedcolors.js b/chromium/third_party/skia/modules/canvaskit/htmlcanvas/_namedcolors.js
index 16dec34d66f..69a736b151e 100644
--- a/chromium/third_party/skia/modules/canvaskit/htmlcanvas/_namedcolors.js
+++ b/chromium/third_party/skia/modules/canvaskit/htmlcanvas/_namedcolors.js
@@ -7,7 +7,7 @@ const CanvasKitInit = require('../canvaskit/bin/canvaskit.js');
CanvasKitInit({
locateFile: (file) => __dirname + '/../canvaskit/bin/'+file,
-}).ready().then((CanvasKit) => {
+}).then((CanvasKit) => {
let colorMap = {
// From https://drafts.csswg.org/css-color/#named-colors
'aliceblue': CanvasKit.Color(240, 248, 255),
@@ -162,4 +162,4 @@ CanvasKitInit({
};
console.log(JSON.stringify(colorMap));
-}); \ No newline at end of file
+});
diff --git a/chromium/third_party/skia/modules/canvaskit/htmlcanvas/canvas2dcontext.js b/chromium/third_party/skia/modules/canvaskit/htmlcanvas/canvas2dcontext.js
index f4fc8dd1a20..8ebecceefbe 100644
--- a/chromium/third_party/skia/modules/canvaskit/htmlcanvas/canvas2dcontext.js
+++ b/chromium/third_party/skia/modules/canvaskit/htmlcanvas/canvas2dcontext.js
@@ -848,7 +848,8 @@ function CanvasRenderingContext2D(skcanvas) {
}
var img = CanvasKit.MakeImage(imageData.data, imageData.width, imageData.height,
CanvasKit.AlphaType.Unpremul,
- CanvasKit.ColorType.RGBA_8888);
+ CanvasKit.ColorType.RGBA_8888,
+ CanvasKit.SkColorSpace.SRGB);
var src = CanvasKit.XYWHRect(dirtyX, dirtyY, dirtyWidth, dirtyHeight);
var dst = CanvasKit.XYWHRect(x+dirtyX, y+dirtyY, dirtyWidth, dirtyHeight);
var inverted = CanvasKit.SkMatrix.invert(this._currentTransform);
diff --git a/chromium/third_party/skia/modules/canvaskit/interface.js b/chromium/third_party/skia/modules/canvaskit/interface.js
index 9159f901ac5..0135dbbbaff 100644
--- a/chromium/third_party/skia/modules/canvaskit/interface.js
+++ b/chromium/third_party/skia/modules/canvaskit/interface.js
@@ -8,6 +8,20 @@
CanvasKit.onRuntimeInitialized = function() {
// All calls to 'this' need to go in externs.js so closure doesn't minify them away.
+ _scratchColor = CanvasKit.Malloc(Float32Array, 4); // 4 color scalars.
+ _scratchColorPtr = _scratchColor['byteOffset'];
+
+ _scratch4x4Matrix = CanvasKit.Malloc(Float32Array, 16); // 16 matrix scalars.
+ _scratch4x4MatrixPtr = _scratch4x4Matrix['byteOffset'];
+
+ _scratch3x3Matrix = CanvasKit.Malloc(Float32Array, 9); // 9 matrix scalars.
+ _scratch3x3MatrixPtr = _scratch3x3Matrix['byteOffset'];
+ // Create single copies of all three supported color spaces
+ // These are sk_sp<SkColorSpace>
+ CanvasKit.SkColorSpace.SRGB = CanvasKit.SkColorSpace._MakeSRGB();
+ CanvasKit.SkColorSpace.DISPLAY_P3 = CanvasKit.SkColorSpace._MakeDisplayP3();
+ CanvasKit.SkColorSpace.ADOBE_RGB = CanvasKit.SkColorSpace._MakeAdobeRGB();
+
// Add some helpers for matrices. This is ported from SkMatrix.cpp
// to save complexity and overhead of going back and forth between
// C++ and JS layers.
@@ -260,7 +274,7 @@ CanvasKit.onRuntimeInitialized = function() {
// This rotate can be used when you already have the cosAngle and sinAngle values
// so you don't have to atan(cos/sin) to call roatated() which expects an angle in radians.
// this does no checking! Behavior for invalid sin or cos values or non-normalized axis vectors
- // is incorrect. Prefer rotate().
+ // is incorrect. Prefer rotated().
CanvasKit.SkM44.rotatedUnitSinCos = function(axisVec, sinAngle, cosAngle) {
var x = axisVec[0];
var y = axisVec[1];
@@ -506,7 +520,31 @@ CanvasKit.onRuntimeInitialized = function() {
}
return m;
- }
+ };
+
+ CanvasKit.SkPath.MakeFromCmds = function(cmds) {
+ var ptrLen = loadCmdsTypedArray(cmds);
+ var path = CanvasKit.SkPath._MakeFromCmds(ptrLen[0], ptrLen[1]);
+ CanvasKit._free(ptrLen[0]);
+ return path;
+ };
+
+ // Deprecated
+ CanvasKit.MakePathFromCmds = CanvasKit.SkPath.MakeFromCmds;
+
+ // The weights array is optional (only used for conics).
+ CanvasKit.SkPath.MakeFromVerbsPointsWeights = function(verbs, pts, weights) {
+ var verbsPtr = copy1dArray(verbs, "HEAPU8");
+ var pointsPtr = copy1dArray(pts, "HEAPF32");
+ var weightsPtr = copy1dArray(weights, "HEAPF32");
+ var numWeights = (weights && weights.length) || 0;
+ var path = CanvasKit.SkPath._MakeFromVerbsPointsWeights(
+ verbsPtr, verbs.length, pointsPtr, pts.length, weightsPtr, numWeights);
+ freeArraysThatAreNotMallocedByUsers(verbsPtr, verbs);
+ freeArraysThatAreNotMallocedByUsers(pointsPtr, pts);
+ freeArraysThatAreNotMallocedByUsers(weightsPtr, weights);
+ return path;
+ };
CanvasKit.SkPath.prototype.addArc = function(oval, startAngle, sweepAngle) {
// see arc() for the HTMLCanvas version
@@ -577,11 +615,11 @@ CanvasKit.onRuntimeInitialized = function() {
ptr = points.byteOffset;
n = points.length/2;
} else {
- ptr = copy2dArray(points, CanvasKit.HEAPF32);
+ ptr = copy2dArray(points, "HEAPF32");
n = points.length;
}
this._addPoly(ptr, n, close);
- CanvasKit._free(ptr);
+ freeArraysThatAreNotMallocedByUsers(ptr, points);
return this;
};
@@ -607,14 +645,14 @@ CanvasKit.onRuntimeInitialized = function() {
CanvasKit.SkPath.prototype.addRoundRect = function() {
// Takes 3, 4, 6 or 7 args
- // - SkRect, radii, ccw
+ // - SkRect, radii (an array of 8 numbers), ccw
// - SkRect, rx, ry, ccw
// - left, top, right, bottom, radii, ccw
// - left, top, right, bottom, rx, ry, ccw
var args = arguments;
if (args.length === 3 || args.length === 6) {
var radii = args[args.length-2];
- } else if (args.length === 6 || args.length === 7){
+ } else if (args.length === 4 || args.length === 7){
// duplicate the given (rx, ry) pairs for each corner.
var rx = args[args.length-3];
var ry = args[args.length-2];
@@ -627,7 +665,7 @@ CanvasKit.onRuntimeInitialized = function() {
SkDebug('addRoundRect needs 8 radii provided. Got ' + radii.length);
return null;
}
- var rptr = copy1dArray(radii, CanvasKit.HEAPF32);
+ var rptr = copy1dArray(radii, "HEAPF32");
if (args.length === 3 || args.length === 4) {
var r = args[0];
var ccw = args[args.length - 1];
@@ -636,10 +674,23 @@ CanvasKit.onRuntimeInitialized = function() {
var a = args;
this._addRoundRect(a[0], a[1], a[2], a[3], rptr, ccw);
}
- CanvasKit._free(rptr);
+ freeArraysThatAreNotMallocedByUsers(rptr, radii);
return this;
};
+ // The weights array is optional (only used for conics).
+ CanvasKit.SkPath.prototype.addVerbsPointsWeights = function(verbs, points, weights) {
+ var verbsPtr = copy1dArray(verbs, "HEAPU8");
+ var pointsPtr = copy1dArray(points, "HEAPF32");
+ var weightsPtr = copy1dArray(weights, "HEAPF32");
+ var numWeights = (weights && weights.length) || 0;
+ this._addVerbsPointsWeights(verbsPtr, verbs.length, pointsPtr, points.length,
+ weightsPtr, numWeights);
+ freeArraysThatAreNotMallocedByUsers(verbsPtr, verbs);
+ freeArraysThatAreNotMallocedByUsers(pointsPtr, points);
+ freeArraysThatAreNotMallocedByUsers(weightsPtr, weights);
+ };
+
CanvasKit.SkPath.prototype.arc = function(x, y, radius, startAngle, endAngle, ccw) {
// emulates the HTMLCanvas behavior. See addArc() for the SkPath version.
// Note input angles are radians.
@@ -749,16 +800,12 @@ CanvasKit.onRuntimeInitialized = function() {
CanvasKit.SkPath.prototype.stroke = function(opts) {
// Fill out any missing values with the default values.
- /**
- * See externs.js for this definition
- * @type {StrokeOpts}
- */
opts = opts || {};
- opts.width = opts.width || 1;
- opts.miter_limit = opts.miter_limit || 4;
- opts.cap = opts.cap || CanvasKit.StrokeCap.Butt;
- opts.join = opts.join || CanvasKit.StrokeJoin.Miter;
- opts.precision = opts.precision || 1;
+ opts['width'] = opts['width'] || 1;
+ opts['miter_limit'] = opts['miter_limit'] || 4;
+ opts['cap'] = opts['cap'] || CanvasKit.StrokeCap.Butt;
+ opts['join'] = opts['join'] || CanvasKit.StrokeJoin.Miter;
+ opts['precision'] = opts['precision'] || 1;
if (this._stroke(opts)) {
return this;
}
@@ -807,9 +854,7 @@ CanvasKit.onRuntimeInitialized = function() {
CanvasKit.SkImage.prototype.makeShader = function(xTileMode, yTileMode, localMatrix) {
var localMatrixPtr = copy3x3MatrixToWasm(localMatrix);
- var shader = this._makeShader(xTileMode, yTileMode, localMatrixPtr);
- CanvasKit._free(localMatrixPtr);
- return shader;
+ return this._makeShader(xTileMode, yTileMode, localMatrixPtr);
}
CanvasKit.SkImage.prototype.readPixels = function(imageInfo, srcX, srcY) {
@@ -854,9 +899,8 @@ CanvasKit.onRuntimeInitialized = function() {
// Accepts an array of four numbers in the range of 0-1 representing a 4f color
CanvasKit.SkCanvas.prototype.clear = function (color4f) {
- var cPtr = copy1dArray(color4f, CanvasKit.HEAPF32);
+ var cPtr = copyColorToWasm(color4f);
this._clear(cPtr);
- CanvasKit._free(cPtr);
}
// concat takes a 3x2, a 3x3, or a 4x4 matrix and upscales it (if needed) to 4x4. This is because
@@ -864,23 +908,30 @@ CanvasKit.onRuntimeInitialized = function() {
CanvasKit.SkCanvas.prototype.concat = function(matr) {
var matrPtr = copy4x4MatrixToWasm(matr);
this._concat(matrPtr);
- CanvasKit._free(matrPtr);
}
// Deprecated - just use concat
CanvasKit.SkCanvas.prototype.concat44 = CanvasKit.SkCanvas.prototype.concat;
// atlas is an SkImage, e.g. from CanvasKit.MakeImageFromEncoded
- // srcRects and dstXforms should be CanvasKit.SkRectBuilder and CanvasKit.RSXFormBuilder
- // or just arrays of floats in groups of 4.
- // colors, if provided, should be a CanvasKit.SkColorBuilder or array of float colors (arrays of 4 floats)
+ // srcRects, dstXforms, and colors should be CanvasKit.SkRectBuilder, CanvasKit.RSXFormBuilder,
+ // and CanvasKit.SkColorBuilder (fastest)
+ // Or they can be an array of floats of length 4*number of destinations.
+ // colors are optional and used to tint the drawn images using the optional blend mode
+ // Colors may be an SkColorBuilder, a Uint32Array of int colors,
+ // a Flat Float32Array of float colors or a 2d Array of Float32Array(4) (deprecated)
CanvasKit.SkCanvas.prototype.drawAtlas = function(atlas, srcRects, dstXforms, paint,
/*optional*/ blendMode, colors) {
if (!atlas || !paint || !srcRects || !dstXforms) {
SkDebug('Doing nothing since missing a required input');
return;
}
- if (srcRects.length !== dstXforms.length || (colors && colors.length !== dstXforms.length)) {
+
+ // builder arguments report the length as the number of rects, but when passed as arrays
+ // their.length attribute is 4x higher because it's the number of total components of all rects.
+ // colors is always going to report the same length, at least until floats colors are supported
+ // by this function.
+ if (srcRects.length !== dstXforms.length) {
SkDebug('Doing nothing since input arrays length mismatches');
return;
}
@@ -892,14 +943,17 @@ CanvasKit.onRuntimeInitialized = function() {
if (srcRects.build) {
srcRectPtr = srcRects.build();
} else {
- srcRectPtr = copy1dArray(srcRects, CanvasKit.HEAPF32);
+ srcRectPtr = copy1dArray(srcRects, "HEAPF32");
}
+ var count = 1;
var dstXformPtr;
if (dstXforms.build) {
dstXformPtr = dstXforms.build();
+ count = dstXforms.length;
} else {
- dstXformPtr = copy1dArray(dstXforms, CanvasKit.HEAPF32);
+ dstXformPtr = copy1dArray(dstXforms, "HEAPF32");
+ count = dstXforms.length / 4;
}
var colorPtr = nullptr;
@@ -907,40 +961,39 @@ CanvasKit.onRuntimeInitialized = function() {
if (colors.build) {
colorPtr = colors.build();
} else {
- if (!isCanvasKitColor(colors[0])) {
- SkDebug('DrawAtlas color argument expected to be CanvasKit.SkRectBuilder or array of ' +
- 'float arrays, but got '+colors);
- return;
- }
- // convert here
- colors = colors.map(toUint32Color);
- colorPtr = copy1dArray(colors, CanvasKit.HEAPU32);
+ colorPtr = copy1dArray(assureIntColors(colors), "HEAPU32");
}
}
- this._drawAtlas(atlas, dstXformPtr, srcRectPtr, colorPtr, dstXforms.length,
- blendMode, paint);
+ this._drawAtlas(atlas, dstXformPtr, srcRectPtr, colorPtr, count, blendMode, paint);
if (srcRectPtr && !srcRects.build) {
- CanvasKit._free(srcRectPtr);
+ freeArraysThatAreNotMallocedByUsers(srcRectPtr, srcRects);
}
if (dstXformPtr && !dstXforms.build) {
- CanvasKit._free(dstXformPtr);
+ freeArraysThatAreNotMallocedByUsers(dstXformPtr, dstXforms);
}
if (colorPtr && !colors.build) {
- CanvasKit._free(colorPtr);
+ freeArraysThatAreNotMallocedByUsers(colorPtr, colors);
}
-
}
CanvasKit.SkCanvas.prototype.drawColor = function (color4f, mode) {
- var cPtr = copy1dArray(color4f, CanvasKit.HEAPF32);
+ var cPtr = copyColorToWasm(color4f);
+ if (mode !== undefined) {
+ this._drawColor(cPtr, mode);
+ } else {
+ this._drawColor(cPtr);
+ }
+ }
+
+ CanvasKit.SkCanvas.prototype.drawColorComponents = function (r, g, b, a, mode) {
+ var cPtr = copyColorComponentsToWasm(r, g, b, a);
if (mode !== undefined) {
this._drawColor(cPtr, mode);
} else {
this._drawColor(cPtr);
}
- CanvasKit._free(cPtr);
}
// points is either an array of [x, y] where x and y are numbers or
@@ -955,63 +1008,64 @@ CanvasKit.onRuntimeInitialized = function() {
ptr = points.byteOffset;
n = points.length/2;
} else {
- ptr = copy2dArray(points, CanvasKit.HEAPF32);
+ ptr = copy2dArray(points, "HEAPF32");
n = points.length;
}
this._drawPoints(mode, ptr, n, paint);
- CanvasKit._free(ptr);
+ freeArraysThatAreNotMallocedByUsers(ptr, points);
}
CanvasKit.SkCanvas.prototype.drawShadow = function(path, zPlaneParams, lightPos, lightRadius, ambientColor, spotColor, flags) {
- var ambiPtr = copy1dArray(ambientColor, CanvasKit.HEAPF32);
- var spotPtr = copy1dArray(spotColor, CanvasKit.HEAPF32);
+ var ambiPtr = copyColorToWasmNoScratch(ambientColor);
+ var spotPtr = copyColorToWasmNoScratch(spotColor);
this._drawShadow(path, zPlaneParams, lightPos, lightRadius, ambiPtr, spotPtr, flags);
- CanvasKit._free(ambiPtr);
- CanvasKit._free(spotPtr);
+ freeArraysThatAreNotMallocedByUsers(ambiPtr, ambientColor);
+ freeArraysThatAreNotMallocedByUsers(spotPtr, spotColor);
}
// getLocalToDevice returns a 4x4 matrix.
CanvasKit.SkCanvas.prototype.getLocalToDevice = function() {
- var matrPtr = CanvasKit._malloc(16 * 4); // allocate space for the matrix
// _getLocalToDevice will copy the values into the pointer.
- this._getLocalToDevice(matrPtr);
- return copy4x4MatrixFromWasm(matrPtr);
+ this._getLocalToDevice(_scratch4x4MatrixPtr);
+ return copy4x4MatrixFromWasm(_scratch4x4MatrixPtr);
}
// findMarkedCTM returns a 4x4 matrix, or null if a matrix was not found at
// the provided marker.
CanvasKit.SkCanvas.prototype.findMarkedCTM = function(marker) {
- var matrPtr = CanvasKit._malloc(16 * 4); // allocate space for the matrix
// _getLocalToDevice will copy the values into the pointer.
- var found = this._findMarkedCTM(marker, matrPtr);
+ var found = this._findMarkedCTM(marker, _scratch4x4MatrixPtr);
if (!found) {
return null;
}
- return copy4x4MatrixFromWasm(matrPtr);
+ return copy4x4MatrixFromWasm(_scratch4x4MatrixPtr);
}
// getTotalMatrix returns the current matrix as a 3x3 matrix.
CanvasKit.SkCanvas.prototype.getTotalMatrix = function() {
- var matrPtr = CanvasKit._malloc(9 * 4); // allocate space for the matrix
// _getTotalMatrix will copy the values into the pointer.
- this._getTotalMatrix(matrPtr);
+ this._getTotalMatrix(_scratch3x3MatrixPtr);
// read them out into an array. TODO(kjlubick): If we change SkMatrix to be
// typedArrays, then we should return a typed array here too.
var rv = new Array(9);
for (var i = 0; i < 9; i++) {
- rv[i] = CanvasKit.HEAPF32[matrPtr/4 + i]; // divide by 4 to "cast" to float.
+ rv[i] = CanvasKit.HEAPF32[_scratch3x3MatrixPtr/4 + i]; // divide by 4 to "cast" to float.
}
- CanvasKit._free(matrPtr);
return rv;
}
// returns Uint8Array
CanvasKit.SkCanvas.prototype.readPixels = function(x, y, w, h, alphaType,
- colorType, dstRowBytes) {
+ colorType, colorSpace, dstRowBytes) {
// supply defaults (which are compatible with HTMLCanvas's getImageData)
alphaType = alphaType || CanvasKit.AlphaType.Unpremul;
colorType = colorType || CanvasKit.ColorType.RGBA_8888;
- dstRowBytes = dstRowBytes || (4 * w);
+ colorSpace = colorSpace || CanvasKit.SkColorSpace.SRGB;
+ var pixBytes = 4;
+ if (colorType === CanvasKit.ColorType.RGBA_F16) {
+ pixBytes = 8;
+ }
+ dstRowBytes = dstRowBytes || (pixBytes * w);
var len = h * dstRowBytes
var pptr = CanvasKit._malloc(len);
@@ -1020,6 +1074,7 @@ CanvasKit.onRuntimeInitialized = function() {
'height': h,
'colorType': colorType,
'alphaType': alphaType,
+ 'colorSpace': colorSpace,
}, pptr, dstRowBytes, x, y);
if (!ok) {
CanvasKit._free(pptr);
@@ -1033,10 +1088,9 @@ CanvasKit.onRuntimeInitialized = function() {
return pixels;
}
- // pixels is a TypedArray. No matter the input size, it will be treated as
- // a Uint8Array (essentially, a byte array).
+ // pixels should be a Uint8Array or a plain JS array.
CanvasKit.SkCanvas.prototype.writePixels = function(pixels, srcWidth, srcHeight,
- destX, destY, alphaType, colorType) {
+ destX, destY, alphaType, colorType, colorSpace) {
if (pixels.byteLength % (srcWidth * srcHeight)) {
throw 'pixels length must be a multiple of the srcWidth * srcHeight';
}
@@ -1044,26 +1098,25 @@ CanvasKit.onRuntimeInitialized = function() {
// supply defaults (which are compatible with HTMLCanvas's putImageData)
alphaType = alphaType || CanvasKit.AlphaType.Unpremul;
colorType = colorType || CanvasKit.ColorType.RGBA_8888;
+ colorSpace = colorSpace || CanvasKit.SkColorSpace.SRGB;
var srcRowBytes = bytesPerPixel * srcWidth;
- var pptr = CanvasKit._malloc(pixels.byteLength);
- CanvasKit.HEAPU8.set(pixels, pptr);
-
+ var pptr = copy1dArray(pixels, "HEAPU8");
var ok = this._writePixels({
'width': srcWidth,
'height': srcHeight,
'colorType': colorType,
'alphaType': alphaType,
+ 'colorSpace': colorSpace,
}, pptr, srcRowBytes, destX, destY);
- CanvasKit._free(pptr);
+ freeArraysThatAreNotMallocedByUsers(pptr, pixels);
return ok;
}
CanvasKit.SkColorFilter.MakeBlend = function(color4f, mode) {
- var cPtr = copy1dArray(color4f, CanvasKit.HEAPF32);
+ var cPtr = copyColorToWasm(color4f);
var result = CanvasKit.SkColorFilter._MakeBlend(cPtr, mode);
- CanvasKit._free(cPtr);
return result;
}
@@ -1072,31 +1125,38 @@ CanvasKit.onRuntimeInitialized = function() {
if (!colorMatrix || colorMatrix.length !== 20) {
throw 'invalid color matrix';
}
- var fptr = copy1dArray(colorMatrix, CanvasKit.HEAPF32);
+ var fptr = copy1dArray(colorMatrix, "HEAPF32");
// We know skia memcopies the floats, so we can free our memory after the call returns.
var m = CanvasKit.SkColorFilter._makeMatrix(fptr);
- CanvasKit._free(fptr);
+ freeArraysThatAreNotMallocedByUsers(fptr, colorMatrix);
return m;
}
CanvasKit.SkImageFilter.MakeMatrixTransform = function(matr, filterQuality, input) {
var matrPtr = copy3x3MatrixToWasm(matr);
- var imgF = CanvasKit.SkImageFilter._MakeMatrixTransform(matrPtr, filterQuality, input);
-
- CanvasKit._free(matrPtr);
- return imgF;
+ return CanvasKit.SkImageFilter._MakeMatrixTransform(matrPtr, filterQuality, input);
}
CanvasKit.SkPaint.prototype.getColor = function() {
- var cPtr = CanvasKit._malloc(16); // 4 floats, 4 bytes each
- this._getColor(cPtr);
- return copyColorFromWasm(cPtr);
+ this._getColor(_scratchColorPtr);
+ return copyColorFromWasm(_scratchColorPtr);
}
- CanvasKit.SkPaint.prototype.setColor = function(color4f) {
- var cPtr = copy1dArray(color4f, CanvasKit.HEAPF32);
- this._setColor(cPtr);
- CanvasKit._free(cPtr);
+ CanvasKit.SkPaint.prototype.setColor = function(color4f, colorSpace) {
+ colorSpace = colorSpace || null; // null will be replaced with sRGB in the C++ method.
+ // emscripten wouldn't bind undefined to the sk_sp<SkColorSpace> expected here.
+ var cPtr = copyColorToWasm(color4f);
+ this._setColor(cPtr, colorSpace);
+ }
+
+ // The color components here are expected to be floating point values (nominally between
+ // 0.0 and 1.0, but with wider color gamuts, the values could exceed this range). To convert
+ // between standard 8 bit colors and floats, just divide by 255 before passing them in.
+ CanvasKit.SkPaint.prototype.setColorComponents = function(r, g, b, a, colorSpace) {
+ colorSpace = colorSpace || null; // null will be replaced with sRGB in the C++ method.
+ // emscripten wouldn't bind undefined to the sk_sp<SkColorSpace> expected here.
+ var cPtr = copyColorComponentsToWasm(r, g, b, a);
+ this._setColor(cPtr, colorSpace);
}
CanvasKit.SkSurface.prototype.captureFrameAsSkPicture = function(drawFrame) {
@@ -1126,7 +1186,7 @@ CanvasKit.onRuntimeInitialized = function() {
// We do not dispose() of the SkSurface here, as the client will typically
// call requestAnimationFrame again from within the supplied callback.
// For drawing a single frame, prefer drawOnce().
- this.flush();
+ this.flush(dirtyRect);
}.bind(this));
}
@@ -1142,7 +1202,7 @@ CanvasKit.onRuntimeInitialized = function() {
}
callback(this._cached_canvas);
- this.flush();
+ this.flush(dirtyRect);
this.dispose();
}.bind(this));
}
@@ -1154,82 +1214,82 @@ CanvasKit.onRuntimeInitialized = function() {
if (!intervals.length || intervals.length % 2 === 1) {
throw 'Intervals array must have even length';
}
- var ptr = copy1dArray(intervals, CanvasKit.HEAPF32);
+ var ptr = copy1dArray(intervals, "HEAPF32");
var dpe = CanvasKit.SkPathEffect._MakeDash(ptr, intervals.length, phase);
- CanvasKit._free(ptr);
+ freeArraysThatAreNotMallocedByUsers(ptr, intervals);
return dpe;
}
- CanvasKit.SkShader.Color = function(color4f) {
- var cPtr = copy1dArray(color4f, CanvasKit.HEAPF32);
- var result = CanvasKit.SkShader._Color(cPtr);
- CanvasKit._free(cPtr);
+ CanvasKit.SkShader.Color = function(color4f, colorSpace) {
+ colorSpace = colorSpace || null
+ var cPtr = copyColorToWasm(color4f);
+ var result = CanvasKit.SkShader._Color(cPtr, colorSpace);
return result;
}
- CanvasKit.SkShader.MakeLinearGradient = function(start, end, colors, pos, mode, localMatrix, flags) {
- var colorPtr = copy2dArray(colors, CanvasKit.HEAPF32);
- var posPtr = copy1dArray(pos, CanvasKit.HEAPF32);
+ CanvasKit.SkShader.MakeLinearGradient = function(start, end, colors, pos, mode, localMatrix, flags, colorSpace) {
+ colorSpace = colorSpace || null
+ var cPtrInfo = copyFlexibleColorArray(colors);
+ var posPtr = copy1dArray(pos, "HEAPF32");
flags = flags || 0;
var localMatrixPtr = copy3x3MatrixToWasm(localMatrix);
- var lgs = CanvasKit._MakeLinearGradientShader(start, end, colorPtr, posPtr,
- colors.length, mode, flags, localMatrixPtr);
+ var lgs = CanvasKit._MakeLinearGradientShader(start, end, cPtrInfo.colorPtr, cPtrInfo.colorType, posPtr,
+ cPtrInfo.count, mode, flags, localMatrixPtr, colorSpace);
- CanvasKit._free(localMatrixPtr);
- CanvasKit._free(colorPtr);
- CanvasKit._free(posPtr);
+ freeArraysThatAreNotMallocedByUsers(cPtrInfo.colorPtr, colors);
+ pos && freeArraysThatAreNotMallocedByUsers(posPtr, pos);
return lgs;
}
- CanvasKit.SkShader.MakeRadialGradient = function(center, radius, colors, pos, mode, localMatrix, flags) {
- var colorPtr = copy2dArray(colors, CanvasKit.HEAPF32);
- var posPtr = copy1dArray(pos, CanvasKit.HEAPF32);
+ CanvasKit.SkShader.MakeRadialGradient = function(center, radius, colors, pos, mode, localMatrix, flags, colorSpace) {
+ colorSpace = colorSpace || null
+ var cPtrInfo = copyFlexibleColorArray(colors);
+ var posPtr = copy1dArray(pos, "HEAPF32");
flags = flags || 0;
var localMatrixPtr = copy3x3MatrixToWasm(localMatrix);
- var rgs = CanvasKit._MakeRadialGradientShader(center, radius, colorPtr, posPtr,
- colors.length, mode, flags, localMatrixPtr);
+ var rgs = CanvasKit._MakeRadialGradientShader(center, radius, cPtrInfo.colorPtr, cPtrInfo.colorType, posPtr,
+ cPtrInfo.count, mode, flags, localMatrixPtr, colorSpace);
- CanvasKit._free(localMatrixPtr);
- CanvasKit._free(colorPtr);
- CanvasKit._free(posPtr);
+ freeArraysThatAreNotMallocedByUsers(cPtrInfo.colorPtr, colors);
+ pos && freeArraysThatAreNotMallocedByUsers(posPtr, pos);
return rgs;
}
- CanvasKit.SkShader.MakeSweepGradient = function(cx, cy, colors, pos, mode, localMatrix, flags, startAngle, endAngle) {
- var colorPtr = copy2dArray(colors, CanvasKit.HEAPF32);
- var posPtr = copy1dArray(pos, CanvasKit.HEAPF32);
+ CanvasKit.SkShader.MakeSweepGradient = function(cx, cy, colors, pos, mode, localMatrix, flags, startAngle, endAngle, colorSpace) {
+ colorSpace = colorSpace || null
+ var cPtrInfo = copyFlexibleColorArray(colors);
+ var posPtr = copy1dArray(pos, "HEAPF32");
flags = flags || 0;
startAngle = startAngle || 0;
endAngle = endAngle || 360;
var localMatrixPtr = copy3x3MatrixToWasm(localMatrix);
- var sgs = CanvasKit._MakeSweepGradientShader(cx, cy, colorPtr, posPtr,
- colors.length, mode,
+ var sgs = CanvasKit._MakeSweepGradientShader(cx, cy, cPtrInfo.colorPtr, cPtrInfo.colorType, posPtr,
+ cPtrInfo.count, mode,
startAngle, endAngle, flags,
- localMatrixPtr);
+ localMatrixPtr, colorSpace);
- CanvasKit._free(localMatrixPtr);
- CanvasKit._free(colorPtr);
- CanvasKit._free(posPtr);
+ freeArraysThatAreNotMallocedByUsers(cPtrInfo.colorPtr, colors);
+ pos && freeArraysThatAreNotMallocedByUsers(posPtr, pos);
return sgs;
}
CanvasKit.SkShader.MakeTwoPointConicalGradient = function(start, startRadius, end, endRadius,
- colors, pos, mode, localMatrix, flags) {
- var colorPtr = copy2dArray(colors, CanvasKit.HEAPF32);
- var posPtr = copy1dArray(pos, CanvasKit.HEAPF32);
+ colors, pos, mode, localMatrix, flags, colorSpace) {
+ colorSpace = colorSpace || null
+ var cPtrInfo = copyFlexibleColorArray(colors);
+ var posPtr = copy1dArray(pos, "HEAPF32");
flags = flags || 0;
var localMatrixPtr = copy3x3MatrixToWasm(localMatrix);
var rgs = CanvasKit._MakeTwoPointConicalGradientShader(
- start, startRadius, end, endRadius,
- colorPtr, posPtr, colors.length, mode, flags, localMatrixPtr);
+ start, startRadius, end, endRadius, cPtrInfo.colorPtr, cPtrInfo.colorType,
+ posPtr, cPtrInfo.count, mode, flags, localMatrixPtr, colorSpace);
- CanvasKit._free(localMatrixPtr);
- CanvasKit._free(colorPtr);
- CanvasKit._free(posPtr);
+ freeArraysThatAreNotMallocedByUsers(cPtrInfo.colorPtr, colors);
+ pos && freeArraysThatAreNotMallocedByUsers(posPtr, pos);
return rgs;
}
@@ -1252,17 +1312,25 @@ CanvasKit.onRuntimeInitialized = function() {
// ambient: {r, g, b, a},
// spot: {r, g, b, a},
// }
-// Returns the same format
+// Returns the same format. Note, if malloced colors are passed in, the memory
+// housing the passed in colors passed in will be overwritten with the computed
+// tonal colors.
CanvasKit.computeTonalColors = function(tonalColors) {
- var cPtrAmbi = copy1dArray(tonalColors['ambient'], CanvasKit.HEAPF32);
- var cPtrSpot = copy1dArray(tonalColors['spot'], CanvasKit.HEAPF32);
+ // copy the colors into WASM
+ var cPtrAmbi = copyColorToWasmNoScratch(tonalColors['ambient']);
+ var cPtrSpot = copyColorToWasmNoScratch(tonalColors['spot']);
+ // The output of this function will be the same pointers we passed in.
this._computeTonalColors(cPtrAmbi, cPtrSpot);
+ // Read the results out.
var result = {
'ambient': copyColorFromWasm(cPtrAmbi),
'spot': copyColorFromWasm(cPtrSpot),
- }
+ };
+ // If the user passed us malloced colors in here, we don't want to clean them up.
+ freeArraysThatAreNotMallocedByUsers(cPtrAmbi, tonalColors['ambient']);
+ freeArraysThatAreNotMallocedByUsers(cPtrSpot, tonalColors['spot']);
return result;
-}
+};
CanvasKit.LTRBRect = function(l, t, r, b) {
return {
@@ -1271,7 +1339,7 @@ CanvasKit.LTRBRect = function(l, t, r, b) {
fRight: r,
fBottom: b,
};
-}
+};
CanvasKit.XYWHRect = function(x, y, w, h) {
return {
@@ -1280,7 +1348,7 @@ CanvasKit.XYWHRect = function(x, y, w, h) {
fRight: x+w,
fBottom: y+h,
};
-}
+};
// RRectXY returns an RRect with the given rect and a radiusX and radiusY for
// all 4 corners.
@@ -1296,14 +1364,7 @@ CanvasKit.RRectXY = function(rect, rx, ry) {
rx4: rx,
ry4: ry,
};
-}
-
-CanvasKit.MakePathFromCmds = function(cmds) {
- var ptrLen = loadCmdsTypedArray(cmds);
- var path = CanvasKit._MakePathFromCmds(ptrLen[0], ptrLen[1]);
- CanvasKit._free(ptrLen[0]);
- return path;
-}
+};
// data is a TypedArray or ArrayBuffer e.g. from fetch().then(resp.arrayBuffer())
CanvasKit.MakeAnimatedImageFromEncoded = function(data) {
@@ -1333,23 +1394,61 @@ CanvasKit.MakeImageFromEncoded = function(data) {
return img;
}
-// pixels must be a Uint8Array with bytes representing the pixel values
+// A variable to hold a canvasElement which can be reused once created the first time.
+var memoizedCanvas2dElement = null;
+
+// Alternative to CanvasKit.MakeImageFromEncoded. Allows for CanvasKit users to take advantage of
+// browser APIs to decode images instead of using codecs included in the CanvasKit wasm binary.
+// Expects that the canvasImageSource has already loaded/decoded.
+// CanvasImageSource reference: https://developer.mozilla.org/en-US/docs/Web/API/CanvasImageSource
+CanvasKit.MakeImageFromCanvasImageSource = function(canvasImageSource) {
+ var width = canvasImageSource.width;
+ var height = canvasImageSource.height;
+
+ if (!memoizedCanvas2dElement) {
+ memoizedCanvas2dElement = document.createElement('canvas');
+ }
+ memoizedCanvas2dElement.width = width;
+ memoizedCanvas2dElement.height = height;
+
+ var ctx2d = memoizedCanvas2dElement.getContext('2d');
+ ctx2d.drawImage(canvasImageSource, 0, 0);
+
+ var imageData = ctx2d.getImageData(0, 0, width, height);
+
+ return CanvasKit.MakeImage(
+ imageData.data,
+ width,
+ height,
+ CanvasKit.AlphaType.Unpremul,
+ CanvasKit.ColorType.RGBA_8888,
+ CanvasKit.SkColorSpace.SRGB
+ );
+}
+
+// pixels may be any Typed Array, but Uint8Array or Uint8ClampedArray is recommended,
+// with bytes representing the pixel values.
// (e.g. each set of 4 bytes could represent RGBA values for a single pixel).
-CanvasKit.MakeImage = function(pixels, width, height, alphaType, colorType) {
+CanvasKit.MakeImage = function(pixels, width, height, alphaType, colorType, colorSpace) {
var bytesPerPixel = pixels.length / (width * height);
var info = {
'width': width,
'height': height,
'alphaType': alphaType,
'colorType': colorType,
+ 'colorSpace': colorSpace,
};
- var pptr = copy1dArray(pixels, CanvasKit.HEAPU8);
+ var pptr = copy1dArray(pixels, "HEAPU8");
// No need to _free pptr, Image takes it with SkData::MakeFromMalloc
return CanvasKit._MakeImage(info, pptr, pixels.length, width * bytesPerPixel);
}
-// colors is an array of float color arrays
+// Colors may be a Uint32Array of int colors, a Flat Float32Array of float colors
+// or a 2d Array of Float32Array(4) (deprecated)
+// the underlying skia function accepts only int colors so it is recommended
+// to pass an array of int colors to avoid an extra conversion.
+// SkColorBuilder is not accepted.
CanvasKit.MakeSkVertices = function(mode, positions, textureCoordinates, colors,
indices, isVolatile) {
// Default isVolitile to true if not set
@@ -1368,21 +1467,23 @@ CanvasKit.MakeSkVertices = function(mode, positions, textureCoordinates, colors,
flags |= (1 << 2);
}
- var builder = new CanvasKit._SkVerticesBuilder(mode, positions.length, idxCount, flags);
+ var builder = new CanvasKit._SkVerticesBuilder(mode, positions.length, idxCount, flags);
- copy2dArray(positions, CanvasKit.HEAPF32, builder.positions());
+ copy2dArray(positions, "HEAPF32", builder.positions());
if (builder.texCoords()) {
- copy2dArray(textureCoordinates, CanvasKit.HEAPF32, builder.texCoords());
+ copy2dArray(textureCoordinates, "HEAPF32", builder.texCoords());
}
if (builder.colors()) {
- // Convert from canvaskit 4f colors to 32 bit uint colors which builder supports.
- copy1dArray(colors.map(toUint32Color), CanvasKit.HEAPU32, builder.colors());
+ if (colors.build) {
+ throw('Color builder not accepted by MakeSkVertices, use array of ints');
+ } else {
+ copy1dArray(assureIntColors(colors), "HEAPU32", builder.colors());
+ }
}
if (builder.indices()) {
- copy1dArray(indices, CanvasKit.HEAPU16, builder.indices());
+ copy1dArray(indices, "HEAPU16", builder.indices());
}
- var idxCount = (indices && indices.length) || 0;
// Create the vertices, which owns the memory that the builder had allocated.
return builder.detach();
};
diff --git a/chromium/third_party/skia/modules/canvaskit/karma.conf.js b/chromium/third_party/skia/modules/canvaskit/karma.conf.js
index dd059443682..962176eefc5 100644
--- a/chromium/third_party/skia/modules/canvaskit/karma.conf.js
+++ b/chromium/third_party/skia/modules/canvaskit/karma.conf.js
@@ -57,7 +57,7 @@ module.exports = function(config) {
concurrency: Infinity,
};
- if (isDocker) {
+ if (isDocker || config.headless) {
// See https://hackernoon.com/running-karma-tests-with-headless-chrome-inside-docker-ae4aceb06ed3
cfg.browsers = ['ChromeHeadlessNoSandbox'],
cfg.customLaunchers = {
@@ -76,6 +76,17 @@ module.exports = function(config) {
],
},
};
+ } else {
+ // Extra options that should only be applied locally
+
+ // Measure test coverage and write output to coverage/ directory
+ cfg.reporters.push('coverage');
+ cfg.preprocessors = {
+ // Measure test coverage of these source files
+ // Since this file is a combination of our code, and emscripten's glue,
+ // we'll never see 100% coverage, but this lets us measure improvements.
+ 'canvaskit/bin/canvaskit.js': ['coverage'],
+ };
}
config.set(cfg);
diff --git a/chromium/third_party/skia/modules/canvaskit/package-lock.json b/chromium/third_party/skia/modules/canvaskit/package-lock.json
new file mode 100644
index 00000000000..5fc95f855b7
--- /dev/null
+++ b/chromium/third_party/skia/modules/canvaskit/package-lock.json
@@ -0,0 +1,3494 @@
+{
+ "name": "canvaskit-local",
+ "version": "0.0.0",
+ "lockfileVersion": 1,
+ "requires": true,
+ "dependencies": {
+ "@babel/code-frame": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz",
+ "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==",
+ "dev": true,
+ "requires": {
+ "@babel/highlight": "^7.8.3"
+ }
+ },
+ "@babel/core": {
+ "version": "7.9.6",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.9.6.tgz",
+ "integrity": "sha512-nD3deLvbsApbHAHttzIssYqgb883yU/d9roe4RZymBCDaZryMJDbptVpEpeQuRh4BJ+SYI8le9YGxKvFEvl1Wg==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.8.3",
+ "@babel/generator": "^7.9.6",
+ "@babel/helper-module-transforms": "^7.9.0",
+ "@babel/helpers": "^7.9.6",
+ "@babel/parser": "^7.9.6",
+ "@babel/template": "^7.8.6",
+ "@babel/traverse": "^7.9.6",
+ "@babel/types": "^7.9.6",
+ "convert-source-map": "^1.7.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.1",
+ "json5": "^2.1.2",
+ "lodash": "^4.17.13",
+ "resolve": "^1.3.2",
+ "semver": "^5.4.1",
+ "source-map": "^0.5.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ },
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+ "dev": true
+ }
+ }
+ },
+ "@babel/generator": {
+ "version": "7.9.6",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.9.6.tgz",
+ "integrity": "sha512-+htwWKJbH2bL72HRluF8zumBxzuX0ZZUFl3JLNyoUjM/Ho8wnVpPXM6aUz8cfKDqQ/h7zHqKt4xzJteUosckqQ==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.9.6",
+ "jsesc": "^2.5.1",
+ "lodash": "^4.17.13",
+ "source-map": "^0.5.0"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+ "dev": true
+ }
+ }
+ },
+ "@babel/helper-function-name": {
+ "version": "7.9.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.9.5.tgz",
+ "integrity": "sha512-JVcQZeXM59Cd1qanDUxv9fgJpt3NeKUaqBqUEvfmQ+BCOKq2xUgaWZW2hr0dkbyJgezYuplEoh5knmrnS68efw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-get-function-arity": "^7.8.3",
+ "@babel/template": "^7.8.3",
+ "@babel/types": "^7.9.5"
+ }
+ },
+ "@babel/helper-get-function-arity": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz",
+ "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.8.3"
+ }
+ },
+ "@babel/helper-member-expression-to-functions": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz",
+ "integrity": "sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.8.3"
+ }
+ },
+ "@babel/helper-module-imports": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz",
+ "integrity": "sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.8.3"
+ }
+ },
+ "@babel/helper-module-transforms": {
+ "version": "7.9.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.9.0.tgz",
+ "integrity": "sha512-0FvKyu0gpPfIQ8EkxlrAydOWROdHpBmiCiRwLkUiBGhCUPRRbVD2/tm3sFr/c/GWFrQ/ffutGUAnx7V0FzT2wA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-module-imports": "^7.8.3",
+ "@babel/helper-replace-supers": "^7.8.6",
+ "@babel/helper-simple-access": "^7.8.3",
+ "@babel/helper-split-export-declaration": "^7.8.3",
+ "@babel/template": "^7.8.6",
+ "@babel/types": "^7.9.0",
+ "lodash": "^4.17.13"
+ }
+ },
+ "@babel/helper-optimise-call-expression": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz",
+ "integrity": "sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.8.3"
+ }
+ },
+ "@babel/helper-replace-supers": {
+ "version": "7.9.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.9.6.tgz",
+ "integrity": "sha512-qX+chbxkbArLyCImk3bWV+jB5gTNU/rsze+JlcF6Nf8tVTigPJSI1o1oBow/9Resa1yehUO9lIipsmu9oG4RzA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-member-expression-to-functions": "^7.8.3",
+ "@babel/helper-optimise-call-expression": "^7.8.3",
+ "@babel/traverse": "^7.9.6",
+ "@babel/types": "^7.9.6"
+ }
+ },
+ "@babel/helper-simple-access": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.8.3.tgz",
+ "integrity": "sha512-VNGUDjx5cCWg4vvCTR8qQ7YJYZ+HBjxOgXEl7ounz+4Sn7+LMD3CFrCTEU6/qXKbA2nKg21CwhhBzO0RpRbdCw==",
+ "dev": true,
+ "requires": {
+ "@babel/template": "^7.8.3",
+ "@babel/types": "^7.8.3"
+ }
+ },
+ "@babel/helper-split-export-declaration": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz",
+ "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.8.3"
+ }
+ },
+ "@babel/helper-validator-identifier": {
+ "version": "7.9.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz",
+ "integrity": "sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g==",
+ "dev": true
+ },
+ "@babel/helpers": {
+ "version": "7.9.6",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.9.6.tgz",
+ "integrity": "sha512-tI4bUbldloLcHWoRUMAj4g1bF313M/o6fBKhIsb3QnGVPwRm9JsNf/gqMkQ7zjqReABiffPV6RWj7hEglID5Iw==",
+ "dev": true,
+ "requires": {
+ "@babel/template": "^7.8.3",
+ "@babel/traverse": "^7.9.6",
+ "@babel/types": "^7.9.6"
+ }
+ },
+ "@babel/highlight": {
+ "version": "7.9.0",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz",
+ "integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.9.0",
+ "chalk": "^2.0.0",
+ "js-tokens": "^4.0.0"
+ }
+ },
+ "@babel/parser": {
+ "version": "7.9.6",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.6.tgz",
+ "integrity": "sha512-AoeIEJn8vt+d/6+PXDRPaksYhnlbMIiejioBZvvMQsOjW/JYK6k/0dKnvvP3EhK5GfMBWDPtrxRtegWdAcdq9Q==",
+ "dev": true
+ },
+ "@babel/template": {
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz",
+ "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.8.3",
+ "@babel/parser": "^7.8.6",
+ "@babel/types": "^7.8.6"
+ }
+ },
+ "@babel/traverse": {
+ "version": "7.9.6",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.9.6.tgz",
+ "integrity": "sha512-b3rAHSjbxy6VEAvlxM8OV/0X4XrG72zoxme6q1MOoe2vd0bEc+TwayhuC1+Dfgqh1QEG+pj7atQqvUprHIccsg==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.8.3",
+ "@babel/generator": "^7.9.6",
+ "@babel/helper-function-name": "^7.9.5",
+ "@babel/helper-split-export-declaration": "^7.8.3",
+ "@babel/parser": "^7.9.6",
+ "@babel/types": "^7.9.6",
+ "debug": "^4.1.0",
+ "globals": "^11.1.0",
+ "lodash": "^4.17.13"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ }
+ }
+ },
+ "@babel/types": {
+ "version": "7.9.6",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.9.6.tgz",
+ "integrity": "sha512-qxXzvBO//jO9ZnoasKF1uJzHd2+M6Q2ZPIVfnFps8JJvXy0ZBbwbNOmE6SGIY5XOY6d1Bo5lb9d9RJ8nv3WSeA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.9.5",
+ "lodash": "^4.17.13",
+ "to-fast-properties": "^2.0.0"
+ }
+ },
+ "@istanbuljs/schema": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz",
+ "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==",
+ "dev": true
+ },
+ "accepts": {
+ "version": "1.3.7",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
+ "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
+ "dev": true,
+ "requires": {
+ "mime-types": "~2.1.24",
+ "negotiator": "0.6.2"
+ }
+ },
+ "after": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz",
+ "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^1.9.0"
+ }
+ },
+ "anymatch": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
+ "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
+ "dev": true,
+ "requires": {
+ "micromatch": "^3.1.4",
+ "normalize-path": "^2.1.1"
+ },
+ "dependencies": {
+ "normalize-path": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
+ "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
+ "dev": true,
+ "requires": {
+ "remove-trailing-separator": "^1.0.1"
+ }
+ }
+ }
+ },
+ "arr-diff": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
+ "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=",
+ "dev": true
+ },
+ "arr-flatten": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
+ "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==",
+ "dev": true
+ },
+ "arr-union": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
+ "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=",
+ "dev": true
+ },
+ "array-slice": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz",
+ "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=",
+ "dev": true
+ },
+ "array-unique": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
+ "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
+ "dev": true
+ },
+ "arraybuffer.slice": {
+ "version": "0.0.7",
+ "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz",
+ "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==",
+ "dev": true
+ },
+ "assign-symbols": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
+ "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=",
+ "dev": true
+ },
+ "async-each": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz",
+ "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==",
+ "dev": true
+ },
+ "async-limiter": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
+ "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==",
+ "dev": true
+ },
+ "atob": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
+ "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
+ "dev": true
+ },
+ "backo2": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
+ "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=",
+ "dev": true
+ },
+ "balanced-match": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
+ "dev": true
+ },
+ "base": {
+ "version": "0.11.2",
+ "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
+ "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==",
+ "dev": true,
+ "requires": {
+ "cache-base": "^1.0.1",
+ "class-utils": "^0.3.5",
+ "component-emitter": "^1.2.1",
+ "define-property": "^1.0.0",
+ "isobject": "^3.0.1",
+ "mixin-deep": "^1.2.0",
+ "pascalcase": "^0.1.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^1.0.0"
+ }
+ },
+ "is-accessor-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-data-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-descriptor": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^1.0.0",
+ "is-data-descriptor": "^1.0.0",
+ "kind-of": "^6.0.2"
+ }
+ }
+ }
+ },
+ "base64-arraybuffer": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz",
+ "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=",
+ "dev": true
+ },
+ "base64id": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz",
+ "integrity": "sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY=",
+ "dev": true
+ },
+ "better-assert": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz",
+ "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=",
+ "dev": true,
+ "requires": {
+ "callsite": "1.0.0"
+ }
+ },
+ "binary-extensions": {
+ "version": "1.13.1",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz",
+ "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==",
+ "dev": true
+ },
+ "bindings": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
+ "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "file-uri-to-path": "1.0.0"
+ }
+ },
+ "blob": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz",
+ "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==",
+ "dev": true
+ },
+ "bluebird": {
+ "version": "3.7.2",
+ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
+ "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
+ "dev": true
+ },
+ "body-parser": {
+ "version": "1.19.0",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
+ "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
+ "dev": true,
+ "requires": {
+ "bytes": "3.1.0",
+ "content-type": "~1.0.4",
+ "debug": "2.6.9",
+ "depd": "~1.1.2",
+ "http-errors": "1.7.2",
+ "iconv-lite": "0.4.24",
+ "on-finished": "~2.3.0",
+ "qs": "6.7.0",
+ "raw-body": "2.4.0",
+ "type-is": "~1.6.17"
+ }
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "braces": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
+ "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+ "dev": true,
+ "requires": {
+ "arr-flatten": "^1.1.0",
+ "array-unique": "^0.3.2",
+ "extend-shallow": "^2.0.1",
+ "fill-range": "^4.0.0",
+ "isobject": "^3.0.1",
+ "repeat-element": "^1.1.2",
+ "snapdragon": "^0.8.1",
+ "snapdragon-node": "^2.0.1",
+ "split-string": "^3.0.2",
+ "to-regex": "^3.0.1"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
+ }
+ },
+ "buffer-alloc": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz",
+ "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==",
+ "dev": true,
+ "requires": {
+ "buffer-alloc-unsafe": "^1.1.0",
+ "buffer-fill": "^1.0.0"
+ }
+ },
+ "buffer-alloc-unsafe": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz",
+ "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==",
+ "dev": true
+ },
+ "buffer-fill": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz",
+ "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=",
+ "dev": true
+ },
+ "bytes": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
+ "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==",
+ "dev": true
+ },
+ "cache-base": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
+ "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==",
+ "dev": true,
+ "requires": {
+ "collection-visit": "^1.0.0",
+ "component-emitter": "^1.2.1",
+ "get-value": "^2.0.6",
+ "has-value": "^1.0.0",
+ "isobject": "^3.0.1",
+ "set-value": "^2.0.0",
+ "to-object-path": "^0.3.0",
+ "union-value": "^1.0.0",
+ "unset-value": "^1.0.0"
+ }
+ },
+ "callsite": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz",
+ "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=",
+ "dev": true
+ },
+ "chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ }
+ },
+ "chokidar": {
+ "version": "2.1.8",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz",
+ "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==",
+ "dev": true,
+ "requires": {
+ "anymatch": "^2.0.0",
+ "async-each": "^1.0.1",
+ "braces": "^2.3.2",
+ "fsevents": "^1.2.7",
+ "glob-parent": "^3.1.0",
+ "inherits": "^2.0.3",
+ "is-binary-path": "^1.0.0",
+ "is-glob": "^4.0.0",
+ "normalize-path": "^3.0.0",
+ "path-is-absolute": "^1.0.0",
+ "readdirp": "^2.2.1",
+ "upath": "^1.1.1"
+ }
+ },
+ "circular-json": {
+ "version": "0.5.9",
+ "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.5.9.tgz",
+ "integrity": "sha512-4ivwqHpIFJZBuhN3g/pEcdbnGUywkBblloGbkglyloVjjR3uT6tieI89MVOfbP2tHX5sgb01FuLgAOzebNlJNQ==",
+ "dev": true
+ },
+ "class-utils": {
+ "version": "0.3.6",
+ "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
+ "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==",
+ "dev": true,
+ "requires": {
+ "arr-union": "^3.1.0",
+ "define-property": "^0.2.5",
+ "isobject": "^3.0.0",
+ "static-extend": "^0.1.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ }
+ }
+ },
+ "collection-visit": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
+ "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=",
+ "dev": true,
+ "requires": {
+ "map-visit": "^1.0.0",
+ "object-visit": "^1.0.0"
+ }
+ },
+ "color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dev": true,
+ "requires": {
+ "color-name": "1.1.3"
+ }
+ },
+ "color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+ "dev": true
+ },
+ "colors": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
+ "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==",
+ "dev": true
+ },
+ "combine-lists": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/combine-lists/-/combine-lists-1.0.1.tgz",
+ "integrity": "sha1-RYwH4J4NkA/Ci3Cj/sLazR0st/Y=",
+ "dev": true,
+ "requires": {
+ "lodash": "^4.5.0"
+ }
+ },
+ "component-bind": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz",
+ "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=",
+ "dev": true
+ },
+ "component-emitter": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
+ "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==",
+ "dev": true
+ },
+ "component-inherit": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz",
+ "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=",
+ "dev": true
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+ "dev": true
+ },
+ "connect": {
+ "version": "3.7.0",
+ "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz",
+ "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==",
+ "dev": true,
+ "requires": {
+ "debug": "2.6.9",
+ "finalhandler": "1.1.2",
+ "parseurl": "~1.3.3",
+ "utils-merge": "1.0.1"
+ }
+ },
+ "content-type": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
+ "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
+ "dev": true
+ },
+ "convert-source-map": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz",
+ "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.1"
+ },
+ "dependencies": {
+ "safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true
+ }
+ }
+ },
+ "cookie": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
+ "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=",
+ "dev": true
+ },
+ "copy-descriptor": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
+ "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=",
+ "dev": true
+ },
+ "core-js": {
+ "version": "2.6.11",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz",
+ "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==",
+ "dev": true
+ },
+ "core-util-is": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
+ "dev": true
+ },
+ "custom-event": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz",
+ "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=",
+ "dev": true
+ },
+ "date-format": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/date-format/-/date-format-1.2.0.tgz",
+ "integrity": "sha1-YV6CjiM90aubua4JUODOzPpuytg=",
+ "dev": true
+ },
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "decode-uri-component": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
+ "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=",
+ "dev": true
+ },
+ "define-property": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
+ "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^1.0.2",
+ "isobject": "^3.0.1"
+ },
+ "dependencies": {
+ "is-accessor-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-data-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-descriptor": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^1.0.0",
+ "is-data-descriptor": "^1.0.0",
+ "kind-of": "^6.0.2"
+ }
+ }
+ }
+ },
+ "depd": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+ "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=",
+ "dev": true
+ },
+ "di": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz",
+ "integrity": "sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=",
+ "dev": true
+ },
+ "dom-serialize": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz",
+ "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=",
+ "dev": true,
+ "requires": {
+ "custom-event": "~1.0.0",
+ "ent": "~2.2.0",
+ "extend": "^3.0.0",
+ "void-elements": "^2.0.0"
+ }
+ },
+ "ee-first": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=",
+ "dev": true
+ },
+ "encodeurl": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+ "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=",
+ "dev": true
+ },
+ "engine.io": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.2.1.tgz",
+ "integrity": "sha512-+VlKzHzMhaU+GsCIg4AoXF1UdDFjHHwMmMKqMJNDNLlUlejz58FCy4LBqB2YVJskHGYl06BatYWKP2TVdVXE5w==",
+ "dev": true,
+ "requires": {
+ "accepts": "~1.3.4",
+ "base64id": "1.0.0",
+ "cookie": "0.3.1",
+ "debug": "~3.1.0",
+ "engine.io-parser": "~2.1.0",
+ "ws": "~3.3.1"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+ "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ }
+ }
+ },
+ "engine.io-client": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.2.1.tgz",
+ "integrity": "sha512-y5AbkytWeM4jQr7m/koQLc5AxpRKC1hEVUb/s1FUAWEJq5AzJJ4NLvzuKPuxtDi5Mq755WuDvZ6Iv2rXj4PTzw==",
+ "dev": true,
+ "requires": {
+ "component-emitter": "1.2.1",
+ "component-inherit": "0.0.3",
+ "debug": "~3.1.0",
+ "engine.io-parser": "~2.1.1",
+ "has-cors": "1.1.0",
+ "indexof": "0.0.1",
+ "parseqs": "0.0.5",
+ "parseuri": "0.0.5",
+ "ws": "~3.3.1",
+ "xmlhttprequest-ssl": "~1.5.4",
+ "yeast": "0.1.2"
+ },
+ "dependencies": {
+ "component-emitter": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
+ "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=",
+ "dev": true
+ },
+ "debug": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+ "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ }
+ }
+ },
+ "engine.io-parser": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.1.3.tgz",
+ "integrity": "sha512-6HXPre2O4Houl7c4g7Ic/XzPnHBvaEmN90vtRO9uLmwtRqQmTOw0QMevL1TOfL2Cpu1VzsaTmMotQgMdkzGkVA==",
+ "dev": true,
+ "requires": {
+ "after": "0.8.2",
+ "arraybuffer.slice": "~0.0.7",
+ "base64-arraybuffer": "0.1.5",
+ "blob": "0.0.5",
+ "has-binary2": "~1.0.2"
+ }
+ },
+ "ent": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz",
+ "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=",
+ "dev": true
+ },
+ "escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=",
+ "dev": true
+ },
+ "escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+ "dev": true
+ },
+ "eventemitter3": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.4.tgz",
+ "integrity": "sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ==",
+ "dev": true
+ },
+ "expand-braces": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/expand-braces/-/expand-braces-0.1.2.tgz",
+ "integrity": "sha1-SIsdHSRRyz06axks/AMPRMWFX+o=",
+ "dev": true,
+ "requires": {
+ "array-slice": "^0.2.3",
+ "array-unique": "^0.2.1",
+ "braces": "^0.1.2"
+ },
+ "dependencies": {
+ "array-unique": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz",
+ "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=",
+ "dev": true
+ },
+ "braces": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-0.1.5.tgz",
+ "integrity": "sha1-wIVxEIUpHYt1/ddOqw+FlygHEeY=",
+ "dev": true,
+ "requires": {
+ "expand-range": "^0.1.0"
+ }
+ }
+ }
+ },
+ "expand-brackets": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
+ "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
+ "dev": true,
+ "requires": {
+ "debug": "^2.3.3",
+ "define-property": "^0.2.5",
+ "extend-shallow": "^2.0.1",
+ "posix-character-classes": "^0.1.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ },
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
+ }
+ },
+ "expand-range": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-0.1.1.tgz",
+ "integrity": "sha1-TLjtoJk8pW+k9B/ELzy7TMrf8EQ=",
+ "dev": true,
+ "requires": {
+ "is-number": "^0.1.1",
+ "repeat-string": "^0.2.2"
+ },
+ "dependencies": {
+ "is-number": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-0.1.1.tgz",
+ "integrity": "sha1-aaevEWlj1HIG7JvZtIoUIW8eOAY=",
+ "dev": true
+ },
+ "repeat-string": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-0.2.2.tgz",
+ "integrity": "sha1-x6jTI2BoNiBZp+RlH8aITosftK4=",
+ "dev": true
+ }
+ }
+ },
+ "extend": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+ "dev": true
+ },
+ "extend-shallow": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
+ "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
+ "dev": true,
+ "requires": {
+ "assign-symbols": "^1.0.0",
+ "is-extendable": "^1.0.1"
+ },
+ "dependencies": {
+ "is-extendable": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+ "dev": true,
+ "requires": {
+ "is-plain-object": "^2.0.4"
+ }
+ }
+ }
+ },
+ "extglob": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
+ "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
+ "dev": true,
+ "requires": {
+ "array-unique": "^0.3.2",
+ "define-property": "^1.0.0",
+ "expand-brackets": "^2.1.4",
+ "extend-shallow": "^2.0.1",
+ "fragment-cache": "^0.2.1",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^1.0.0"
+ }
+ },
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ },
+ "is-accessor-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-data-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-descriptor": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^1.0.0",
+ "is-data-descriptor": "^1.0.0",
+ "kind-of": "^6.0.2"
+ }
+ }
+ }
+ },
+ "file-uri-to-path": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
+ "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
+ "dev": true,
+ "optional": true
+ },
+ "fill-range": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
+ "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^2.0.1",
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1",
+ "to-regex-range": "^2.1.0"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
+ }
+ },
+ "finalhandler": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
+ "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
+ "dev": true,
+ "requires": {
+ "debug": "2.6.9",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "on-finished": "~2.3.0",
+ "parseurl": "~1.3.3",
+ "statuses": "~1.5.0",
+ "unpipe": "~1.0.0"
+ }
+ },
+ "follow-redirects": {
+ "version": "1.11.0",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.11.0.tgz",
+ "integrity": "sha512-KZm0V+ll8PfBrKwMzdo5D13b1bur9Iq9Zd/RMmAoQQcl2PxxFml8cxXPaaPYVbV0RjNjq1CU7zIzAOqtUPudmA==",
+ "dev": true,
+ "requires": {
+ "debug": "^3.0.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+ "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ }
+ }
+ },
+ "for-in": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
+ "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=",
+ "dev": true
+ },
+ "fragment-cache": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
+ "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=",
+ "dev": true,
+ "requires": {
+ "map-cache": "^0.2.2"
+ }
+ },
+ "fs-access": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/fs-access/-/fs-access-1.0.1.tgz",
+ "integrity": "sha1-1qh/JiJxzv6+wwxVNAf7mV2od3o=",
+ "dev": true,
+ "requires": {
+ "null-check": "^1.0.0"
+ }
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+ "dev": true
+ },
+ "fsevents": {
+ "version": "1.2.11",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.11.tgz",
+ "integrity": "sha512-+ux3lx6peh0BpvY0JebGyZoiR4D+oYzdPZMKJwkZ+sFkNJzpL7tXc/wehS49gUAxg3tmMHPHZkA8JU2rhhgDHw==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "bindings": "^1.5.0",
+ "nan": "^2.12.1",
+ "node-pre-gyp": "*"
+ },
+ "dependencies": {
+ "abbrev": {
+ "version": "1.1.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "ansi-regex": {
+ "version": "2.1.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "aproba": {
+ "version": "1.2.0",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "are-we-there-yet": {
+ "version": "1.1.5",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "delegates": "^1.0.0",
+ "readable-stream": "^2.0.6"
+ }
+ },
+ "balanced-match": {
+ "version": "1.0.0",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "chownr": {
+ "version": "1.1.3",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "code-point-at": {
+ "version": "1.1.0",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "console-control-strings": {
+ "version": "1.1.0",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "core-util-is": {
+ "version": "1.0.2",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "debug": {
+ "version": "3.2.6",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "deep-extend": {
+ "version": "0.6.0",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "delegates": {
+ "version": "1.0.0",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "detect-libc": {
+ "version": "1.0.3",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "fs-minipass": {
+ "version": "1.2.7",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "minipass": "^2.6.0"
+ }
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "gauge": {
+ "version": "2.7.4",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "aproba": "^1.0.3",
+ "console-control-strings": "^1.0.0",
+ "has-unicode": "^2.0.0",
+ "object-assign": "^4.1.0",
+ "signal-exit": "^3.0.0",
+ "string-width": "^1.0.1",
+ "strip-ansi": "^3.0.1",
+ "wide-align": "^1.1.0"
+ }
+ },
+ "glob": {
+ "version": "7.1.6",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "has-unicode": {
+ "version": "2.0.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "iconv-lite": {
+ "version": "0.4.24",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ }
+ },
+ "ignore-walk": {
+ "version": "3.0.3",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "minimatch": "^3.0.4"
+ }
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.4",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "ini": {
+ "version": "1.3.5",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "1.0.0",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "number-is-nan": "^1.0.0"
+ }
+ },
+ "isarray": {
+ "version": "1.0.0",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "minimatch": {
+ "version": "3.0.4",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "minimist": {
+ "version": "0.0.8",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "minipass": {
+ "version": "2.9.0",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "safe-buffer": "^5.1.2",
+ "yallist": "^3.0.0"
+ }
+ },
+ "minizlib": {
+ "version": "1.3.3",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "minipass": "^2.9.0"
+ }
+ },
+ "mkdirp": {
+ "version": "0.5.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "minimist": "0.0.8"
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "needle": {
+ "version": "2.4.0",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "debug": "^3.2.6",
+ "iconv-lite": "^0.4.4",
+ "sax": "^1.2.4"
+ }
+ },
+ "node-pre-gyp": {
+ "version": "0.14.0",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "detect-libc": "^1.0.2",
+ "mkdirp": "^0.5.1",
+ "needle": "^2.2.1",
+ "nopt": "^4.0.1",
+ "npm-packlist": "^1.1.6",
+ "npmlog": "^4.0.2",
+ "rc": "^1.2.7",
+ "rimraf": "^2.6.1",
+ "semver": "^5.3.0",
+ "tar": "^4.4.2"
+ }
+ },
+ "nopt": {
+ "version": "4.0.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "abbrev": "1",
+ "osenv": "^0.1.4"
+ }
+ },
+ "npm-bundled": {
+ "version": "1.1.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "npm-normalize-package-bin": "^1.0.1"
+ }
+ },
+ "npm-normalize-package-bin": {
+ "version": "1.0.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "npm-packlist": {
+ "version": "1.4.7",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "ignore-walk": "^3.0.1",
+ "npm-bundled": "^1.0.1"
+ }
+ },
+ "npmlog": {
+ "version": "4.1.2",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "are-we-there-yet": "~1.1.2",
+ "console-control-strings": "~1.1.0",
+ "gauge": "~2.7.3",
+ "set-blocking": "~2.0.0"
+ }
+ },
+ "number-is-nan": {
+ "version": "1.0.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "object-assign": {
+ "version": "4.1.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "once": {
+ "version": "1.4.0",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "os-homedir": {
+ "version": "1.0.2",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "os-tmpdir": {
+ "version": "1.0.2",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "osenv": {
+ "version": "0.1.5",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "os-homedir": "^1.0.0",
+ "os-tmpdir": "^1.0.0"
+ }
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "process-nextick-args": {
+ "version": "2.0.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "rc": {
+ "version": "1.2.8",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "deep-extend": "^0.6.0",
+ "ini": "~1.3.0",
+ "minimist": "^1.2.0",
+ "strip-json-comments": "~2.0.1"
+ },
+ "dependencies": {
+ "minimist": {
+ "version": "1.2.0",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ }
+ }
+ },
+ "readable-stream": {
+ "version": "2.3.6",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "rimraf": {
+ "version": "2.7.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.1.2",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "safer-buffer": {
+ "version": "2.1.2",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "sax": {
+ "version": "1.2.4",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "semver": {
+ "version": "5.7.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "set-blocking": {
+ "version": "2.0.0",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "signal-exit": {
+ "version": "3.0.2",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "string-width": {
+ "version": "1.0.2",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "code-point-at": "^1.0.0",
+ "is-fullwidth-code-point": "^1.0.0",
+ "strip-ansi": "^3.0.0"
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "3.0.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ },
+ "strip-json-comments": {
+ "version": "2.0.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "tar": {
+ "version": "4.4.13",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "chownr": "^1.1.1",
+ "fs-minipass": "^1.2.5",
+ "minipass": "^2.8.6",
+ "minizlib": "^1.2.1",
+ "mkdirp": "^0.5.0",
+ "safe-buffer": "^5.1.2",
+ "yallist": "^3.0.3"
+ }
+ },
+ "util-deprecate": {
+ "version": "1.0.2",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "wide-align": {
+ "version": "1.1.3",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "string-width": "^1.0.2 || 2"
+ }
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "yallist": {
+ "version": "3.1.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ }
+ }
+ },
+ "gensync": {
+ "version": "1.0.0-beta.1",
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz",
+ "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==",
+ "dev": true
+ },
+ "get-value": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
+ "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=",
+ "dev": true
+ },
+ "glob": {
+ "version": "7.1.6",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
+ "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "glob-parent": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
+ "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
+ "dev": true,
+ "requires": {
+ "is-glob": "^3.1.0",
+ "path-dirname": "^1.0.0"
+ },
+ "dependencies": {
+ "is-glob": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
+ "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.0"
+ }
+ }
+ }
+ },
+ "globals": {
+ "version": "11.12.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
+ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
+ "dev": true
+ },
+ "graceful-fs": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz",
+ "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==",
+ "dev": true
+ },
+ "has-binary2": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz",
+ "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==",
+ "dev": true,
+ "requires": {
+ "isarray": "2.0.1"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
+ "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=",
+ "dev": true
+ }
+ }
+ },
+ "has-cors": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
+ "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=",
+ "dev": true
+ },
+ "has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+ "dev": true
+ },
+ "has-value": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
+ "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=",
+ "dev": true,
+ "requires": {
+ "get-value": "^2.0.6",
+ "has-values": "^1.0.0",
+ "isobject": "^3.0.0"
+ }
+ },
+ "has-values": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz",
+ "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=",
+ "dev": true,
+ "requires": {
+ "is-number": "^3.0.0",
+ "kind-of": "^4.0.0"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
+ "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "html-escaper": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
+ "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
+ "dev": true
+ },
+ "http-errors": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
+ "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
+ "dev": true,
+ "requires": {
+ "depd": "~1.1.2",
+ "inherits": "2.0.3",
+ "setprototypeof": "1.1.1",
+ "statuses": ">= 1.5.0 < 2",
+ "toidentifier": "1.0.0"
+ }
+ },
+ "http-proxy": {
+ "version": "1.18.1",
+ "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz",
+ "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==",
+ "dev": true,
+ "requires": {
+ "eventemitter3": "^4.0.0",
+ "follow-redirects": "^1.0.0",
+ "requires-port": "^1.0.0"
+ }
+ },
+ "iconv-lite": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "dev": true,
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ }
+ },
+ "indexof": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
+ "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=",
+ "dev": true
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "dev": true,
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
+ "dev": true
+ },
+ "is-accessor-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-binary-path": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
+ "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=",
+ "dev": true,
+ "requires": {
+ "binary-extensions": "^1.0.0"
+ }
+ },
+ "is-buffer": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
+ "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
+ "dev": true
+ },
+ "is-data-descriptor": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^0.1.6",
+ "is-data-descriptor": "^0.1.4",
+ "kind-of": "^5.0.0"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+ "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+ "dev": true
+ }
+ }
+ },
+ "is-docker": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-1.1.0.tgz",
+ "integrity": "sha1-8EN01O7lMQ6ajhE78UlUEeRhdqE=",
+ "dev": true
+ },
+ "is-extendable": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+ "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
+ "dev": true
+ },
+ "is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+ "dev": true
+ },
+ "is-glob": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
+ "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.1"
+ }
+ },
+ "is-number": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-plain-object": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+ "dev": true,
+ "requires": {
+ "isobject": "^3.0.1"
+ }
+ },
+ "is-windows": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
+ "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==",
+ "dev": true
+ },
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+ "dev": true
+ },
+ "isbinaryfile": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.3.tgz",
+ "integrity": "sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw==",
+ "dev": true,
+ "requires": {
+ "buffer-alloc": "^1.2.0"
+ }
+ },
+ "isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+ "dev": true
+ },
+ "isobject": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
+ "dev": true
+ },
+ "istanbul-lib-coverage": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz",
+ "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==",
+ "dev": true
+ },
+ "istanbul-lib-instrument": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz",
+ "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==",
+ "dev": true,
+ "requires": {
+ "@babel/core": "^7.7.5",
+ "@istanbuljs/schema": "^0.1.2",
+ "istanbul-lib-coverage": "^3.0.0",
+ "semver": "^6.3.0"
+ }
+ },
+ "istanbul-lib-report": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz",
+ "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==",
+ "dev": true,
+ "requires": {
+ "istanbul-lib-coverage": "^3.0.0",
+ "make-dir": "^3.0.0",
+ "supports-color": "^7.1.0"
+ },
+ "dependencies": {
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
+ "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ }
+ }
+ },
+ "istanbul-lib-source-maps": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz",
+ "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==",
+ "dev": true,
+ "requires": {
+ "debug": "^4.1.1",
+ "istanbul-lib-coverage": "^3.0.0",
+ "source-map": "^0.6.1"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ }
+ }
+ },
+ "istanbul-reports": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz",
+ "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==",
+ "dev": true,
+ "requires": {
+ "html-escaper": "^2.0.0",
+ "istanbul-lib-report": "^3.0.0"
+ }
+ },
+ "jasmine-core": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.1.0.tgz",
+ "integrity": "sha1-pHheE11d9lAk38kiSVPfWFvSdmw=",
+ "dev": true
+ },
+ "js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true
+ },
+ "jsesc": {
+ "version": "2.5.2",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
+ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
+ "dev": true
+ },
+ "json5": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz",
+ "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.2.5"
+ }
+ },
+ "karma": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/karma/-/karma-3.0.0.tgz",
+ "integrity": "sha512-ZTjyuDXVXhXsvJ1E4CnZzbCjSxD6sEdzEsFYogLuZM0yqvg/mgz+O+R1jb0J7uAQeuzdY8kJgx6hSNXLwFuHIQ==",
+ "dev": true,
+ "requires": {
+ "bluebird": "^3.3.0",
+ "body-parser": "^1.16.1",
+ "chokidar": "^2.0.3",
+ "colors": "^1.1.0",
+ "combine-lists": "^1.0.0",
+ "connect": "^3.6.0",
+ "core-js": "^2.2.0",
+ "di": "^0.0.1",
+ "dom-serialize": "^2.2.0",
+ "expand-braces": "^0.1.1",
+ "glob": "^7.1.1",
+ "graceful-fs": "^4.1.2",
+ "http-proxy": "^1.13.0",
+ "isbinaryfile": "^3.0.0",
+ "lodash": "^4.17.4",
+ "log4js": "^3.0.0",
+ "mime": "^2.3.1",
+ "minimatch": "^3.0.2",
+ "optimist": "^0.6.1",
+ "qjobs": "^1.1.4",
+ "range-parser": "^1.2.0",
+ "rimraf": "^2.6.0",
+ "safe-buffer": "^5.0.1",
+ "socket.io": "2.1.1",
+ "source-map": "^0.6.1",
+ "tmp": "0.0.33",
+ "useragent": "2.2.1"
+ }
+ },
+ "karma-chrome-launcher": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-2.2.0.tgz",
+ "integrity": "sha512-uf/ZVpAabDBPvdPdveyk1EPgbnloPvFFGgmRhYLTDH7gEB4nZdSBk8yTU47w1g/drLSx5uMOkjKk7IWKfWg/+w==",
+ "dev": true,
+ "requires": {
+ "fs-access": "^1.0.0",
+ "which": "^1.2.1"
+ }
+ },
+ "karma-coverage": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/karma-coverage/-/karma-coverage-2.0.2.tgz",
+ "integrity": "sha512-zge5qiGEIKDdzWciQwP4p0LSac4k/L6VfrBsERMUn5mpDvxhv1sPVOrSlpzpi70T7NhuEy4bgnpAKIYuumIMCw==",
+ "dev": true,
+ "requires": {
+ "istanbul-lib-coverage": "^3.0.0",
+ "istanbul-lib-instrument": "^4.0.1",
+ "istanbul-lib-report": "^3.0.0",
+ "istanbul-lib-source-maps": "^4.0.0",
+ "istanbul-reports": "^3.0.0",
+ "minimatch": "^3.0.4"
+ }
+ },
+ "karma-jasmine": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-1.1.2.tgz",
+ "integrity": "sha1-OU8rJf+0pkS5rabyLUQ+L9CIhsM=",
+ "dev": true
+ },
+ "kind-of": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+ "dev": true
+ },
+ "lodash": {
+ "version": "4.17.15",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
+ "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
+ "dev": true
+ },
+ "log4js": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/log4js/-/log4js-3.0.6.tgz",
+ "integrity": "sha512-ezXZk6oPJCWL483zj64pNkMuY/NcRX5MPiB0zE6tjZM137aeusrOnW1ecxgF9cmwMWkBMhjteQxBPoZBh9FDxQ==",
+ "dev": true,
+ "requires": {
+ "circular-json": "^0.5.5",
+ "date-format": "^1.2.0",
+ "debug": "^3.1.0",
+ "rfdc": "^1.1.2",
+ "streamroller": "0.7.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+ "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ }
+ }
+ },
+ "lru-cache": {
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.2.4.tgz",
+ "integrity": "sha1-bGWGGb7PFAMdDQtZSxYELOTcBj0=",
+ "dev": true
+ },
+ "make-dir": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
+ "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
+ "dev": true,
+ "requires": {
+ "semver": "^6.0.0"
+ }
+ },
+ "map-cache": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
+ "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=",
+ "dev": true
+ },
+ "map-visit": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
+ "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=",
+ "dev": true,
+ "requires": {
+ "object-visit": "^1.0.0"
+ }
+ },
+ "media-typer": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+ "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=",
+ "dev": true
+ },
+ "micromatch": {
+ "version": "3.1.10",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
+ "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
+ "dev": true,
+ "requires": {
+ "arr-diff": "^4.0.0",
+ "array-unique": "^0.3.2",
+ "braces": "^2.3.1",
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "extglob": "^2.0.4",
+ "fragment-cache": "^0.2.1",
+ "kind-of": "^6.0.2",
+ "nanomatch": "^1.2.9",
+ "object.pick": "^1.3.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.2"
+ }
+ },
+ "mime": {
+ "version": "2.4.4",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz",
+ "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==",
+ "dev": true
+ },
+ "mime-db": {
+ "version": "1.43.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz",
+ "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==",
+ "dev": true
+ },
+ "mime-types": {
+ "version": "2.1.26",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz",
+ "integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==",
+ "dev": true,
+ "requires": {
+ "mime-db": "1.43.0"
+ }
+ },
+ "minimatch": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "minimist": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
+ "dev": true
+ },
+ "mixin-deep": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz",
+ "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==",
+ "dev": true,
+ "requires": {
+ "for-in": "^1.0.2",
+ "is-extendable": "^1.0.1"
+ },
+ "dependencies": {
+ "is-extendable": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+ "dev": true,
+ "requires": {
+ "is-plain-object": "^2.0.4"
+ }
+ }
+ }
+ },
+ "mkdirp": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.3.tgz",
+ "integrity": "sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg==",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.2.5"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ },
+ "nan": {
+ "version": "2.14.0",
+ "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz",
+ "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==",
+ "dev": true,
+ "optional": true
+ },
+ "nanomatch": {
+ "version": "1.2.13",
+ "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
+ "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==",
+ "dev": true,
+ "requires": {
+ "arr-diff": "^4.0.0",
+ "array-unique": "^0.3.2",
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "fragment-cache": "^0.2.1",
+ "is-windows": "^1.0.2",
+ "kind-of": "^6.0.2",
+ "object.pick": "^1.3.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.1"
+ }
+ },
+ "negotiator": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
+ "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==",
+ "dev": true
+ },
+ "normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true
+ },
+ "null-check": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/null-check/-/null-check-1.0.0.tgz",
+ "integrity": "sha1-l33/1xdgErnsMNKjnbXPcqBDnt0=",
+ "dev": true
+ },
+ "object-component": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz",
+ "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=",
+ "dev": true
+ },
+ "object-copy": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
+ "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=",
+ "dev": true,
+ "requires": {
+ "copy-descriptor": "^0.1.0",
+ "define-property": "^0.2.5",
+ "kind-of": "^3.0.3"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ },
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "object-visit": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
+ "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=",
+ "dev": true,
+ "requires": {
+ "isobject": "^3.0.0"
+ }
+ },
+ "object.pick": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
+ "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=",
+ "dev": true,
+ "requires": {
+ "isobject": "^3.0.1"
+ }
+ },
+ "on-finished": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+ "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
+ "dev": true,
+ "requires": {
+ "ee-first": "1.1.1"
+ }
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "dev": true,
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "optimist": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
+ "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=",
+ "dev": true,
+ "requires": {
+ "minimist": "~0.0.1",
+ "wordwrap": "~0.0.2"
+ },
+ "dependencies": {
+ "minimist": {
+ "version": "0.0.10",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz",
+ "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=",
+ "dev": true
+ }
+ }
+ },
+ "os-tmpdir": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
+ "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
+ "dev": true
+ },
+ "parseqs": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz",
+ "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=",
+ "dev": true,
+ "requires": {
+ "better-assert": "~1.0.0"
+ }
+ },
+ "parseuri": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz",
+ "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=",
+ "dev": true,
+ "requires": {
+ "better-assert": "~1.0.0"
+ }
+ },
+ "parseurl": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+ "dev": true
+ },
+ "pascalcase": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
+ "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=",
+ "dev": true
+ },
+ "path-dirname": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
+ "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=",
+ "dev": true
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+ "dev": true
+ },
+ "path-parse": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
+ "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
+ "dev": true
+ },
+ "posix-character-classes": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
+ "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=",
+ "dev": true
+ },
+ "process-nextick-args": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
+ "dev": true
+ },
+ "qjobs": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz",
+ "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==",
+ "dev": true
+ },
+ "qs": {
+ "version": "6.7.0",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
+ "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==",
+ "dev": true
+ },
+ "range-parser": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+ "dev": true
+ },
+ "raw-body": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
+ "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
+ "dev": true,
+ "requires": {
+ "bytes": "3.1.0",
+ "http-errors": "1.7.2",
+ "iconv-lite": "0.4.24",
+ "unpipe": "1.0.0"
+ }
+ },
+ "readable-stream": {
+ "version": "2.3.7",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+ "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+ "dev": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ },
+ "dependencies": {
+ "safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true
+ }
+ }
+ },
+ "readdirp": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz",
+ "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.11",
+ "micromatch": "^3.1.10",
+ "readable-stream": "^2.0.2"
+ }
+ },
+ "regex-not": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
+ "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^3.0.2",
+ "safe-regex": "^1.1.0"
+ }
+ },
+ "remove-trailing-separator": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
+ "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=",
+ "dev": true
+ },
+ "repeat-element": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz",
+ "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==",
+ "dev": true
+ },
+ "repeat-string": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
+ "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=",
+ "dev": true
+ },
+ "requirejs": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.6.tgz",
+ "integrity": "sha512-ipEzlWQe6RK3jkzikgCupiTbTvm4S0/CAU5GlgptkN5SO6F3u0UD0K18wy6ErDqiCyP4J4YYe1HuAShvsxePLg==",
+ "dev": true
+ },
+ "requires-port": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
+ "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
+ "dev": true
+ },
+ "resolve": {
+ "version": "1.17.0",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
+ "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==",
+ "dev": true,
+ "requires": {
+ "path-parse": "^1.0.6"
+ }
+ },
+ "resolve-url": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
+ "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=",
+ "dev": true
+ },
+ "ret": {
+ "version": "0.1.15",
+ "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
+ "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==",
+ "dev": true
+ },
+ "rfdc": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.1.4.tgz",
+ "integrity": "sha512-5C9HXdzK8EAqN7JDif30jqsBzavB7wLpaubisuQIGHWf2gUXSpzy6ArX/+Da8RjFpagWsCn+pIgxTMAmKw9Zug==",
+ "dev": true
+ },
+ "rimraf": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+ "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
+ "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==",
+ "dev": true
+ },
+ "safe-regex": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
+ "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=",
+ "dev": true,
+ "requires": {
+ "ret": "~0.1.10"
+ }
+ },
+ "safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "dev": true
+ },
+ "semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true
+ },
+ "set-value": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
+ "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^2.0.1",
+ "is-extendable": "^0.1.1",
+ "is-plain-object": "^2.0.3",
+ "split-string": "^3.0.1"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
+ }
+ },
+ "setprototypeof": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
+ "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==",
+ "dev": true
+ },
+ "snapdragon": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
+ "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==",
+ "dev": true,
+ "requires": {
+ "base": "^0.11.1",
+ "debug": "^2.2.0",
+ "define-property": "^0.2.5",
+ "extend-shallow": "^2.0.1",
+ "map-cache": "^0.2.2",
+ "source-map": "^0.5.6",
+ "source-map-resolve": "^0.5.0",
+ "use": "^3.1.0"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ },
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ },
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+ "dev": true
+ }
+ }
+ },
+ "snapdragon-node": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz",
+ "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==",
+ "dev": true,
+ "requires": {
+ "define-property": "^1.0.0",
+ "isobject": "^3.0.0",
+ "snapdragon-util": "^3.0.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^1.0.0"
+ }
+ },
+ "is-accessor-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-data-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-descriptor": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^1.0.0",
+ "is-data-descriptor": "^1.0.0",
+ "kind-of": "^6.0.2"
+ }
+ }
+ }
+ },
+ "snapdragon-util": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz",
+ "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.2.0"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "socket.io": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.1.1.tgz",
+ "integrity": "sha512-rORqq9c+7W0DAK3cleWNSyfv/qKXV99hV4tZe+gGLfBECw3XEhBy7x85F3wypA9688LKjtwO9pX9L33/xQI8yA==",
+ "dev": true,
+ "requires": {
+ "debug": "~3.1.0",
+ "engine.io": "~3.2.0",
+ "has-binary2": "~1.0.2",
+ "socket.io-adapter": "~1.1.0",
+ "socket.io-client": "2.1.1",
+ "socket.io-parser": "~3.2.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+ "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ }
+ }
+ },
+ "socket.io-adapter": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz",
+ "integrity": "sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g==",
+ "dev": true
+ },
+ "socket.io-client": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.1.1.tgz",
+ "integrity": "sha512-jxnFyhAuFxYfjqIgduQlhzqTcOEQSn+OHKVfAxWaNWa7ecP7xSNk2Dx/3UEsDcY7NcFafxvNvKPmmO7HTwTxGQ==",
+ "dev": true,
+ "requires": {
+ "backo2": "1.0.2",
+ "base64-arraybuffer": "0.1.5",
+ "component-bind": "1.0.0",
+ "component-emitter": "1.2.1",
+ "debug": "~3.1.0",
+ "engine.io-client": "~3.2.0",
+ "has-binary2": "~1.0.2",
+ "has-cors": "1.1.0",
+ "indexof": "0.0.1",
+ "object-component": "0.0.3",
+ "parseqs": "0.0.5",
+ "parseuri": "0.0.5",
+ "socket.io-parser": "~3.2.0",
+ "to-array": "0.1.4"
+ },
+ "dependencies": {
+ "component-emitter": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
+ "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=",
+ "dev": true
+ },
+ "debug": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+ "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ }
+ }
+ },
+ "socket.io-parser": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.2.0.tgz",
+ "integrity": "sha512-FYiBx7rc/KORMJlgsXysflWx/RIvtqZbyGLlHZvjfmPTPeuD/I8MaW7cfFrj5tRltICJdgwflhfZ3NVVbVLFQA==",
+ "dev": true,
+ "requires": {
+ "component-emitter": "1.2.1",
+ "debug": "~3.1.0",
+ "isarray": "2.0.1"
+ },
+ "dependencies": {
+ "component-emitter": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
+ "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=",
+ "dev": true
+ },
+ "debug": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+ "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "isarray": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
+ "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=",
+ "dev": true
+ }
+ }
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ },
+ "source-map-resolve": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz",
+ "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==",
+ "dev": true,
+ "requires": {
+ "atob": "^2.1.2",
+ "decode-uri-component": "^0.2.0",
+ "resolve-url": "^0.2.1",
+ "source-map-url": "^0.4.0",
+ "urix": "^0.1.0"
+ }
+ },
+ "source-map-url": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz",
+ "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=",
+ "dev": true
+ },
+ "split-string": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
+ "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^3.0.0"
+ }
+ },
+ "static-extend": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
+ "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=",
+ "dev": true,
+ "requires": {
+ "define-property": "^0.2.5",
+ "object-copy": "^0.1.0"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ }
+ }
+ },
+ "statuses": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
+ "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=",
+ "dev": true
+ },
+ "streamroller": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-0.7.0.tgz",
+ "integrity": "sha512-WREzfy0r0zUqp3lGO096wRuUp7ho1X6uo/7DJfTlEi0Iv/4gT7YHqXDjKC2ioVGBZtE8QzsQD9nx1nIuoZ57jQ==",
+ "dev": true,
+ "requires": {
+ "date-format": "^1.2.0",
+ "debug": "^3.1.0",
+ "mkdirp": "^0.5.1",
+ "readable-stream": "^2.3.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+ "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ }
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ },
+ "dependencies": {
+ "safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true
+ }
+ }
+ },
+ "supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ },
+ "tmp": {
+ "version": "0.0.33",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
+ "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
+ "dev": true,
+ "requires": {
+ "os-tmpdir": "~1.0.2"
+ }
+ },
+ "to-array": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz",
+ "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=",
+ "dev": true
+ },
+ "to-fast-properties": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
+ "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=",
+ "dev": true
+ },
+ "to-object-path": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
+ "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "to-regex": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz",
+ "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==",
+ "dev": true,
+ "requires": {
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "regex-not": "^1.0.2",
+ "safe-regex": "^1.1.0"
+ }
+ },
+ "to-regex-range": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
+ "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
+ "dev": true,
+ "requires": {
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1"
+ }
+ },
+ "toidentifier": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
+ "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==",
+ "dev": true
+ },
+ "type-is": {
+ "version": "1.6.18",
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+ "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+ "dev": true,
+ "requires": {
+ "media-typer": "0.3.0",
+ "mime-types": "~2.1.24"
+ }
+ },
+ "ultron": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz",
+ "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==",
+ "dev": true
+ },
+ "union-value": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
+ "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==",
+ "dev": true,
+ "requires": {
+ "arr-union": "^3.1.0",
+ "get-value": "^2.0.6",
+ "is-extendable": "^0.1.1",
+ "set-value": "^2.0.1"
+ }
+ },
+ "unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=",
+ "dev": true
+ },
+ "unset-value": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
+ "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=",
+ "dev": true,
+ "requires": {
+ "has-value": "^0.3.1",
+ "isobject": "^3.0.0"
+ },
+ "dependencies": {
+ "has-value": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz",
+ "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=",
+ "dev": true,
+ "requires": {
+ "get-value": "^2.0.3",
+ "has-values": "^0.1.4",
+ "isobject": "^2.0.0"
+ },
+ "dependencies": {
+ "isobject": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
+ "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
+ "dev": true,
+ "requires": {
+ "isarray": "1.0.0"
+ }
+ }
+ }
+ },
+ "has-values": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz",
+ "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=",
+ "dev": true
+ }
+ }
+ },
+ "upath": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz",
+ "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==",
+ "dev": true
+ },
+ "urix": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
+ "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=",
+ "dev": true
+ },
+ "use": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
+ "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==",
+ "dev": true
+ },
+ "useragent": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/useragent/-/useragent-2.2.1.tgz",
+ "integrity": "sha1-z1k+9PLRdYdei7ZY6pLhik/QbY4=",
+ "dev": true,
+ "requires": {
+ "lru-cache": "2.2.x",
+ "tmp": "0.0.x"
+ }
+ },
+ "util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
+ "dev": true
+ },
+ "utils-merge": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+ "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=",
+ "dev": true
+ },
+ "void-elements": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz",
+ "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=",
+ "dev": true
+ },
+ "which": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+ "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+ "dev": true,
+ "requires": {
+ "isexe": "^2.0.0"
+ }
+ },
+ "wordwrap": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
+ "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=",
+ "dev": true
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+ "dev": true
+ },
+ "ws": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz",
+ "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==",
+ "dev": true,
+ "requires": {
+ "async-limiter": "~1.0.0",
+ "safe-buffer": "~5.1.0",
+ "ultron": "~1.1.0"
+ },
+ "dependencies": {
+ "safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true
+ }
+ }
+ },
+ "xmlhttprequest-ssl": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz",
+ "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=",
+ "dev": true
+ },
+ "yeast": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
+ "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=",
+ "dev": true
+ }
+ }
+}
diff --git a/chromium/third_party/skia/modules/canvaskit/package.json b/chromium/third_party/skia/modules/canvaskit/package.json
index 33a4a86cb93..24f520b947d 100644
--- a/chromium/third_party/skia/modules/canvaskit/package.json
+++ b/chromium/third_party/skia/modules/canvaskit/package.json
@@ -10,6 +10,7 @@
"jasmine-core": "~3.1.0",
"karma": "~3.0.0",
"karma-chrome-launcher": "~2.2.0",
+ "karma-coverage": "^2.0.2",
"karma-jasmine": "~1.1.2",
"requirejs": "~2.3.5"
},
diff --git a/chromium/third_party/skia/modules/canvaskit/paragraph.js b/chromium/third_party/skia/modules/canvaskit/paragraph.js
index 381e8cb777d..c171a36721d 100644
--- a/chromium/third_party/skia/modules/canvaskit/paragraph.js
+++ b/chromium/third_party/skia/modules/canvaskit/paragraph.js
@@ -15,7 +15,7 @@
var ret = [];
for (var i = 0; i < floatArray.length; i+=5) {
var r = CanvasKit.LTRBRect(floatArray[i], floatArray[i+1], floatArray[i+2], floatArray[i+3]);
- if (floatArray[i+4] === 1) {
+ if (floatArray[i+4] === 0) {
r['direction'] = CanvasKit.TextDirection.RTL;
} else {
r['direction'] = CanvasKit.TextDirection.LTR;
@@ -34,11 +34,8 @@
s['disableHinting'] = s['disableHinting'] || false;
if (s['ellipsis']) {
var str = s['ellipsis'];
- var strLen = lengthBytesUTF8(str) + 1;
- var strPtr = CanvasKit._malloc(strLen);
- stringToUTF8(str, strPtr, strLen);
- s['_ellipsisPtr'] = strPtr;
- s['_ellipsisLen'] = strLen;
+ s['_ellipsisPtr'] = cacheOrCopyString(str);
+ s['_ellipsisLen'] = lengthBytesUTF8(str) + 1; // add 1 for the null terminator.
} else {
s['_ellipsisPtr'] = nullptr;
s['_ellipsisLen'] = 0;
@@ -50,7 +47,7 @@
s['textDirection'] = s['textDirection'] || CanvasKit.TextDirection.LTR;
s['textStyle'] = CanvasKit.TextStyle(s['textStyle']);
return s;
- }
+ };
function fontStyle(s) {
s = s || {};
@@ -65,27 +62,16 @@
CanvasKit.TextStyle = function(s) {
// Use [''] to tell closure not to minify the names
- if (!isCanvasKitColor(s['color'])) {
+ if (!s['color']) {
s['color'] = CanvasKit.BLACK;
}
- s['foregroundColor'] = s['foregroundColor'] || CanvasKit.TRANSPARENT;
- s['backgroundColor'] = s['backgroundColor'] || CanvasKit.TRANSPARENT;
s['decoration'] = s['decoration'] || 0;
s['decorationThickness'] = s['decorationThickness'] || 0;
s['fontSize'] = s['fontSize'] || 0;
- if (Array.isArray(s['fontFamilies']) && s['fontFamilies'].length) {
- var sPtr = naiveCopyStrArray(s['fontFamilies']);
- s['_fontFamilies'] = sPtr;
- s['_numFontFamilies'] = s['fontFamilies'].length;
- } else {
- s['_fontFamilies'] = nullptr;
- s['_numFontFamilies'] = 0;
- SkDebug("no font families provided, text may draw wrong or not at all")
- }
s['fontStyle'] = fontStyle(s['fontStyle']);
return s;
- }
+ };
// returns a pointer to a place on the heap that has an array
// of char* (effectively a char**). For now, this does the naive thing
@@ -100,50 +86,79 @@
}
var sPtrs = [];
for (var i = 0; i < strings.length; i++) {
- var str = strings[i];
- // Add 1 for null terminator, which we need when copying/converting
- var strLen = lengthBytesUTF8(str) + 1;
- var strPtr = CanvasKit._malloc(strLen);
- stringToUTF8(str, strPtr, strLen);
+ var strPtr = cacheOrCopyString(strings[i]);
sPtrs.push(strPtr);
}
- return copy1dArray(sPtrs, CanvasKit.HEAPU32);
+ return copy1dArray(sPtrs, "HEAPU32");
}
- function copyColors(textStyle) {
- // these two color fields were arrays, but will set to WASM pointers before we pass this
+ // maps string -> malloc'd pointer
+ var stringCache = {};
+
+ // cacheOrCopyString copies a string from JS into WASM on the heap and returns the pointer
+ // to the memory of the string. It is expected that a caller to this helper will *not* free
+ // that memory, so it is cached. Thus, if a future call to this function with the same string
+ // will return the cached pointer, preventing the memory usage from growing unbounded (in
+ // a normal use case).
+ function cacheOrCopyString(str) {
+ if (stringCache[str]) {
+ return stringCache[str];
+ }
+ // Add 1 for null terminator, which we need when copying/converting
+ var strLen = lengthBytesUTF8(str) + 1;
+ var strPtr = CanvasKit._malloc(strLen);
+ stringToUTF8(str, strPtr, strLen);
+ stringCache[str] = strPtr;
+ return strPtr;
+ }
+
+ // These scratch arrays are allocated once to copy the color data into, which saves us
+ // having to free them after every invocation.
+ var scratchForegroundColorPtr = CanvasKit._malloc(4 * 4); // room for 4 32bit floats
+ var scratchBackgroundColorPtr = CanvasKit._malloc(4 * 4); // room for 4 32bit floats
+
+ function copyArrays(textStyle) {
+ // These color fields were arrays, but will set to WASM pointers before we pass this
// object over the WASM interface.
- textStyle['colorPtr'] = copy1dArray(textStyle['color'], CanvasKit.HEAPF32);
- textStyle['foregroundColorPtr'] = nullptr; // nullptr is 0, from helper.js
- textStyle['backgroundColorPtr'] = nullptr;
+ textStyle['_colorPtr'] = copyColorToWasm(textStyle['color']);
+ textStyle['_foregroundColorPtr'] = nullptr; // nullptr is 0, from helper.js
+ textStyle['_backgroundColorPtr'] = nullptr;
- if (isCanvasKitColor(textStyle['foregroundColor']) && textStyle['foregroundColor'][3] > 0) {
- textStyle['foregroundColorPtr'] = copy1dArray(textStyle['foregroundColor'], CanvasKit.HEAPF32);
+ if (textStyle['foregroundColor']) {
+ textStyle['_foregroundColorPtr'] = copyColorToWasm(textStyle['foregroundColor'], scratchForegroundColorPtr);
+ }
+ if (textStyle['backgroundColor']) {
+ textStyle['_backgroundColorPtr'] = copyColorToWasm(textStyle['backgroundColor'], scratchBackgroundColorPtr);
}
- if (isCanvasKitColor(textStyle['backgroundColor']) && textStyle['backgroundColor'][3] > 0) {
- textStyle['backgroundColorPtr'] = copy1dArray(textStyle['backgroundColor'], CanvasKit.HEAPF32);
+
+ if (Array.isArray(textStyle['fontFamilies']) && textStyle['fontFamilies'].length) {
+ textStyle['_fontFamiliesPtr'] = naiveCopyStrArray(textStyle['fontFamilies']);
+ textStyle['_fontFamiliesLen'] = textStyle['fontFamilies'].length;
+ } else {
+ textStyle['_fontFamiliesPtr'] = nullptr;
+ textStyle['_fontFamiliesLen'] = 0;
+ SkDebug('no font families provided, text may draw wrong or not at all');
}
- return textStyle;
}
- function freeColors(textStyle) {
- CanvasKit._free(textStyle['colorPtr']);
- CanvasKit._free(textStyle['foregroundColorPtr']);
- CanvasKit._free(textStyle['backgroundColorPtr']);
+ function freeArrays(textStyle) {
+ // The font family strings will get copied to a vector on the C++ side, which is owned by
+ // the text style.
+ CanvasKit._free(textStyle['_fontFamiliesPtr']);
}
CanvasKit.ParagraphBuilder.Make = function(paragraphStyle, fontManager) {
- paragraphStyle['textStyle'] = copyColors(paragraphStyle['textStyle']);
+ copyArrays(paragraphStyle['textStyle']);
var result = CanvasKit.ParagraphBuilder._Make(paragraphStyle, fontManager);
- freeColors(paragraphStyle['textStyle']);
+ freeArrays(paragraphStyle['textStyle']);
return result;
- }
+ };
CanvasKit.ParagraphBuilder.prototype.pushStyle = function(textStyle) {
- var tmpStyle = copyColors(textStyle);
- this._pushStyle(tmpStyle);
- freeColors(tmpStyle);
+ copyArrays(textStyle);
+ this._pushStyle(textStyle);
+ freeArrays(textStyle);
}
});
-}(Module)); // When this file is loaded in, the high level object is "Module"; \ No newline at end of file
+}(Module)); // When this file is loaded in, the high level object is "Module";
diff --git a/chromium/third_party/skia/modules/canvaskit/paragraph_bindings.cpp b/chromium/third_party/skia/modules/canvaskit/paragraph_bindings.cpp
index 92f7b9ebbdf..93142217470 100644
--- a/chromium/third_party/skia/modules/canvaskit/paragraph_bindings.cpp
+++ b/chromium/third_party/skia/modules/canvaskit/paragraph_bindings.cpp
@@ -48,8 +48,8 @@ struct SimpleTextStyle {
SkScalar fontSize;
SimpleFontStyle fontStyle;
- uintptr_t /* const char** */ fontFamilies;
- int numFontFamilies;
+ uintptr_t /* const char** */ fontFamiliesPtr;
+ int fontFamiliesLen;
};
para::TextStyle toTextStyle(const SimpleTextStyle& s) {
@@ -80,10 +80,10 @@ para::TextStyle toTextStyle(const SimpleTextStyle& s) {
ts.setDecorationThicknessMultiplier(s.decorationThickness);
}
- const char** fontFamilies = reinterpret_cast<const char**>(s.fontFamilies);
- if (s.numFontFamilies > 0 && fontFamilies != nullptr) {
+ const char** fontFamilies = reinterpret_cast<const char**>(s.fontFamiliesPtr);
+ if (s.fontFamiliesLen > 0 && fontFamilies != nullptr) {
std::vector<SkString> ff;
- for (int i = 0; i< s.numFontFamilies; i++) {
+ for (int i = 0; i < s.fontFamiliesLen; i++) {
ff.emplace_back(fontFamilies[i]);
}
ts.setFontFamilies(ff);
@@ -279,15 +279,15 @@ EMSCRIPTEN_BINDINGS(Paragraph) {
.field("textStyle", &SimpleParagraphStyle::textStyle);
value_object<SimpleTextStyle>("TextStyle")
- .field("colorPtr", &SimpleTextStyle::colorPtr)
- .field("foregroundColorPtr", &SimpleTextStyle::foregroundColorPtr)
- .field("backgroundColorPtr", &SimpleTextStyle::backgroundColorPtr)
+ .field("_colorPtr", &SimpleTextStyle::colorPtr)
+ .field("_foregroundColorPtr", &SimpleTextStyle::foregroundColorPtr)
+ .field("_backgroundColorPtr", &SimpleTextStyle::backgroundColorPtr)
.field("decoration", &SimpleTextStyle::decoration)
.field("decorationThickness", &SimpleTextStyle::decorationThickness)
- .field("_fontFamilies", &SimpleTextStyle::fontFamilies)
+ .field("_fontFamiliesPtr", &SimpleTextStyle::fontFamiliesPtr)
+ .field("_fontFamiliesLen", &SimpleTextStyle::fontFamiliesLen)
.field("fontSize", &SimpleTextStyle::fontSize)
- .field("fontStyle", &SimpleTextStyle::fontStyle)
- .field("_numFontFamilies", &SimpleTextStyle::numFontFamilies);
+ .field("fontStyle", &SimpleTextStyle::fontStyle);
// The U stands for unsigned - we can't bind a generic/template object, so we have to specify it
// with the type we are using.
diff --git a/chromium/third_party/skia/modules/canvaskit/particles.js b/chromium/third_party/skia/modules/canvaskit/particles.js
index 830a164ce84..24ce748c348 100644
--- a/chromium/third_party/skia/modules/canvaskit/particles.js
+++ b/chromium/third_party/skia/modules/canvaskit/particles.js
@@ -38,9 +38,9 @@ CanvasKit.MakeParticles = function(json, assets) {
// Not entirely sure if it matters, but the uintptr_t are 32 bits
// we want to copy our array of uintptr_t into the right size memory.
- var namesPtr = copy1dArray(assetNamePtrs, CanvasKit.HEAPU32);
- var assetsPtr = copy1dArray(assetDataPtrs, CanvasKit.HEAPU32);
- var assetSizesPtr = copy1dArray(assetSizes, CanvasKit.HEAPU32);
+ var namesPtr = copy1dArray(assetNamePtrs, "HEAPU32");
+ var assetsPtr = copy1dArray(assetDataPtrs, "HEAPU32");
+ var assetSizesPtr = copy1dArray(assetSizes, "HEAPU32");
var particles = CanvasKit._MakeParticles(json, assetKeys.length,
namesPtr, assetsPtr, assetSizesPtr);
@@ -73,4 +73,4 @@ CanvasKit._extraInitializations.push(function() {
}
return new Float32Array(CanvasKit.HEAPU8.buffer, fptr, numFloats);
}
-}); \ No newline at end of file
+});
diff --git a/chromium/third_party/skia/modules/canvaskit/perf/assets/test_1500x959.jpg b/chromium/third_party/skia/modules/canvaskit/perf/assets/test_1500x959.jpg
new file mode 100644
index 00000000000..d27ee59c4aa
--- /dev/null
+++ b/chromium/third_party/skia/modules/canvaskit/perf/assets/test_1500x959.jpg
Binary files differ
diff --git a/chromium/third_party/skia/modules/canvaskit/perf/assets/test_512x512.png b/chromium/third_party/skia/modules/canvaskit/perf/assets/test_512x512.png
new file mode 100644
index 00000000000..c2efb8108df
--- /dev/null
+++ b/chromium/third_party/skia/modules/canvaskit/perf/assets/test_512x512.png
Binary files differ
diff --git a/chromium/third_party/skia/modules/canvaskit/perf/assets/test_64x64.png b/chromium/third_party/skia/modules/canvaskit/perf/assets/test_64x64.png
new file mode 100644
index 00000000000..a3bb32e9f22
--- /dev/null
+++ b/chromium/third_party/skia/modules/canvaskit/perf/assets/test_64x64.png
Binary files differ
diff --git a/chromium/third_party/skia/modules/canvaskit/perf/canvas.bench.js b/chromium/third_party/skia/modules/canvaskit/perf/canvas.bench.js
index 3e822a623c6..1117ad64f1c 100644
--- a/chromium/third_party/skia/modules/canvaskit/perf/canvas.bench.js
+++ b/chromium/third_party/skia/modules/canvaskit/perf/canvas.bench.js
@@ -38,6 +38,77 @@ describe('Basic Canvas ops', () => {
benchmarkAndReport('canvas_drawColor', setup, test, teardown);
});
+ it('can compute tonal colors', () => {
+ function setup(ctx) {};
+
+ function test(ctx) {
+ for (let i = 0; i < 10; i++) {
+ const input = {
+ ambient: randomColor(),
+ spot: randomColor(),
+ };
+ const out = CanvasKit.computeTonalColors(input);
+ if (out.spot[2] > 10 || out.ambient[3] > 10) {
+ // Something to make sure v8 can't optimize away the return value
+ throw 'not possible';
+ }
+ }
+ };
+
+ function teardown(ctx) {};
+
+ benchmarkAndReport('computeTonalColors', setup, test, teardown);
+ });
+
+ function randomColor() {
+ return CanvasKit.Color4f(Math.random(), Math.random(), Math.random(), Math.random());
+ }
+
+ it('can get and set the color to a paint', () => {
+ function setup(ctx) {
+ ctx.paint = new CanvasKit.SkPaint();
+ };
+
+ function test(ctx) {
+ for (let i = 0; i < 10; i++) {
+ ctx.paint.setColor(randomColor());
+ const color = ctx.paint.getColor();
+ if (color[3] > 4) {
+ // Something to make sure v8 can't optimize away the return value
+ throw 'not possible';
+ }
+ }
+ };
+
+ function teardown(ctx) {
+ ctx.paint.delete();
+ };
+
+ benchmarkAndReport('paint_setColor_getColor', setup, test, teardown);
+ });
+
+ it('can set the color to a paint by components', () => {
+ function setup(ctx) {
+ ctx.paint = new CanvasKit.SkPaint();
+ };
+
+ function test(ctx) {
+ const r = Math.random();
+ const g = Math.random();
+ const b = Math.random();
+ const a = Math.random();
+ for (let i = 0; i < 10000; i++) {
+ ctx.paint.setColorComponents(r, g, b, a);
+ }
+ };
+
+ function teardown(ctx) {
+ ctx.paint.delete();
+ };
+
+ benchmarkAndReport('paint_setColorComponents', setup, test, teardown);
+ });
+
it('can draw a shadow with tonal colors', () => {
function setup(ctx) {
ctx.surface = CanvasKit.MakeCanvasSurface('test');
@@ -50,7 +121,7 @@ describe('Basic Canvas ops', () => {
};
const lightRadius = 30;
const flags = 0;
- const lightPos = [250,150,300];
+ const lightPos = [250,150,300];
const zPlaneParams = [0,0,1];
const path = starPath(CanvasKit);
@@ -67,4 +138,100 @@ describe('Basic Canvas ops', () => {
benchmarkAndReport('canvas_drawShadow', setup, test, teardown);
});
+
+ it('can draw a gradient with an array of 1M colors', () => {
+ function setup(ctx) {
+ ctx.surface = CanvasKit.MakeCanvasSurface('test');
+ ctx.canvas = ctx.surface.getCanvas();
+ };
+
+ function test(ctx) {
+ ctx.canvas.clear(CanvasKit.WHITE);
+
+ const num = 1000000;
+ colors = Array(num);
+ positions = Array(num);
+ for (let i=0; i<num; i++) {
+ colors[i] = CanvasKit.Color((i*37)%255, 255, (1-i/num)*255);
+ positions[i] = i/num;
+ }
+
+ const shader = CanvasKit.SkShader.MakeRadialGradient(
+ [300, 300], 50, // center, radius
+ colors, positions,
+ CanvasKit.TileMode.Mirror,
+ );
+ const paint = new CanvasKit.SkPaint();
+ paint.setStyle(CanvasKit.PaintStyle.Fill);
+ paint.setShader(shader);
+ ctx.canvas.drawPaint(paint);
+ ctx.surface.flush();
+ paint.delete();
+ };
+
+ function teardown(ctx) {
+ ctx.surface.delete();
+ };
+
+ benchmarkAndReport('canvas_drawHugeGradient', setup, test, teardown);
+ });
+
+ function NO_OP() {}
+
+ function htmlImageElementToDataURL(htmlImageElement) {
+ const canvas = document.createElement('canvas');
+ canvas.height = htmlImageElement.height;
+ canvas.width = htmlImageElement.width;
+ const ctx = canvas.getContext('2d')
+ ctx.drawImage(htmlImageElement, 0, 0);
+ return canvas.toDataURL();
+ }
+
+ const TEST_IMAGE_FILENAMES = [ 'test_64x64.png', 'test_512x512.png', 'test_1500x959.jpg'];
+ // This for loop generates two perf cases for each test image. One uses browser APIs
+ // to decode an image, and the other uses codecs included in the CanvasKit wasm to decode an
+ // image. wasm codec Image decoding is faster (50 microseconds vs 20000 microseconds), but
+ // including codecs in wasm increases the size of the CanvasKit wasm binary.
+ for (const testImageFilename of TEST_IMAGE_FILENAMES) {
+ const imageResponsePromise = fetch(`/assets/${testImageFilename}`);
+ const imageArrayBufferPromise = imageResponsePromise.then((imageResponse) => imageResponse.arrayBuffer());
+
+ const htmlImageElement = new Image();
+ htmlImageElementLoadPromise = new Promise((resolve) => htmlImageElement.addEventListener('load', resolve));
+ // Create a data url of the image so that load and decode time can be measured
+ // while hopefully ignoring the time of getting the image from disk / the network.
+ imageDataURLPromise = htmlImageElementLoadPromise.then(() => htmlImageElementToDataURL(htmlImageElement));
+ htmlImageElement.src = `/assets/${testImageFilename}`;
+
+ it('can decode an image using HTMLImageElement and Canvas2D', async () => {
+ const imageDataURL = await imageDataURLPromise;
+
+ async function test(ctx) {
+ const image = new Image();
+ // Testing showed that waiting for the load event is faster than waiting for
+ // image.decode().
+ // HTMLImageElement.decode() reference: https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/decode
+ const promise = new Promise((resolve) => image.addEventListener('load', resolve));
+ image.src = imageDataURL;
+
+ await promise;
+
+ const img = await CanvasKit.MakeImageFromCanvasImageSource(image);
+ img.delete();
+ }
+
+ await asyncBenchmarkAndReport(`canvas_${testImageFilename}_HTMLImageElementDecoding`, NO_OP, test, NO_OP);
+ });
+
+ it('can decode an image using codecs in wasm', async () => {
+ const encodedArrayBuffer = await imageArrayBufferPromise;
+
+ function test(ctx) {
+ const img = CanvasKit.MakeImageFromEncoded(encodedArrayBuffer);
+ img.delete();
+ }
+
+ benchmarkAndReport(`canvas_${testImageFilename}_wasmImageDecoding`, NO_OP, test, NO_OP);
+ });
+ }
});
diff --git a/chromium/third_party/skia/modules/canvaskit/ready.js b/chromium/third_party/skia/modules/canvaskit/ready.js
deleted file mode 100644
index 60f24862d9e..00000000000
--- a/chromium/third_party/skia/modules/canvaskit/ready.js
+++ /dev/null
@@ -1,16 +0,0 @@
-// See https://github.com/kripken/emscripten/issues/5820#issuecomment-385722568
-// for context on why the .then() that comes with Module breaks things (e.g. infinite loops)
-// and why the below fixes it.
-Module['ready'] = function() {
- return new Promise(function (resolve, reject) {
- Module['onAbort'] = reject;
- if (runtimeInitialized) {
- resolve(Module);
- } else {
- addOnPostRun(function() {
- resolve(Module);
- });
- }
- });
-}
-delete Module['then']; \ No newline at end of file
diff --git a/chromium/third_party/skia/modules/canvaskit/rt_shader.js b/chromium/third_party/skia/modules/canvaskit/rt_shader.js
index c3771693d1a..6dc21f06963 100644
--- a/chromium/third_party/skia/modules/canvaskit/rt_shader.js
+++ b/chromium/third_party/skia/modules/canvaskit/rt_shader.js
@@ -2,19 +2,17 @@ CanvasKit._extraInitializations = CanvasKit._extraInitializations || [];
CanvasKit._extraInitializations.push(function() {
CanvasKit.SkRuntimeEffect.prototype.makeShader = function(floats, isOpaque, localMatrix) {
// We don't need to free these floats because they will become owned by the shader.
- var fptr = copy1dArray(floats, CanvasKit.HEAPF32);
+ var fptr = copy1dArray(floats, "HEAPF32");
var localMatrixPtr = copy3x3MatrixToWasm(localMatrix);
// Our array has 4 bytes per float, so be sure to account for that before
// sending it over the wire.
- var rts = this._makeShader(fptr, floats.length * 4, !!isOpaque, localMatrixPtr);
- CanvasKit._free(localMatrixPtr);
- return rts;
+ return this._makeShader(fptr, floats.length * 4, !!isOpaque, localMatrixPtr);
}
// childrenWithShaders is an array of other shaders (e.g. SkImage.makeShader())
CanvasKit.SkRuntimeEffect.prototype.makeShaderWithChildren = function(floats, isOpaque, childrenShaders, localMatrix) {
// We don't need to free these floats because they will become owned by the shader.
- var fptr = copy1dArray(floats, CanvasKit.HEAPF32);
+ var fptr = copy1dArray(floats, "HEAPF32");
var localMatrixPtr = copy3x3MatrixToWasm(localMatrix);
var barePointers = [];
for (var i = 0; i < childrenShaders.length; i++) {
@@ -22,12 +20,10 @@ CanvasKit._extraInitializations.push(function() {
// and send that over the wire, so it can be re-wrapped as an sk_sp.
barePointers.push(childrenShaders[i].$$.ptr);
}
- var childrenPointers = copy1dArray(barePointers, CanvasKit.HEAPU32);
+ var childrenPointers = copy1dArray(barePointers, "HEAPU32");
// Our array has 4 bytes per float, so be sure to account for that before
// sending it over the wire.
- var rts = this._makeShaderWithChildren(fptr, floats.length * 4, !!isOpaque, childrenPointers,
- barePointers.length, localMatrixPtr);
- CanvasKit._free(localMatrixPtr);
- return rts;
+ return this._makeShaderWithChildren(fptr, floats.length * 4, !!isOpaque, childrenPointers,
+ barePointers.length, localMatrixPtr);
}
-}); \ No newline at end of file
+});
diff --git a/chromium/third_party/skia/modules/canvaskit/skottie.js b/chromium/third_party/skia/modules/canvaskit/skottie.js
index a52470658b7..f07eea51b30 100644
--- a/chromium/third_party/skia/modules/canvaskit/skottie.js
+++ b/chromium/third_party/skia/modules/canvaskit/skottie.js
@@ -38,9 +38,9 @@ CanvasKit.MakeManagedAnimation = function(json, assets) {
// Not entirely sure if it matters, but the uintptr_t are 32 bits
// we want to copy our array of uintptr_t into the right size memory.
- var namesPtr = copy1dArray(assetNamePtrs, CanvasKit.HEAPU32);
- var assetsPtr = copy1dArray(assetDataPtrs, CanvasKit.HEAPU32);
- var assetSizesPtr = copy1dArray(assetSizes, CanvasKit.HEAPU32);
+ var namesPtr = copy1dArray(assetNamePtrs, "HEAPU32");
+ var assetsPtr = copy1dArray(assetDataPtrs, "HEAPU32");
+ var assetSizesPtr = copy1dArray(assetSizes, "HEAPU32");
var anim = CanvasKit._MakeManagedAnimation(json, assetKeys.length, namesPtr,
assetsPtr, assetSizesPtr);
@@ -58,10 +58,9 @@ CanvasKit.MakeManagedAnimation = function(json, assets) {
CanvasKit._extraInitializations.push(function() {
CanvasKit.ManagedAnimation.prototype.setColor = function(key, color) {
- var cPtr = copy1dArray(color, CanvasKit.HEAPF32);
+ var cPtr = copyColorToWasm(color);
this._setColor(key, cPtr);
- CanvasKit._free(cPtr);
}
});
-}(Module)); // When this file is loaded in, the high level object is "Module"; \ No newline at end of file
+}(Module)); // When this file is loaded in, the high level object is "Module";
diff --git a/chromium/third_party/skia/modules/canvaskit/viewer_bindings.cpp b/chromium/third_party/skia/modules/canvaskit/viewer_bindings.cpp
index 66466ef5883..c73408e47f5 100644
--- a/chromium/third_party/skia/modules/canvaskit/viewer_bindings.cpp
+++ b/chromium/third_party/skia/modules/canvaskit/viewer_bindings.cpp
@@ -8,24 +8,66 @@
#include <emscripten.h>
#include <emscripten/bind.h>
#include "include/core/SkCanvas.h"
+#include "include/core/SkSurface.h"
+#include "include/gpu/GrContext.h"
+#include "tools/skui/InputState.h"
+#include "tools/skui/ModifierKey.h"
+#include "tools/viewer/SKPSlide.h"
#include "tools/viewer/SampleSlide.h"
+#include "tools/viewer/SvgSlide.h"
+#include <GLES3/gl3.h>
#include <string>
using namespace emscripten;
+static sk_sp<Slide> MakeSlide(std::string name) {
+ if (name == "PathText") {
+ extern Sample* MakePathTextSample();
+ return sk_make_sp<SampleSlide>(MakePathTextSample);
+ }
+ if (name == "TessellatedWedge") {
+ extern Sample* MakeTessellatedWedgeSample();
+ return sk_make_sp<SampleSlide>(MakeTessellatedWedgeSample);
+ }
+ return nullptr;
+}
+
+static sk_sp<Slide> MakeSkpSlide(std::string name, std::string skpData) {
+ auto stream = std::make_unique<SkMemoryStream>(skpData.data(), skpData.size(),
+ /*copyData=*/true);
+ return sk_make_sp<SKPSlide>(SkString(name.c_str()), std::move(stream));
+}
+
+static sk_sp<Slide> MakeSvgSlide(std::string name, std::string svgText) {
+ auto stream = std::make_unique<SkMemoryStream>(svgText.data(), svgText.size(),
+ /*copyData=*/true);
+ return sk_make_sp<SvgSlide>(SkString(name.c_str()), std::move(stream));
+}
+
EMSCRIPTEN_BINDINGS(Viewer) {
- function("MakeSlide", optional_override([](std::string name)->sk_sp<Slide> {
- if (name == "WavyPathText") {
- extern Sample* MakeWavyPathTextSample();
- return sk_make_sp<SampleSlide>(MakeWavyPathTextSample);
- }
- return nullptr;
- }));
+ function("MakeSlide", &MakeSlide);
+ function("MakeSkpSlide", &MakeSkpSlide);
+ function("MakeSvgSlide", &MakeSvgSlide);
class_<Slide>("Slide")
.smart_ptr<sk_sp<Slide>>("sk_sp<Slide>")
.function("load", &Slide::load)
.function("animate", &Slide::animate)
.function("draw", optional_override([](Slide& self, SkCanvas& canvas) {
self.draw(&canvas);
- }));
+ }))
+ .function("onChar", &Slide::onChar)
+ .function("onMouse", &Slide::onMouse);
+ enum_<skui::InputState>("InputState")
+ .value("Down", skui::InputState::kDown)
+ .value("Up", skui::InputState::kUp)
+ .value("Move", skui::InputState::kMove)
+ .value("Right", skui::InputState::kRight)
+ .value("Left", skui::InputState::kLeft);
+ enum_<skui::ModifierKey>("ModifierKey")
+ .value("None", skui::ModifierKey::kNone)
+ .value("Shift", skui::ModifierKey::kShift)
+ .value("Control", skui::ModifierKey::kControl)
+ .value("Option", skui::ModifierKey::kOption)
+ .value("Command", skui::ModifierKey::kCommand)
+ .value("FirstPress", skui::ModifierKey::kFirstPress);
}
diff --git a/chromium/third_party/skia/modules/particles/src/SkParticleEffect.cpp b/chromium/third_party/skia/modules/particles/src/SkParticleEffect.cpp
index 1cca0608bfc..55c1bba74dc 100644
--- a/chromium/third_party/skia/modules/particles/src/SkParticleEffect.cpp
+++ b/chromium/third_party/skia/modules/particles/src/SkParticleEffect.cpp
@@ -121,6 +121,7 @@ void SkParticleEffectParams::prepare(const skresources::ResourceProvider* resour
auto buildProgram = [this](const SkSL::String& code, Program* p) {
SkSL::Compiler compiler;
SkSL::Program::Settings settings;
+ settings.fRemoveDeadFunctions = false;
SkTArray<std::unique_ptr<SkParticleExternalValue>> externalValues;
diff --git a/chromium/third_party/skia/modules/pathkit/CHANGELOG.md b/chromium/third_party/skia/modules/pathkit/CHANGELOG.md
index dc0b62811cb..9b968bd55cc 100644
--- a/chromium/third_party/skia/modules/pathkit/CHANGELOG.md
+++ b/chromium/third_party/skia/modules/pathkit/CHANGELOG.md
@@ -7,7 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Changed
- - Updated to emscripten 1.39.6
+ - Updated to emscripten 1.39.16
+
+### Breaking
+ - `PathKitInit(...)` now directly returns a Promise. As such, `PathKitInit(...).ready()`
+ has been removed.
## [0.6.0] 2019-02-25
diff --git a/chromium/third_party/skia/modules/pathkit/compile.sh b/chromium/third_party/skia/modules/pathkit/compile.sh
index 7d4660d321c..32cd53c0afc 100755
--- a/chromium/third_party/skia/modules/pathkit/compile.sh
+++ b/chromium/third_party/skia/modules/pathkit/compile.sh
@@ -93,7 +93,7 @@ echo "Compiling bitcode"
--args="cc=\"${EMCC}\" \
cxx=\"${EMCXX}\" \
ar=\"${EMAR}\" \
- extra_cflags=[\"-DSK_DISABLE_READBUFFER=1\",\"-s\", \"WARN_UNALIGNED=1\",
+ extra_cflags=[\"-s\", \"WARN_UNALIGNED=1\",
\"-s\", \"MAIN_MODULE=1\",
${EXTRA_CFLAGS}
] \
@@ -110,10 +110,9 @@ echo "Generating WASM"
${EMCXX} $RELEASE_CONF -std=c++17 \
-I. \
--bind \
+--no-entry \
--pre-js $BASE_DIR/helper.js \
--pre-js $BASE_DIR/chaining.js \
---post-js $BASE_DIR/ready.js \
--DSK_DISABLE_READBUFFER=1 \
-fno-rtti -fno-exceptions -DEMSCRIPTEN_HAS_UNBOUND_TYPE_NAMES=0 \
$WASM_CONF \
-s ERROR_ON_UNDEFINED_SYMBOLS=1 \
diff --git a/chromium/third_party/skia/modules/pathkit/npm-asmjs/README.md b/chromium/third_party/skia/modules/pathkit/npm-asmjs/README.md
index ff71af1b764..ac05297a4aa 100644
--- a/chromium/third_party/skia/modules/pathkit/npm-asmjs/README.md
+++ b/chromium/third_party/skia/modules/pathkit/npm-asmjs/README.md
@@ -5,7 +5,7 @@ To use the library, run `npm install pathkit-asmjs` and then simply include it:
<script src="/node_modules/pathkit-asmjs/bin/pathkit.js"></script>
PathKitInit({
locateFile: (file) => '/node_modules/pathkit-asmjs/bin/'+file,
- }).ready().then((PathKit) => {
+ }).then((PathKit) => {
// Code goes here using PathKit
});
@@ -14,7 +14,7 @@ a global `PathKitInit` that can be called to load the WASM code. The `locateFile
is used to tell the JS loader where to find the .js.mem file. By default, it will
look for /pathkit.js.mem, so if this is not the case, use `locateFile` to configure
this properly.
-The `PathKit` object returned upon resolution of the .ready() Promise is fully loaded and ready to use.
+The `PathKit` object returned upon resolution of the PathKitInit Promise is fully loaded and ready to use.
See the [API page](https://skia.org/user/modules/pathkit) and
[example.html](https://github.com/google/skia/blob/master/modules/pathkit/npm-asmjs/example.html)
@@ -29,7 +29,7 @@ used with just a few configuration changes.
In the JS code, use require():
const PathKitInit = require('pathkit-asmjs/bin/pathkit.js')
- PathKitInit().ready().then((PathKit) => {
+ PathKitInit().then((PathKit) => {
// Code goes here using PathKit
})
diff --git a/chromium/third_party/skia/modules/pathkit/npm-asmjs/example.html b/chromium/third_party/skia/modules/pathkit/npm-asmjs/example.html
index a2fc4c18902..c50c453e1af 100644
--- a/chromium/third_party/skia/modules/pathkit/npm-asmjs/example.html
+++ b/chromium/third_party/skia/modules/pathkit/npm-asmjs/example.html
@@ -53,7 +53,7 @@
PathKitInit({
locateFile: (file) => '/node_modules/pathkit-asmjs/bin/'+file,
- }).ready().then((PathKit) => {
+ }).then((PathKit) => {
window.PathKit = PathKit;
OutputsExample(PathKit);
Path2DExample(PathKit);
diff --git a/chromium/third_party/skia/modules/pathkit/npm-wasm/README.md b/chromium/third_party/skia/modules/pathkit/npm-wasm/README.md
index 815e417d8f5..2e7a6d74b83 100644
--- a/chromium/third_party/skia/modules/pathkit/npm-wasm/README.md
+++ b/chromium/third_party/skia/modules/pathkit/npm-wasm/README.md
@@ -5,7 +5,7 @@ To use the library, run `npm install pathkit-wasm` and then simply include it:
<script src="/node_modules/pathkit-wasm/bin/pathkit.js"></script>
PathKitInit({
locateFile: (file) => '/node_modules/pathkit-wasm/bin/'+file,
- }).ready().then((PathKit) => {
+ }).then((PathKit) => {
// Code goes here using PathKit
});
@@ -14,7 +14,7 @@ a global `PathKitInit` that can be called to load the WASM code. The `locateFile
is used to tell the JS loader where to find the .wasm file. By default, it will
look for /pathkit.wasm, so if this is not the case, use `locateFile` to configure
this properly.
-The `PathKit` object returned upon resolution of the .ready() Promise is fully loaded and ready to use.
+The `PathKit` object returned upon resolution of the PathKitInit Promise is fully loaded and ready to use.
See the [API page](https://skia.org/user/modules/pathkit) and
[example.html](https://github.com/google/skia/blob/master/modules/pathkit/npm-wasm/example.html)
@@ -29,7 +29,7 @@ used with a few configuration changes.
In the JS code, use require():
const PathKitInit = require('pathkit-wasm/bin/pathkit.js')
- PathKitInit().ready().then((PathKit) => {
+ PathKitInit().then((PathKit) => {
// Code goes here using PathKit
})
diff --git a/chromium/third_party/skia/modules/pathkit/npm-wasm/example.html b/chromium/third_party/skia/modules/pathkit/npm-wasm/example.html
index d7c2469a992..488f6ecbed8 100644
--- a/chromium/third_party/skia/modules/pathkit/npm-wasm/example.html
+++ b/chromium/third_party/skia/modules/pathkit/npm-wasm/example.html
@@ -53,7 +53,7 @@
PathKitInit({
locateFile: (file) => '/node_modules/pathkit-wasm/bin/'+file,
- }).ready().then((PathKit) => {
+ }).then((PathKit) => {
window.PathKit = PathKit;
OutputsExample(PathKit);
Path2DExample(PathKit);
diff --git a/chromium/third_party/skia/modules/pathkit/pathkit_wasm_bindings.cpp b/chromium/third_party/skia/modules/pathkit/pathkit_wasm_bindings.cpp
index 56db3e186dc..30ecd21ffd5 100644
--- a/chromium/third_party/skia/modules/pathkit/pathkit_wasm_bindings.cpp
+++ b/chromium/third_party/skia/modules/pathkit/pathkit_wasm_bindings.cpp
@@ -259,31 +259,36 @@ SkPathOrNull EMSCRIPTEN_KEEPALIVE ResolveBuilder(SkOpBuilder& builder) {
//========================================================================================
void EMSCRIPTEN_KEEPALIVE ToCanvas(const SkPath& path, emscripten::val /* Path2D or Canvas*/ ctx) {
- for (auto [verb, pts, w] : SkPathPriv::Iterate(path)) {
+ SkPath::Iter iter(path, false);
+ SkPoint pts[4];
+ SkPath::Verb verb;
+ while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
switch (verb) {
- case SkPathVerb::kMove:
+ case SkPath::kMove_Verb:
ctx.call<void>("moveTo", pts[0].x(), pts[0].y());
break;
- case SkPathVerb::kLine:
+ case SkPath::kLine_Verb:
ctx.call<void>("lineTo", pts[1].x(), pts[1].y());
break;
- case SkPathVerb::kQuad:
+ case SkPath::kQuad_Verb:
ctx.call<void>("quadraticCurveTo", pts[1].x(), pts[1].y(), pts[2].x(), pts[2].y());
break;
- case SkPathVerb::kConic:
+ case SkPath::kConic_Verb:
SkPoint quads[5];
// approximate with 2^1=2 quads.
- SkPath::ConvertConicToQuads(pts[0], pts[1], pts[2], *w, quads, 1);
+ SkPath::ConvertConicToQuads(pts[0], pts[1], pts[2], iter.conicWeight(), quads, 1);
ctx.call<void>("quadraticCurveTo", quads[1].x(), quads[1].y(), quads[2].x(), quads[2].y());
ctx.call<void>("quadraticCurveTo", quads[3].x(), quads[3].y(), quads[4].x(), quads[4].y());
break;
- case SkPathVerb::kCubic:
+ case SkPath::kCubic_Verb:
ctx.call<void>("bezierCurveTo", pts[1].x(), pts[1].y(), pts[2].x(), pts[2].y(),
pts[3].x(), pts[3].y());
break;
- case SkPathVerb::kClose:
+ case SkPath::kClose_Verb:
ctx.call<void>("closePath");
break;
+ case SkPath::kDone_Verb:
+ break;
}
}
}
diff --git a/chromium/third_party/skia/modules/pathkit/perf/perfReporter.js b/chromium/third_party/skia/modules/pathkit/perf/perfReporter.js
index 35fd3590bc5..d2211d231cb 100644
--- a/chromium/third_party/skia/modules/pathkit/perf/perfReporter.js
+++ b/chromium/third_party/skia/modules/pathkit/perf/perfReporter.js
@@ -51,6 +51,55 @@ function benchmarkAndReport(benchName, setupFn, testFn, teardownFn) {
}
}
+// The same as benchmarkAndReport, except expects the third parameter, testFn, to return a promise
+async function asyncBenchmarkAndReport(benchName, setupFn, testFn, teardownFn) {
+ try {
+ let ctx = {};
+ // warmup 3 times (arbitrary choice)
+ setupFn(ctx);
+ await testFn(ctx);
+ await testFn(ctx);
+ await testFn(ctx);
+ teardownFn(ctx);
+
+ ctx = {};
+ setupFn(ctx);
+ let start = Date.now();
+ let now = start;
+ times = 0;
+ // See how many times we can do it in 100ms (arbitrary choice)
+ while (now - start < 100) {
+ await testFn(ctx);
+ now = Date.now();
+ times++;
+ }
+
+ teardownFn(ctx);
+
+ // Try to make it go for 2 seconds (arbitrarily chosen)
+ // Since the pre-try took 100ms, multiply by 20 to get
+ // approximate tries in 2s (unless now - start >> 100 ms)
+ let goalTimes = times * 20;
+ ctx = {};
+ setupFn(ctx);
+ times = 0;
+ start = Date.now();
+ while (times < goalTimes) {
+ await testFn(ctx);
+ times++;
+ }
+ const end = Date.now();
+ teardownFn(ctx);
+
+ const us = (end - start) * 1000 / times;
+ console.log(benchName, `${us} microseconds`)
+ return _report(us, benchName);
+ } catch(e) {
+ console.error('caught error', e);
+ return Promise.reject(e);
+ }
+}
+
function _report(microseconds, benchName) {
return fetch(REPORT_URL, {
diff --git a/chromium/third_party/skia/modules/pathkit/ready.js b/chromium/third_party/skia/modules/pathkit/ready.js
deleted file mode 100644
index 60f24862d9e..00000000000
--- a/chromium/third_party/skia/modules/pathkit/ready.js
+++ /dev/null
@@ -1,16 +0,0 @@
-// See https://github.com/kripken/emscripten/issues/5820#issuecomment-385722568
-// for context on why the .then() that comes with Module breaks things (e.g. infinite loops)
-// and why the below fixes it.
-Module['ready'] = function() {
- return new Promise(function (resolve, reject) {
- Module['onAbort'] = reject;
- if (runtimeInitialized) {
- resolve(Module);
- } else {
- addOnPostRun(function() {
- resolve(Module);
- });
- }
- });
-}
-delete Module['then']; \ No newline at end of file
diff --git a/chromium/third_party/skia/modules/skottie/gm/ExternalProperties.cpp b/chromium/third_party/skia/modules/skottie/gm/ExternalProperties.cpp
index 786a95d0d7e..0b82ba5654e 100644
--- a/chromium/third_party/skia/modules/skottie/gm/ExternalProperties.cpp
+++ b/chromium/third_party/skia/modules/skottie/gm/ExternalProperties.cpp
@@ -25,14 +25,15 @@ static constexpr char kSkottieResource[] = "skottie/skottie_sample_webfont.json"
// Dummy web font loader which serves a single local font (checked in under resources/).
class FakeWebFontProvider final : public skresources::ResourceProvider {
public:
- FakeWebFontProvider() : fFontData(GetResourceAsData(kWebFontResource)) {}
+ FakeWebFontProvider()
+ : fTypeface(SkTypeface::MakeFromData(GetResourceAsData(kWebFontResource))) {}
- sk_sp<SkData> loadFont(const char[], const char[]) const override {
- return fFontData;
+ sk_sp<SkTypeface> loadTypeface(const char[], const char[]) const override {
+ return fTypeface;
}
private:
- sk_sp<SkData> fFontData;
+ sk_sp<SkTypeface> fTypeface;
using INHERITED = skresources::ResourceProvider;
};
diff --git a/chromium/third_party/skia/modules/skottie/gm/SkottieGM.cpp b/chromium/third_party/skia/modules/skottie/gm/SkottieGM.cpp
index 03b0b757841..6365d179b31 100644
--- a/chromium/third_party/skia/modules/skottie/gm/SkottieGM.cpp
+++ b/chromium/third_party/skia/modules/skottie/gm/SkottieGM.cpp
@@ -25,14 +25,15 @@ static constexpr char kSkottieResource[] = "skottie/skottie_sample_webfont.json"
// Dummy web font loader which serves a single local font (checked in under resources/).
class FakeWebFontProvider final : public skresources::ResourceProvider {
public:
- FakeWebFontProvider() : fFontData(GetResourceAsData(kWebFontResource)) {}
+ FakeWebFontProvider()
+ : fTypeface(SkTypeface::MakeFromData(GetResourceAsData(kWebFontResource))) {}
- sk_sp<SkData> loadFont(const char[], const char[]) const override {
- return fFontData;
+ sk_sp<SkTypeface> loadTypeface(const char[], const char[]) const override {
+ return fTypeface;
}
private:
- sk_sp<SkData> fFontData;
+ sk_sp<SkTypeface> fTypeface;
using INHERITED = skresources::ResourceProvider;
};
diff --git a/chromium/third_party/skia/modules/skottie/include/Skottie.h b/chromium/third_party/skia/modules/skottie/include/Skottie.h
index 6e7d643b806..c519068b430 100644
--- a/chromium/third_party/skia/modules/skottie/include/Skottie.h
+++ b/chromium/third_party/skia/modules/skottie/include/Skottie.h
@@ -14,6 +14,7 @@
#include "include/core/SkString.h"
#include "include/core/SkTypes.h"
#include "modules/skottie/include/ExternalLayer.h"
+#include "modules/skottie/include/SkottieProperty.h"
#include "modules/skresources/include/SkResources.h"
#include <memory>
@@ -39,8 +40,6 @@ namespace internal { class Animator; }
using ImageAsset = skresources::ImageAsset;
using ResourceProvider = skresources::ResourceProvider;
-class PropertyObserver;
-
/**
* A Logger subclass can be used to receive Animation::Builder parsing errors and warnings.
*/
@@ -68,9 +67,11 @@ public:
class Builder final {
public:
enum Flags : uint32_t {
- kDeferImageLoading = 0x01, // Normally, all static image frames are resolved at
- // load time via ImageAsset::getFrame(0). With this flag,
- // frames are only resolved when needed, at seek() time.
+ kDeferImageLoading = 0x01, // Normally, all static image frames are resolved at
+ // load time via ImageAsset::getFrame(0). With this flag,
+ // frames are only resolved when needed, at seek() time.
+ kPreferEmbeddedFonts = 0x02, // Attempt to use the embedded fonts (glyph paths,
+ // normally used as fallback) over native Skia typefaces.
};
explicit Builder(uint32_t flags = 0);
diff --git a/chromium/third_party/skia/modules/skottie/src/Layer.cpp b/chromium/third_party/skia/modules/skottie/src/Layer.cpp
index 3a7e1630173..575e0b2d5c9 100644
--- a/chromium/third_party/skia/modules/skottie/src/Layer.cpp
+++ b/chromium/third_party/skia/modules/skottie/src/Layer.cpp
@@ -275,9 +275,10 @@ private:
LayerBuilder::LayerBuilder(const skjson::ObjectValue& jlayer)
: fJlayer(jlayer)
- , fIndex(ParseDefault<int>(jlayer["ind"], -1))
+ , fIndex (ParseDefault<int>(jlayer["ind" ], -1))
, fParentIndex(ParseDefault<int>(jlayer["parent"], -1))
- , fType(ParseDefault<int>(jlayer["ty"], -1)) {
+ , fType (ParseDefault<int>(jlayer["ty" ], -1))
+ , fAutoOrient (ParseDefault<int>(jlayer["ao" ], 0)) {
if (this->isCamera() || ParseDefault<int>(jlayer["ddd"], 0)) {
fFlags |= Flags::kIs3D;
@@ -363,8 +364,8 @@ sk_sp<sksg::Transform> LayerBuilder::doAttachTransform(const AnimationBuilder& a
}
return this->is3D()
- ? abuilder.attachMatrix3D(*jtransform, std::move(parent_transform))
- : abuilder.attachMatrix2D(*jtransform, std::move(parent_transform));
+ ? abuilder.attachMatrix3D(*jtransform, std::move(parent_transform), fAutoOrient)
+ : abuilder.attachMatrix2D(*jtransform, std::move(parent_transform), fAutoOrient);
}
bool LayerBuilder::hasMotionBlur(const CompositionBuilder* cbuilder) const {
diff --git a/chromium/third_party/skia/modules/skottie/src/Layer.h b/chromium/third_party/skia/modules/skottie/src/Layer.h
index 3a1f6f2bf6f..5d29b203287 100644
--- a/chromium/third_party/skia/modules/skottie/src/Layer.h
+++ b/chromium/third_party/skia/modules/skottie/src/Layer.h
@@ -63,6 +63,7 @@ private:
const int fIndex;
const int fParentIndex;
const int fType;
+ const bool fAutoOrient;
sk_sp<sksg::Transform> fLayerTransform; // this layer's transform node.
sk_sp<sksg::Transform> fTransformCache[2]; // cached 2D/3D chain for the local node
diff --git a/chromium/third_party/skia/modules/skottie/src/SkottiePriv.h b/chromium/third_party/skia/modules/skottie/src/SkottiePriv.h
index b12b8e4c67d..398ecd795ae 100644
--- a/chromium/third_party/skia/modules/skottie/src/SkottiePriv.h
+++ b/chromium/third_party/skia/modules/skottie/src/SkottiePriv.h
@@ -14,6 +14,7 @@
#include "include/core/SkString.h"
#include "include/core/SkTypeface.h"
#include "include/private/SkTHash.h"
+#include "include/utils/SkCustomTypeface.h"
#include "modules/skottie/include/SkottieProperty.h"
#include "modules/skottie/src/animator/Animator.h"
#include "modules/sksg/include/SkSGScene.h"
@@ -63,10 +64,12 @@ public:
AnimationInfo parse(const skjson::ObjectValue&);
struct FontInfo {
- SkString fFamily,
- fStyle;
- SkScalar fAscentPct;
- sk_sp<SkTypeface> fTypeface;
+ SkString fFamily,
+ fStyle,
+ fPath;
+ SkScalar fAscentPct;
+ sk_sp<SkTypeface> fTypeface;
+ SkCustomTypefaceBuilder fCustomBuilder;
bool matches(const char family[], const char style[]) const;
};
@@ -74,8 +77,10 @@ public:
void log(Logger::Level, const skjson::Value*, const char fmt[], ...) const;
- sk_sp<sksg::Transform> attachMatrix2D(const skjson::ObjectValue&, sk_sp<sksg::Transform>) const;
- sk_sp<sksg::Transform> attachMatrix3D(const skjson::ObjectValue&, sk_sp<sksg::Transform>) const;
+ sk_sp<sksg::Transform> attachMatrix2D(const skjson::ObjectValue&, sk_sp<sksg::Transform>,
+ bool auto_orient = false) const;
+ sk_sp<sksg::Transform> attachMatrix3D(const skjson::ObjectValue&, sk_sp<sksg::Transform>,
+ bool auto_orient = false) const;
sk_sp<sksg::Transform> attachCamera(const skjson::ObjectValue& jlayer,
const skjson::ObjectValue& jtransform,
@@ -182,6 +187,10 @@ private:
void parseFonts (const skjson::ObjectValue* jfonts,
const skjson::ArrayValue* jchars);
+ // Return true iff all fonts were resolved.
+ bool resolveNativeTypefaces();
+ bool resolveEmbeddedTypefaces(const skjson::ArrayValue& jchars);
+
void dispatchMarkers(const skjson::ArrayValue*) const;
sk_sp<sksg::RenderNode> attachBlendMode(const skjson::ObjectValue&,
diff --git a/chromium/third_party/skia/modules/skottie/src/SkottieTool.cpp b/chromium/third_party/skia/modules/skottie/src/SkottieTool.cpp
index e546c593b89..9ce6288e4c9 100644
--- a/chromium/third_party/skia/modules/skottie/src/SkottieTool.cpp
+++ b/chromium/third_party/skia/modules/skottie/src/SkottieTool.cpp
@@ -12,6 +12,7 @@
#include "include/core/SkSurface.h"
#include "include/encode/SkPngEncoder.h"
#include "modules/skottie/include/Skottie.h"
+#include "modules/skottie/utils/SkottieUtils.h"
#include "modules/skresources/include/SkResources.h"
#include "src/core/SkOSFile.h"
#include "src/core/SkTaskGroup.h"
@@ -45,6 +46,8 @@ static DEFINE_int(threads, 0, "Number of worker threads (0 -> cores count).");
namespace {
+static constexpr SkColor kClearColor = SK_ColorWHITE;
+
std::unique_ptr<SkFILEWStream> MakeFrameStream(size_t idx, const char* ext) {
const auto frame_file = SkStringPrintf("0%06zu.%s", idx, ext);
auto stream = std::make_unique<SkFILEWStream>(SkOSPath::Join(FLAGS_writePath[0],
@@ -87,7 +90,7 @@ private:
SkCanvas* beginFrame(size_t) override {
auto* canvas = fSurface->getCanvas();
- canvas->clear(SK_ColorTRANSPARENT);
+ canvas->clear(kClearColor);
return canvas;
}
@@ -161,7 +164,7 @@ private:
SkCanvas* beginFrame(size_t) override {
auto* canvas = fSurface->getCanvas();
- canvas->clear(SK_ColorTRANSPARENT);
+ canvas->clear(kClearColor);
return canvas;
}
@@ -182,7 +185,7 @@ struct MP4Sink final : public Sink {
SkCanvas* beginFrame(size_t) override {
SkCanvas* canvas = fSurface->getCanvas();
- canvas->clear(SK_ColorTRANSPARENT);
+ canvas->clear(kClearColor);
return canvas;
}
@@ -265,6 +268,8 @@ int main(int argc, char** argv) {
/*predecode=*/true),
/*predecode=*/true));
auto data = SkData::MakeFromFileName(FLAGS_input[0]);
+ auto precomp_interceptor =
+ sk_make_sp<skottie_utils::ExternalAnimationPrecompInterceptor>(rp, "__");
if (!data) {
SkDebugf("Could not load %s.\n", FLAGS_input[0]);
@@ -334,12 +339,14 @@ int main(int argc, char** argv) {
// iOS doesn't support thread_local on versions less than 9.0.
auto anim = skottie::Animation::Builder()
.setResourceProvider(rp)
+ .setPrecompInterceptor(precomp_interceptor)
.make(static_cast<const char*>(data->data()), data->size());
auto sink = MakeSink(FLAGS_format[0], scale_matrix);
#else
thread_local static auto* anim =
skottie::Animation::Builder()
.setResourceProvider(rp)
+ .setPrecompInterceptor(precomp_interceptor)
.make(static_cast<const char*>(data->data()), data->size())
.release();
thread_local static auto* sink = MakeSink(FLAGS_format[0], scale_matrix).release();
diff --git a/chromium/third_party/skia/modules/skottie/src/Transform.cpp b/chromium/third_party/skia/modules/skottie/src/Transform.cpp
index ff8b45c6188..7548233bf54 100644
--- a/chromium/third_party/skia/modules/skottie/src/Transform.cpp
+++ b/chromium/third_party/skia/modules/skottie/src/Transform.cpp
@@ -20,15 +20,18 @@ TransformAdapter2D::TransformAdapter2D(const AnimationBuilder& abuilder,
const skjson::ObjectValue* jscale,
const skjson::ObjectValue* jrotation,
const skjson::ObjectValue* jskew,
- const skjson::ObjectValue* jskew_axis)
+ const skjson::ObjectValue* jskew_axis,
+ bool auto_orient)
: INHERITED(sksg::Matrix<SkMatrix>::Make(SkMatrix::I())) {
this->bind(abuilder, janchor_point, fAnchorPoint);
- this->bind(abuilder, jposition , fPosition);
this->bind(abuilder, jscale , fScale);
this->bind(abuilder, jrotation , fRotation);
this->bind(abuilder, jskew , fSkew);
this->bind(abuilder, jskew_axis , fSkewAxis);
+
+ this->bindAutoOrientable(abuilder, jposition, &fPosition, auto_orient ? &fOrientation
+ : nullptr);
}
TransformAdapter2D::~TransformAdapter2D() {}
@@ -38,14 +41,11 @@ void TransformAdapter2D::onSync() {
}
SkMatrix TransformAdapter2D::totalMatrix() const {
- SkMatrix t = SkMatrix::MakeTrans(-fAnchorPoint.x, -fAnchorPoint.y);
-
- t.postScale(fScale.x / 100, fScale.y / 100); // 100% based
- t.postRotate(fRotation);
- t.postTranslate(fPosition.x, fPosition.y);
// TODO: skew
-
- return t;
+ return SkMatrix::Translate(fPosition.x, fPosition.y)
+ * SkMatrix::RotateDeg(fRotation + fOrientation)
+ * SkMatrix::Scale (fScale.x / 100, fScale.y / 100) // 100% based
+ * SkMatrix::Translate(-fAnchorPoint.x, -fAnchorPoint.y);
}
SkPoint TransformAdapter2D::getAnchorPoint() const {
@@ -91,7 +91,8 @@ void TransformAdapter2D::setSkewAxis(float sa) {
}
sk_sp<sksg::Transform> AnimationBuilder::attachMatrix2D(const skjson::ObjectValue& jtransform,
- sk_sp<sksg::Transform> parent) const {
+ sk_sp<sksg::Transform> parent,
+ bool auto_orient) const {
const auto* jrotation = &jtransform["r"];
if (jrotation->is<skjson::NullValue>()) {
// Some 2D rotations are disguised as 3D...
@@ -104,7 +105,8 @@ sk_sp<sksg::Transform> AnimationBuilder::attachMatrix2D(const skjson::ObjectValu
jtransform["s"],
*jrotation,
jtransform["sk"],
- jtransform["sa"]);
+ jtransform["sa"],
+ auto_orient);
SkASSERT(adapter);
const auto dispatched = this->dispatchTransformProperty(adapter);
@@ -172,7 +174,8 @@ SkM44 TransformAdapter3D::totalMatrix() const {
}
sk_sp<sksg::Transform> AnimationBuilder::attachMatrix3D(const skjson::ObjectValue& jtransform,
- sk_sp<sksg::Transform> parent) const {
+ sk_sp<sksg::Transform> parent,
+ bool /*TODO: auto_orient*/) const {
auto adapter = TransformAdapter3D::Make(jtransform, *this);
SkASSERT(adapter);
diff --git a/chromium/third_party/skia/modules/skottie/src/Transform.h b/chromium/third_party/skia/modules/skottie/src/Transform.h
index bf688e34c9d..16a6ebee9a0 100644
--- a/chromium/third_party/skia/modules/skottie/src/Transform.h
+++ b/chromium/third_party/skia/modules/skottie/src/Transform.h
@@ -33,7 +33,8 @@ public:
const skjson::ObjectValue* jscale,
const skjson::ObjectValue* jrotation,
const skjson::ObjectValue* jskew,
- const skjson::ObjectValue* jskew_axis);
+ const skjson::ObjectValue* jskew_axis,
+ bool auto_orient = false);
~TransformAdapter2D() override;
// Accessors needed for public property APIs.
@@ -66,7 +67,8 @@ private:
fScale = { 100, 100 };
ScalarValue fRotation = 0,
fSkew = 0,
- fSkewAxis = 0;
+ fSkewAxis = 0,
+ fOrientation = 0; // additional rotation component controlled by auto-orient
using INHERITED = DiscardableAdapterBase<TransformAdapter2D, sksg::Matrix<SkMatrix>>;
};
diff --git a/chromium/third_party/skia/modules/skottie/src/animator/Animator.cpp b/chromium/third_party/skia/modules/skottie/src/animator/Animator.cpp
index 231ff514022..3e891476f8b 100644
--- a/chromium/third_party/skia/modules/skottie/src/animator/Animator.cpp
+++ b/chromium/third_party/skia/modules/skottie/src/animator/Animator.cpp
@@ -49,8 +49,7 @@ void AnimatablePropertyContainer::shrink_to_fit() {
bool AnimatablePropertyContainer::bindImpl(const AnimationBuilder& abuilder,
const skjson::ObjectValue* jprop,
- KeyframeAnimatorBuilder& builder,
- void* target_value) {
+ KeyframeAnimatorBuilder& builder) {
if (!jprop) {
return false;
}
@@ -65,7 +64,7 @@ bool AnimatablePropertyContainer::bindImpl(const AnimationBuilder& abuilder,
// Older Json versions don't have an "a" animation marker.
// For those, we attempt to parse both ways.
if (!ParseDefault<bool>(jpropA, false)) {
- if (builder.parseValue(abuilder, jpropK, target_value)) {
+ if (builder.parseValue(abuilder, jpropK)) {
// Static property.
return true;
}
@@ -81,7 +80,7 @@ bool AnimatablePropertyContainer::bindImpl(const AnimationBuilder& abuilder,
sk_sp<KeyframeAnimator> animator;
const skjson::ArrayValue* jkfs = jpropK;
if (jkfs && jkfs->size() > 0) {
- animator = builder.make(abuilder, *jkfs, target_value);
+ animator = builder.make(abuilder, *jkfs);
}
if (!animator) {
diff --git a/chromium/third_party/skia/modules/skottie/src/animator/Animator.h b/chromium/third_party/skia/modules/skottie/src/animator/Animator.h
index 2c1918b89f9..399d98c5caa 100644
--- a/chromium/third_party/skia/modules/skottie/src/animator/Animator.h
+++ b/chromium/third_party/skia/modules/skottie/src/animator/Animator.h
@@ -12,6 +12,8 @@
#include <vector>
+struct SkV2;
+
namespace skjson {
class ObjectValue;
@@ -54,6 +56,12 @@ public:
return this->bind<T>(abuilder, jobject, &v);
}
+ // A flavor of bind<Vec2Value> which drives an additional/optional orientation target
+ // (rotation in degrees), when bound to a motion path property.
+ bool bindAutoOrientable(const AnimationBuilder& abuilder,
+ const skjson::ObjectValue* jobject,
+ SkV2* v, float* orientation);
+
bool isStatic() const { return fAnimators.empty(); }
protected:
@@ -66,10 +74,7 @@ protected:
private:
StateChanged onSeek(float) final;
- bool bindImpl(const AnimationBuilder&,
- const skjson::ObjectValue*,
- KeyframeAnimatorBuilder&,
- void*);
+ bool bindImpl(const AnimationBuilder&, const skjson::ObjectValue*, KeyframeAnimatorBuilder&);
std::vector<sk_sp<Animator>> fAnimators;
bool fHasSynced = false;
diff --git a/chromium/third_party/skia/modules/skottie/src/animator/KeyframeAnimator.h b/chromium/third_party/skia/modules/skottie/src/animator/KeyframeAnimator.h
index 35eee98b595..d9955e4376b 100644
--- a/chromium/third_party/skia/modules/skottie/src/animator/KeyframeAnimator.h
+++ b/chromium/third_party/skia/modules/skottie/src/animator/KeyframeAnimator.h
@@ -108,11 +108,9 @@ class KeyframeAnimatorBuilder : public SkNoncopyable {
public:
virtual ~KeyframeAnimatorBuilder();
- virtual sk_sp<KeyframeAnimator> make(const AnimationBuilder&,
- const skjson::ArrayValue&,
- void* target_value) = 0;
+ virtual sk_sp<KeyframeAnimator> make(const AnimationBuilder&, const skjson::ArrayValue&) = 0;
- virtual bool parseValue(const AnimationBuilder&, const skjson::Value&, void*) const = 0;
+ virtual bool parseValue(const AnimationBuilder&, const skjson::Value&) const = 0;
protected:
virtual bool parseKFValue(const AnimationBuilder&,
diff --git a/chromium/third_party/skia/modules/skottie/src/animator/ScalarKeyframeAnimator.cpp b/chromium/third_party/skia/modules/skottie/src/animator/ScalarKeyframeAnimator.cpp
index e9e6bd52039..d0babc50779 100644
--- a/chromium/third_party/skia/modules/skottie/src/animator/ScalarKeyframeAnimator.cpp
+++ b/chromium/third_party/skia/modules/skottie/src/animator/ScalarKeyframeAnimator.cpp
@@ -19,22 +19,21 @@ class ScalarKeyframeAnimator final : public KeyframeAnimator {
public:
class Builder final : public KeyframeAnimatorBuilder {
public:
+ explicit Builder(ScalarValue* target) : fTarget(target) {}
+
sk_sp<KeyframeAnimator> make(const AnimationBuilder& abuilder,
- const skjson::ArrayValue& jkfs,
- void* target_value) override {
+ const skjson::ArrayValue& jkfs) override {
SkASSERT(jkfs.size() > 0);
if (!this->parseKeyframes(abuilder, jkfs)) {
return nullptr;
}
return sk_sp<ScalarKeyframeAnimator>(
- new ScalarKeyframeAnimator(std::move(fKFs),
- std::move(fCMs),
- static_cast<ScalarValue*>(target_value)));
+ new ScalarKeyframeAnimator(std::move(fKFs), std::move(fCMs), fTarget));
}
- bool parseValue(const AnimationBuilder&, const skjson::Value& jv, void* v) const override {
- return Parse(jv, static_cast<float*>(v));
+ bool parseValue(const AnimationBuilder&, const skjson::Value& jv) const override {
+ return Parse(jv, fTarget);
}
private:
@@ -44,6 +43,8 @@ public:
Keyframe::Value* v) override {
return Parse(jv, &v->flt);
}
+
+ ScalarValue* fTarget;
};
private:
@@ -73,9 +74,9 @@ template <>
bool AnimatablePropertyContainer::bind<ScalarValue>(const AnimationBuilder& abuilder,
const skjson::ObjectValue* jprop,
ScalarValue* v) {
- ScalarKeyframeAnimator::Builder builder;
+ ScalarKeyframeAnimator::Builder builder(v);
- return this->bindImpl(abuilder, jprop, builder, v);
+ return this->bindImpl(abuilder, jprop, builder);
}
} // namespace skottie::internal
diff --git a/chromium/third_party/skia/modules/skottie/src/animator/ShapeKeyframeAnimator.cpp b/chromium/third_party/skia/modules/skottie/src/animator/ShapeKeyframeAnimator.cpp
index b5f6fee532c..b53dc1ba859 100644
--- a/chromium/third_party/skia/modules/skottie/src/animator/ShapeKeyframeAnimator.cpp
+++ b/chromium/third_party/skia/modules/skottie/src/animator/ShapeKeyframeAnimator.cpp
@@ -169,9 +169,9 @@ template <>
bool AnimatablePropertyContainer::bind<ShapeValue>(const AnimationBuilder& abuilder,
const skjson::ObjectValue* jprop,
ShapeValue* v) {
- VectorKeyframeAnimatorBuilder builder(parse_encoding_len, parse_encoding_data);
+ VectorKeyframeAnimatorBuilder builder(v, parse_encoding_len, parse_encoding_data);
- return this->bindImpl(abuilder, jprop, builder, v);
+ return this->bindImpl(abuilder, jprop, builder);
}
} // namespace internal
diff --git a/chromium/third_party/skia/modules/skottie/src/animator/TextKeyframeAnimator.cpp b/chromium/third_party/skia/modules/skottie/src/animator/TextKeyframeAnimator.cpp
index e1f8d14256c..8fb1ad1fe87 100644
--- a/chromium/third_party/skia/modules/skottie/src/animator/TextKeyframeAnimator.cpp
+++ b/chromium/third_party/skia/modules/skottie/src/animator/TextKeyframeAnimator.cpp
@@ -18,9 +18,10 @@ class TextKeyframeAnimator final : public KeyframeAnimator {
public:
class Builder final : public KeyframeAnimatorBuilder {
public:
+ explicit Builder(TextValue* target) : fTarget(target) {}
+
sk_sp<KeyframeAnimator> make(const AnimationBuilder& abuilder,
- const skjson::ArrayValue& jkfs,
- void* target_value) override {
+ const skjson::ArrayValue& jkfs) override {
SkASSERT(jkfs.size() > 0);
fValues.reserve(jkfs.size());
@@ -33,12 +34,11 @@ public:
new TextKeyframeAnimator(std::move(fKFs),
std::move(fCMs),
std::move(fValues),
- static_cast<TextValue*>(target_value)));
+ fTarget));
}
- bool parseValue(const AnimationBuilder& abuilder,
- const skjson::Value& jv, void* v) const override {
- return Parse(jv, abuilder, static_cast<TextValue*>(v));
+ bool parseValue(const AnimationBuilder& abuilder, const skjson::Value& jv) const override {
+ return Parse(jv, abuilder, fTarget);
}
private:
@@ -62,6 +62,7 @@ public:
}
std::vector<TextValue> fValues;
+ TextValue* fTarget;
};
private:
@@ -95,8 +96,8 @@ template <>
bool AnimatablePropertyContainer::bind<TextValue>(const AnimationBuilder& abuilder,
const skjson::ObjectValue* jprop,
TextValue* v) {
- TextKeyframeAnimator::Builder builder;
- return this->bindImpl(abuilder, jprop, builder, v);
+ TextKeyframeAnimator::Builder builder(v);
+ return this->bindImpl(abuilder, jprop, builder);
}
} // namespace skottie::internal
diff --git a/chromium/third_party/skia/modules/skottie/src/animator/Vec2KeyframeAnimator.cpp b/chromium/third_party/skia/modules/skottie/src/animator/Vec2KeyframeAnimator.cpp
index 6e531cd45f5..7907203156f 100644
--- a/chromium/third_party/skia/modules/skottie/src/animator/Vec2KeyframeAnimator.cpp
+++ b/chromium/third_party/skia/modules/skottie/src/animator/Vec2KeyframeAnimator.cpp
@@ -11,6 +11,8 @@
#include "modules/skottie/src/animator/Animator.h"
#include "modules/skottie/src/animator/KeyframeAnimator.h"
+#include <cmath>
+
namespace skottie::internal {
namespace {
@@ -25,9 +27,12 @@ class Vec2KeyframeAnimator final : public KeyframeAnimator {
public:
class Builder final : public KeyframeAnimatorBuilder {
public:
+ Builder(Vec2Value* vec_target, float* rot_target)
+ : fVecTarget(vec_target)
+ , fRotTarget(rot_target) {}
+
sk_sp<KeyframeAnimator> make(const AnimationBuilder& abuilder,
- const skjson::ArrayValue& jkfs,
- void* target_value) override {
+ const skjson::ArrayValue& jkfs) override {
SkASSERT(jkfs.size() > 0);
fValues.reserve(jkfs.size());
@@ -40,11 +45,12 @@ public:
new Vec2KeyframeAnimator(std::move(fKFs),
std::move(fCMs),
std::move(fValues),
- static_cast<Vec2Value*>(target_value)));
+ fVecTarget,
+ fRotTarget));
}
- bool parseValue(const AnimationBuilder&, const skjson::Value& jv, void* v) const override {
- return Parse(jv, static_cast<Vec2Value*>(v));
+ bool parseValue(const AnimationBuilder&, const skjson::Value& jv) const override {
+ return Parse(jv, fVecTarget);
}
private:
@@ -120,20 +126,30 @@ public:
}
std::vector<SpatialValue> fValues;
+ Vec2Value* fVecTarget; // required
+ float* fRotTarget; // optional
SkV2 fTi{0,0},
fTo{0,0};
};
private:
Vec2KeyframeAnimator(std::vector<Keyframe> kfs, std::vector<SkCubicMap> cms,
- std::vector<SpatialValue> vs, Vec2Value* target_value)
+ std::vector<SpatialValue> vs, Vec2Value* vec_target, float* rot_target)
: INHERITED(std::move(kfs), std::move(cms))
, fValues(std::move(vs))
- , fTarget(target_value) {}
-
- StateChanged update(const Vec2Value& new_value) {
- const auto changed = (new_value != *fTarget);
- *fTarget = new_value;
+ , fVecTarget(vec_target)
+ , fRotTarget(rot_target) {}
+
+ StateChanged update(const Vec2Value& new_vec_value, const Vec2Value& new_tan_value) {
+ auto changed = (new_vec_value != *fVecTarget);
+ *fVecTarget = new_vec_value;
+
+ if (fRotTarget) {
+ const auto new_rot_value = SkRadiansToDegrees(std::atan2(new_tan_value.y,
+ new_tan_value.x));
+ changed |= new_rot_value != *fRotTarget;
+ *fRotTarget = new_rot_value;
+ }
return changed;
}
@@ -145,36 +161,39 @@ private:
if (v0.cmeasure) {
// Spatial keyframe: the computed weight is relative to the interpolation path
// arc length.
- SkPoint pos;
- if (v0.cmeasure->getPosTan(lerp_info.weight * v0.cmeasure->length(), &pos, nullptr)) {
- return this->update({ pos.fX, pos.fY });
+ SkPoint pos;
+ SkVector tan;
+ if (v0.cmeasure->getPosTan(lerp_info.weight * v0.cmeasure->length(), &pos, &tan)) {
+ return this->update({ pos.fX, pos.fY }, {tan.fX, tan.fY});
}
}
const auto& v1 = fValues[lerp_info.vrec1.idx];
- return this->update(Lerp(v0.v2, v1.v2, lerp_info.weight));
+ const auto tan = v1.v2 - v0.v2;
+
+ return this->update(Lerp(v0.v2, v1.v2, lerp_info.weight), tan);
}
const std::vector<SpatialValue> fValues;
- Vec2Value* fTarget;
+ Vec2Value* fVecTarget;
+ float* fRotTarget;
using INHERITED = KeyframeAnimator;
};
} // namespace
-template <>
-bool AnimatablePropertyContainer::bind<Vec2Value>(const AnimationBuilder& abuilder,
- const skjson::ObjectValue* jprop,
- Vec2Value* v) {
+bool AnimatablePropertyContainer::bindAutoOrientable(const AnimationBuilder& abuilder,
+ const skjson::ObjectValue* jprop,
+ Vec2Value* v, float* orientation) {
if (!jprop) {
return false;
}
if (!ParseDefault<bool>((*jprop)["s"], false)) {
// Regular (static or keyframed) 2D value.
- Vec2KeyframeAnimator::Builder builder;
- return this->bindImpl(abuilder, jprop, builder, v);
+ Vec2KeyframeAnimator::Builder builder(v, orientation);
+ return this->bindImpl(abuilder, jprop, builder);
}
// Separate-dimensions vector value: each component is animated independently.
@@ -182,4 +201,11 @@ bool AnimatablePropertyContainer::bind<Vec2Value>(const AnimationBuilder& abuild
| this->bind(abuilder, (*jprop)["y"], &v->y);
}
+template <>
+bool AnimatablePropertyContainer::bind<Vec2Value>(const AnimationBuilder& abuilder,
+ const skjson::ObjectValue* jprop,
+ Vec2Value* v) {
+ return this->bindAutoOrientable(abuilder, jprop, v, nullptr);
+}
+
} // namespace skottie::internal
diff --git a/chromium/third_party/skia/modules/skottie/src/animator/VectorKeyframeAnimator.cpp b/chromium/third_party/skia/modules/skottie/src/animator/VectorKeyframeAnimator.cpp
index 23a482688d5..008b751efc2 100644
--- a/chromium/third_party/skia/modules/skottie/src/animator/VectorKeyframeAnimator.cpp
+++ b/chromium/third_party/skia/modules/skottie/src/animator/VectorKeyframeAnimator.cpp
@@ -44,16 +44,7 @@ VectorValue::operator SkV3() const {
}
VectorValue::operator SkColor() const {
- // best effort to turn this into a color
- const auto r = this->size() > 0 ? (*this)[0] : 0,
- g = this->size() > 1 ? (*this)[1] : 0,
- b = this->size() > 2 ? (*this)[2] : 0,
- a = this->size() > 3 ? (*this)[3] : 1;
-
- return SkColorSetARGB(SkScalarRoundToInt(SkTPin(a, 0.0f, 1.0f) * 255),
- SkScalarRoundToInt(SkTPin(r, 0.0f, 1.0f) * 255),
- SkScalarRoundToInt(SkTPin(g, 0.0f, 1.0f) * 255),
- SkScalarRoundToInt(SkTPin(b, 0.0f, 1.0f) * 255));
+ return static_cast<SkColor4f>(*this).toSkColor();
}
VectorValue::operator SkColor4f() const {
@@ -84,7 +75,7 @@ public:
std::vector<SkCubicMap> cms,
std::vector<float> storage,
size_t vec_len,
- VectorValue* target_value)
+ std::vector<float>* target_value)
: INHERITED(std::move(kfs), std::move(cms))
, fStorage(std::move(storage))
, fVecLen(vec_len)
@@ -143,21 +134,22 @@ private:
const std::vector<float> fStorage;
const size_t fVecLen;
- VectorValue* fTarget;
+ std::vector<float>* fTarget;
using INHERITED = KeyframeAnimator;
};
} // namespace
-VectorKeyframeAnimatorBuilder::VectorKeyframeAnimatorBuilder(VectorLenParser parse_len,
+VectorKeyframeAnimatorBuilder::VectorKeyframeAnimatorBuilder(std::vector<float>* target,
+ VectorLenParser parse_len,
VectorDataParser parse_data)
: fParseLen(parse_len)
- , fParseData(parse_data) {}
+ , fParseData(parse_data)
+ , fTarget(target) {}
sk_sp<KeyframeAnimator> VectorKeyframeAnimatorBuilder::make(const AnimationBuilder& abuilder,
- const skjson::ArrayValue& jkfs,
- void* target_value) {
+ const skjson::ArrayValue& jkfs) {
SkASSERT(jkfs.size() > 0);
// peek at the first keyframe value to find our vector length
@@ -190,20 +182,18 @@ sk_sp<KeyframeAnimator> VectorKeyframeAnimatorBuilder::make(const AnimationBuild
std::move(fCMs),
std::move(fStorage),
fVecLen,
- static_cast<VectorValue*>(target_value)));
+ fTarget));
}
bool VectorKeyframeAnimatorBuilder::parseValue(const AnimationBuilder&,
- const skjson::Value& jv,
- void* raw_v) const {
+ const skjson::Value& jv) const {
size_t vec_len;
if (!this->fParseLen(jv, &vec_len)) {
return false;
}
- auto* v = static_cast<VectorValue*>(raw_v);
- v->resize(vec_len);
- return fParseData(jv, vec_len, v->data());
+ fTarget->resize(vec_len);
+ return fParseData(jv, vec_len, fTarget->data());
}
bool VectorKeyframeAnimatorBuilder::parseKFValue(const AnimationBuilder&,
@@ -246,6 +236,7 @@ bool AnimatablePropertyContainer::bind<VectorValue>(const AnimationBuilder& abui
if (!ParseDefault<bool>((*jprop)["s"], false)) {
// Regular (static or keyframed) vector value.
VectorKeyframeAnimatorBuilder builder(
+ v,
// Len parser.
[](const skjson::Value& jv, size_t* len) -> bool {
if (const skjson::ArrayValue* ja = jv) {
@@ -259,7 +250,7 @@ bool AnimatablePropertyContainer::bind<VectorValue>(const AnimationBuilder& abui
return parse_array(jv, data, len);
});
- return this->bindImpl(abuilder, jprop, builder, v);
+ return this->bindImpl(abuilder, jprop, builder);
}
// Separate-dimensions vector value: each component is animated independently.
diff --git a/chromium/third_party/skia/modules/skottie/src/animator/VectorKeyframeAnimator.h b/chromium/third_party/skia/modules/skottie/src/animator/VectorKeyframeAnimator.h
index b23ac4690d9..5b8c209e6fc 100644
--- a/chromium/third_party/skia/modules/skottie/src/animator/VectorKeyframeAnimator.h
+++ b/chromium/third_party/skia/modules/skottie/src/animator/VectorKeyframeAnimator.h
@@ -19,16 +19,12 @@ public:
using VectorLenParser = bool(*)(const skjson::Value&, size_t*);
using VectorDataParser = bool(*)(const skjson::Value&, size_t, float*);
- VectorKeyframeAnimatorBuilder(VectorLenParser, VectorDataParser);
+ VectorKeyframeAnimatorBuilder(std::vector<float>*, VectorLenParser, VectorDataParser);
- sk_sp<KeyframeAnimator> make(const AnimationBuilder&,
- const skjson::ArrayValue&,
- void*) override;
+ sk_sp<KeyframeAnimator> make(const AnimationBuilder&, const skjson::ArrayValue&) override;
private:
- bool parseValue(const AnimationBuilder&,
- const skjson::Value&,
- void*) const override;
+ bool parseValue(const AnimationBuilder&, const skjson::Value&) const override;
bool parseKFValue(const AnimationBuilder&,
const skjson::ObjectValue&,
@@ -42,6 +38,7 @@ private:
size_t fVecLen, // size of individual vector values we store
fCurrentVec = 0; // vector value index being parsed (corresponding
// storage offset is fCurrentVec * fVecLen)
+ std::vector<float>* fTarget;
};
} // namespace skottie::internal
diff --git a/chromium/third_party/skia/modules/skottie/src/effects/MotionTileEffect.cpp b/chromium/third_party/skia/modules/skottie/src/effects/MotionTileEffect.cpp
index bc2c31f7f88..71be1636beb 100644
--- a/chromium/third_party/skia/modules/skottie/src/effects/MotionTileEffect.cpp
+++ b/chromium/third_party/skia/modules/skottie/src/effects/MotionTileEffect.cpp
@@ -95,7 +95,7 @@ protected:
const auto phase_shift = SkVector::Make(phase_vec.fX / layerShaderMatrix.getScaleX(),
phase_vec.fY / layerShaderMatrix.getScaleY())
* std::fmod(fPhase * (1/360.0f), 1);
- const auto phase_shader_matrix = SkMatrix::MakeTrans(phase_shift.x(), phase_shift.y());
+ const auto phase_shader_matrix = SkMatrix::Translate(phase_shift.x(), phase_shift.y());
// The mask is generated using a step gradient shader, spanning 2 x tile width/height,
// and perpendicular to the phase vector.
diff --git a/chromium/third_party/skia/modules/skottie/src/layers/TextLayer.cpp b/chromium/third_party/skia/modules/skottie/src/layers/TextLayer.cpp
index 39b9e3d0890..35e9dfe9ca0 100644
--- a/chromium/third_party/skia/modules/skottie/src/layers/TextLayer.cpp
+++ b/chromium/third_party/skia/modules/skottie/src/layers/TextLayer.cpp
@@ -17,6 +17,7 @@
#include "modules/sksg/include/SkSGDraw.h"
#include "modules/sksg/include/SkSGGroup.h"
#include "modules/sksg/include/SkSGPaint.h"
+#include "modules/sksg/include/SkSGPath.h"
#include "modules/sksg/include/SkSGText.h"
#include <string.h>
@@ -93,6 +94,70 @@ SkFontStyle FontStyle(const AnimationBuilder* abuilder, const char* style) {
return SkFontStyle(weight, SkFontStyle::kNormal_Width, slant);
}
+bool parse_glyph_path(const skjson::ObjectValue* jdata,
+ const AnimationBuilder* abuilder,
+ SkPath* path) {
+ // Glyph path encoding:
+ //
+ // "data": {
+ // "shapes": [ // follows the shape layer format
+ // {
+ // "ty": "gr", // group shape type
+ // "it": [ // group items
+ // {
+ // "ty": "sh", // actual shape
+ // "ks": <path data> // animatable path format, but always static
+ // },
+ // ...
+ // ]
+ // },
+ // ...
+ // ]
+ // }
+
+ if (!jdata) {
+ return false;
+ }
+
+ const skjson::ArrayValue* jshapes = (*jdata)["shapes"];
+ if (!jshapes) {
+ // Space/empty glyph.
+ return true;
+ }
+
+ for (const skjson::ObjectValue* jgrp : *jshapes) {
+ if (!jgrp) {
+ return false;
+ }
+
+ const skjson::ArrayValue* jit = (*jgrp)["it"];
+ if (!jit) {
+ return false;
+ }
+
+ for (const skjson::ObjectValue* jshape : *jit) {
+ if (!jshape) {
+ return false;
+ }
+
+ // Glyph paths should never be animated. But they are encoded as
+ // animatable properties, so we use the appropriate helpers.
+ AnimationBuilder::AutoScope ascope(abuilder);
+ auto path_node = abuilder->attachPath((*jshape)["ks"]);
+ auto animators = ascope.release();
+
+ if (!path_node || !animators.empty()) {
+ return false;
+ }
+
+ // Successfully parsed a static path. Whew.
+ path->addPath(path_node->getPath());
+ }
+ }
+
+ return true;
+}
+
} // namespace
bool AnimationBuilder::FontInfo::matches(const char family[], const char style[]) const {
@@ -127,129 +192,204 @@ void AnimationBuilder::parseFonts(const skjson::ObjectValue* jfonts,
// }
// ]
// },
- if (jfonts) {
- if (const skjson::ArrayValue* jlist = (*jfonts)["list"]) {
- for (const skjson::ObjectValue* jfont : *jlist) {
- if (!jfont) {
- continue;
- }
+ const skjson::ArrayValue* jlist = jfonts
+ ? static_cast<const skjson::ArrayValue*>((*jfonts)["list"])
+ : nullptr;
+ if (!jlist) {
+ return;
+ }
+
+ // First pass: collect font info.
+ for (const skjson::ObjectValue* jfont : *jlist) {
+ if (!jfont) {
+ continue;
+ }
- const skjson::StringValue* jname = (*jfont)["fName"];
- const skjson::StringValue* jfamily = (*jfont)["fFamily"];
- const skjson::StringValue* jstyle = (*jfont)["fStyle"];
- const skjson::StringValue* jpath = (*jfont)["fPath"];
+ const skjson::StringValue* jname = (*jfont)["fName"];
+ const skjson::StringValue* jfamily = (*jfont)["fFamily"];
+ const skjson::StringValue* jstyle = (*jfont)["fStyle"];
+ const skjson::StringValue* jpath = (*jfont)["fPath"];
- if (!jname || !jname->size() ||
- !jfamily || !jfamily->size() ||
- !jstyle || !jstyle->size()) {
- this->log(Logger::Level::kError, jfont, "Invalid font.");
- continue;
- }
+ if (!jname || !jname->size() ||
+ !jfamily || !jfamily->size() ||
+ !jstyle || !jstyle->size()) {
+ this->log(Logger::Level::kError, jfont, "Invalid font.");
+ continue;
+ }
+
+ fFonts.set(SkString(jname->begin(), jname->size()),
+ {
+ SkString(jfamily->begin(), jfamily->size()),
+ SkString( jstyle->begin(), jstyle->size()),
+ jpath ? SkString( jpath->begin(), jpath->size()) : SkString(),
+ ParseDefault((*jfont)["ascent"] , 0.0f),
+ nullptr, // placeholder
+ SkCustomTypefaceBuilder()
+ });
+ }
+
+ // Optional pass.
+ if (jchars && (fFlags & Animation::Builder::kPreferEmbeddedFonts) &&
+ this->resolveEmbeddedTypefaces(*jchars)) {
+ return;
+ }
- const auto& fmgr = fLazyFontMgr.get();
+ // Native typeface resolution.
+ if (this->resolveNativeTypefaces()) {
+ return;
+ }
- // Typeface fallback order:
- // 1) externally-loaded font (provided by the embedder)
- // 2) system font (family/style)
- // 3) system default
+ // Embedded typeface fallback.
+ if (jchars && !(fFlags & Animation::Builder::kPreferEmbeddedFonts) &&
+ this->resolveEmbeddedTypefaces(*jchars)) {
+ }
+}
- sk_sp<SkTypeface> tf =
- fmgr->makeFromData(fResourceProvider->loadFont(jname->begin(),
- jpath ? jpath->begin()
- : nullptr));
+bool AnimationBuilder::resolveNativeTypefaces() {
+ bool has_unresolved = false;
- if (!tf) {
- tf.reset(fmgr->matchFamilyStyle(jfamily->begin(),
- FontStyle(this, jstyle->begin())));
- }
+ fFonts.foreach([&](const SkString& name, FontInfo* finfo) {
+ SkASSERT(finfo);
- if (!tf) {
- this->log(Logger::Level::kError, nullptr,
- "Could not create typeface for %s|%s.",
- jfamily->begin(), jstyle->begin());
- // Last resort.
- tf = fmgr->legacyMakeTypeface(nullptr, FontStyle(this, jstyle->begin()));
- if (!tf) {
- continue;
- }
- }
+ if (finfo->fTypeface) {
+ // Already resolved from glyph paths.
+ return;
+ }
+
+ const auto& fmgr = fLazyFontMgr.get();
+
+ // Typeface fallback order:
+ // 1) externally-loaded font (provided by the embedder)
+ // 2) system font (family/style)
+ // 3) system default
- fFonts.set(SkString(jname->begin(), jname->size()),
- {
- SkString(jfamily->begin(), jfamily->size()),
- SkString(jstyle->begin(), jstyle->size()),
- ParseDefault((*jfont)["ascent"] , 0.0f),
- std::move(tf)
- });
+ finfo->fTypeface = fResourceProvider->loadTypeface(name.c_str(), finfo->fPath.c_str());
+
+ // legacy API fallback
+ // TODO: remove after client migration
+ if (!finfo->fTypeface) {
+ finfo->fTypeface = fmgr->makeFromData(
+ fResourceProvider->loadFont(name.c_str(), finfo->fPath.c_str()));
+ }
+
+ if (!finfo->fTypeface) {
+ finfo->fTypeface.reset(fmgr->matchFamilyStyle(finfo->fFamily.c_str(),
+ FontStyle(this, finfo->fStyle.c_str())));
+
+ if (!finfo->fTypeface) {
+ this->log(Logger::Level::kError, nullptr, "Could not create typeface for %s|%s.",
+ finfo->fFamily.c_str(), finfo->fStyle.c_str());
+ // Last resort.
+ finfo->fTypeface = fmgr->legacyMakeTypeface(nullptr,
+ FontStyle(this, finfo->fStyle.c_str()));
+
+ has_unresolved |= !finfo->fTypeface;
}
}
- }
+ });
+ return !has_unresolved;
+}
+
+bool AnimationBuilder::resolveEmbeddedTypefaces(const skjson::ArrayValue& jchars) {
// Optional array of glyphs, to be associated with one of the declared fonts. E.g.
// "chars": [
// {
// "ch": "t",
// "data": {
- // "shapes": [...]
+ // "shapes": [...] // shape-layer-like geometry
// },
- // "fFamily": "Roboto",
- // "size": 50,
- // "style": "Regular",
- // "w": 32.67
+ // "fFamily": "Roboto", // part of the font key
+ // "size": 50, // apparently ignored
+ // "style": "Regular", // part of the font key
+ // "w": 32.67 // width/advance (1/100 units)
// }
// ]
- if (jchars) {
- FontInfo* current_font = nullptr;
+ FontInfo* current_font = nullptr;
- for (const skjson::ObjectValue* jchar : *jchars) {
- if (!jchar) {
- continue;
- }
+ for (const skjson::ObjectValue* jchar : jchars) {
+ if (!jchar) {
+ continue;
+ }
- const skjson::StringValue* jch = (*jchar)["ch"];
- if (!jch) {
- continue;
- }
+ const skjson::StringValue* jch = (*jchar)["ch"];
+ if (!jch) {
+ continue;
+ }
- const skjson::StringValue* jfamily = (*jchar)["fFamily"];
- const skjson::StringValue* jstyle = (*jchar)["style"]; // "style", not "fStyle"...
+ const skjson::StringValue* jfamily = (*jchar)["fFamily"];
+ const skjson::StringValue* jstyle = (*jchar)["style"]; // "style", not "fStyle"...
- const auto* ch_ptr = jch->begin();
- const auto ch_len = jch->size();
+ const auto* ch_ptr = jch->begin();
+ const auto ch_len = jch->size();
- if (!jfamily || !jstyle || (SkUTF::CountUTF8(ch_ptr, ch_len) != 1)) {
- this->log(Logger::Level::kError, jchar, "Invalid glyph.");
- continue;
- }
+ if (!jfamily || !jstyle || (SkUTF::CountUTF8(ch_ptr, ch_len) != 1)) {
+ this->log(Logger::Level::kError, jchar, "Invalid glyph.");
+ continue;
+ }
- const auto uni = SkUTF::NextUTF8(&ch_ptr, ch_ptr + ch_len);
- SkASSERT(uni != -1);
-
- const auto* family = jfamily->begin();
- const auto* style = jstyle->begin();
-
- // Locate (and cache) the font info. Unlike text nodes, glyphs reference the font by
- // (family, style) -- not by name :( For now this performs a linear search over *all*
- // fonts: generally there are few of them, and glyph definitions are font-clustered.
- // If problematic, we can refactor as a two-level hashmap.
- if (!current_font || !current_font->matches(family, style)) {
- current_font = nullptr;
- fFonts.foreach([&](const SkString& name, FontInfo* finfo) {
- if (finfo->matches(family, style)) {
- current_font = finfo;
- // TODO: would be nice to break early here...
- }
- });
- if (!current_font) {
- this->log(Logger::Level::kError, nullptr,
- "Font not found for codepoint (%d, %s, %s).", uni, family, style);
- continue;
+ const auto uni = SkUTF::NextUTF8(&ch_ptr, ch_ptr + ch_len);
+ SkASSERT(uni != -1);
+ if (!SkTFitsIn<SkGlyphID>(uni)) {
+ // Custom font keys are SkGlyphIDs. We could implement a remapping scheme if needed,
+ // but for now direct mapping seems to work well enough.
+ this->log(Logger::Level::kError, jchar, "Unsupported glyph ID.");
+ continue;
+ }
+ const auto glyph_id = SkTo<SkGlyphID>(uni);
+
+ const auto* family = jfamily->begin();
+ const auto* style = jstyle->begin();
+
+ // Locate (and cache) the font info. Unlike text nodes, glyphs reference the font by
+ // (family, style) -- not by name :( For now this performs a linear search over *all*
+ // fonts: generally there are few of them, and glyph definitions are font-clustered.
+ // If problematic, we can refactor as a two-level hashmap.
+ if (!current_font || !current_font->matches(family, style)) {
+ current_font = nullptr;
+ fFonts.foreach([&](const SkString& name, FontInfo* finfo) {
+ if (finfo->matches(family, style)) {
+ current_font = finfo;
+ // TODO: would be nice to break early here...
}
+ });
+ if (!current_font) {
+ this->log(Logger::Level::kError, nullptr,
+ "Font not found for codepoint (%d, %s, %s).", uni, family, style);
+ continue;
}
+ }
- // TODO: parse glyphs
+ SkPath path;
+ if (!parse_glyph_path((*jchar)["data"], this, &path)) {
+ continue;
}
+
+ const auto advance = ParseDefault((*jchar)["w"], 0.0f);
+
+ // Interestingly, glyph paths are defined in a percentage-based space,
+ // regardless of declared glyph size...
+ static constexpr float kPtScale = 0.01f;
+
+ // Normalize the path and advance for 1pt.
+ path.transform(SkMatrix::Scale(kPtScale, kPtScale));
+
+ current_font->fCustomBuilder.setGlyph(glyph_id, advance * kPtScale, path);
}
+
+ // Final pass to commit custom typefaces.
+ auto has_unresolved = false;
+ fFonts.foreach([&has_unresolved](const SkString&, FontInfo* finfo) {
+ if (finfo->fTypeface) {
+ return; // already resolved
+ }
+
+ finfo->fTypeface = finfo->fCustomBuilder.detach();
+
+ has_unresolved |= !finfo->fTypeface;
+ });
+
+ return !has_unresolved;
}
sk_sp<sksg::RenderNode> AnimationBuilder::attachTextLayer(const skjson::ObjectValue& jlayer,
@@ -265,6 +405,5 @@ const AnimationBuilder::FontInfo* AnimationBuilder::findFont(const SkString& fon
return fFonts.find(font_name);
}
-
} // namespace internal
} // namespace skottie
diff --git a/chromium/third_party/skia/modules/skottie/src/layers/shapelayer/Repeater.cpp b/chromium/third_party/skia/modules/skottie/src/layers/shapelayer/Repeater.cpp
index 3a80af05827..1d26b08f32e 100644
--- a/chromium/third_party/skia/modules/skottie/src/layers/shapelayer/Repeater.cpp
+++ b/chromium/third_party/skia/modules/skottie/src/layers/shapelayer/Repeater.cpp
@@ -49,7 +49,7 @@ private:
const auto t = fOffset + index;
// Position, scale & rotation are "scaled" by index/offset.
- SkMatrix m = SkMatrix::MakeTrans(-fAnchorPoint.x,
+ SkMatrix m = SkMatrix::Translate(-fAnchorPoint.x,
-fAnchorPoint.y);
m.postScale(std::pow(fScale.x * .01f, fOffset),
std::pow(fScale.y * .01f, fOffset));
diff --git a/chromium/third_party/skia/modules/skottie/src/text/TextValue.cpp b/chromium/third_party/skia/modules/skottie/src/text/TextValue.cpp
index f87158d159d..08741f1b62f 100644
--- a/chromium/third_party/skia/modules/skottie/src/text/TextValue.cpp
+++ b/chromium/third_party/skia/modules/skottie/src/text/TextValue.cpp
@@ -63,33 +63,34 @@ bool Parse(const skjson::Value& jv, const internal::AnimationBuilder& abuilder,
}
}
- // Skia resizing extension "sk_rs":
static constexpr Shaper::ResizePolicy gResizeMap[] = {
- Shaper::ResizePolicy::kNone, // 'sk_rs': 0
- Shaper::ResizePolicy::kScaleToFit, // 'sk_rs': 1
- Shaper::ResizePolicy::kDownscaleToFit, // 'sk_rs': 2
+ Shaper::ResizePolicy::kNone, // 'rs': 0
+ Shaper::ResizePolicy::kScaleToFit, // 'rs': 1
+ Shaper::ResizePolicy::kDownscaleToFit, // 'rs': 2
};
- v->fResize = gResizeMap[std::min<size_t>(ParseDefault<size_t>((*jtxt)["sk_rs"], 0),
- SK_ARRAY_COUNT(gResizeMap))];
+ // TODO: remove "sk_rs" support after migrating clients.
+ v->fResize = gResizeMap[std::min(std::max(ParseDefault<size_t>((*jtxt)[ "rs"], 0),
+ ParseDefault<size_t>((*jtxt)["sk_rs"], 0)),
+ SK_ARRAY_COUNT(gResizeMap))];
// In point mode, the text is baseline-aligned.
v->fVAlign = v->fBox.isEmpty() ? Shaper::VAlign::kTopBaseline
: Shaper::VAlign::kTop;
- // Skia vertical alignment extension "sk_vj":
static constexpr Shaper::VAlign gVAlignMap[] = {
- Shaper::VAlign::kVisualTop, // 'sk_vj': 0
- Shaper::VAlign::kVisualCenter, // 'sk_vj': 1
- Shaper::VAlign::kVisualBottom, // 'sk_vj': 2
+ Shaper::VAlign::kVisualTop, // 'vj': 0
+ Shaper::VAlign::kVisualCenter, // 'vj': 1
+ Shaper::VAlign::kVisualBottom, // 'vj': 2
};
- size_t sk_vj;
- if (skottie::Parse((*jtxt)["sk_vj"], &sk_vj)) {
- if (sk_vj < SK_ARRAY_COUNT(gVAlignMap)) {
- v->fVAlign = gVAlignMap[sk_vj];
+ size_t vj;
+ if (skottie::Parse((*jtxt)[ "vj"], &vj) ||
+ skottie::Parse((*jtxt)["sk_vj"], &vj)) { // TODO: remove after migrating clients.
+ if (vj < SK_ARRAY_COUNT(gVAlignMap)) {
+ v->fVAlign = gVAlignMap[vj];
} else {
// Legacy sk_vj values.
// TODO: remove after clients update.
- switch (sk_vj) {
+ switch (vj) {
case 3:
// 'sk_vj': 3 -> kVisualCenter/kScaleToFit
v->fVAlign = Shaper::VAlign::kVisualCenter;
@@ -102,7 +103,7 @@ bool Parse(const skjson::Value& jv, const internal::AnimationBuilder& abuilder,
break;
default:
abuilder.log(Logger::Level::kWarning, nullptr,
- "Ignoring unknown 'sk_vj' value: %zu", sk_vj);
+ "Ignoring unknown 'vj' value: %zu", vj);
break;
}
}
diff --git a/chromium/third_party/skia/modules/skparagraph/BUILD.gn b/chromium/third_party/skia/modules/skparagraph/BUILD.gn
index aa90c30dd39..48e09296c88 100644
--- a/chromium/third_party/skia/modules/skparagraph/BUILD.gn
+++ b/chromium/third_party/skia/modules/skparagraph/BUILD.gn
@@ -4,6 +4,7 @@ import("../../gn/skia.gni")
declare_args() {
skia_enable_skparagraph = true
+ paragraph_gms_enabled = true
paragraph_tests_enabled = true
paragraph_bench_enabled = false
}
@@ -22,6 +23,7 @@ if (skia_enable_skparagraph) {
public = skparagraph_public
if (skia_use_icu && skia_use_harfbuzz) {
sources = skparagraph_sources
+ configs += [ "../../third_party/icu/config:no_cxx" ]
} else {
sources = []
}
@@ -43,6 +45,7 @@ if (skia_enable_skparagraph) {
configs += [ "../../:skia_private" ]
if (skia_use_icu && skia_use_harfbuzz) {
sources = skparagraph_utils
+ configs += [ "../../third_party/icu/config:no_cxx" ]
} else {
sources = []
}
@@ -53,10 +56,25 @@ if (skia_enable_skparagraph) {
]
}
+ source_set("gm") {
+ if (skia_use_icu && skia_use_harfbuzz && paragraph_gms_enabled) {
+ testonly = true
+ sources = [ "gm/simple_gm.cpp" ]
+ deps = [
+ ":skparagraph",
+ "../..:gpu_tool_utils",
+ "../..:skia",
+ "../skshaper",
+ "//third_party/icu",
+ ]
+ }
+ }
+
source_set("tests") {
if (skia_use_icu && skia_use_harfbuzz && paragraph_tests_enabled) {
testonly = true
- sources = [ "//tests/SkParagraphTest.cpp" ]
+ sources = [ "tests/SkParagraphTest.cpp" ]
+ configs += [ "../../third_party/icu/config:no_cxx" ]
deps = [
":skparagraph",
"../..:gpu_tool_utils",
@@ -70,7 +88,8 @@ if (skia_enable_skparagraph) {
source_set("bench") {
if (skia_use_icu && skia_use_harfbuzz && paragraph_bench_enabled) {
testonly = true
- sources = [ "//bench/ParagraphBench.cpp" ]
+ sources = [ "bench/ParagraphBench.cpp" ]
+ configs += [ "../../third_party/icu/config:no_cxx" ]
deps = [
":skparagraph",
"../..:skia",
@@ -83,7 +102,8 @@ if (skia_enable_skparagraph) {
source_set("samples") {
if (skia_use_icu && skia_use_harfbuzz) {
testonly = true
- sources = [ "//samplecode/SampleParagraph.cpp" ]
+ sources = [ "samples/SampleParagraph.cpp" ]
+ configs += [ "../../third_party/icu/config:no_cxx" ]
deps = [
":skparagraph",
":utils",
diff --git a/chromium/third_party/skia/modules/skparagraph/bench/ParagraphBench.cpp b/chromium/third_party/skia/modules/skparagraph/bench/ParagraphBench.cpp
new file mode 100644
index 00000000000..35e4300f90f
--- /dev/null
+++ b/chromium/third_party/skia/modules/skparagraph/bench/ParagraphBench.cpp
@@ -0,0 +1,64 @@
+// Copyright 2019 Google LLC.
+// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
+
+#include "bench/Benchmark.h"
+
+#if !defined(SK_BUILD_FOR_ANDROID_FRAMEWORK) && !defined(SK_BUILD_FOR_GOOGLE3)
+
+#include "modules/skparagraph/include/FontCollection.h"
+#include "modules/skparagraph/include/Paragraph.h"
+#include "modules/skparagraph/src/ParagraphBuilderImpl.h"
+#include "modules/skparagraph/src/ParagraphImpl.h"
+#include "tools/Resources.h"
+
+#include <cfloat>
+#include "include/core/SkPictureRecorder.h"
+#include "modules/skparagraph/utils/TestFontCollection.h"
+
+using namespace skia::textlayout;
+namespace {
+struct ParagraphBench : public Benchmark {
+ ParagraphBench(SkScalar width, const char* r, const char* n)
+ : fResource(r), fName(n), fWidth(width) {}
+ sk_sp<SkData> fData;
+ const char* fResource;
+ const char* fName;
+ SkScalar fWidth;
+ const char* onGetName() override { return fName; }
+ bool isSuitableFor(Backend backend) override { return backend == kNonRendering_Backend; }
+ void onDelayedSetup() override { fData = GetResourceAsData(fResource); }
+ void onDraw(int loops, SkCanvas*) override {
+ if (!fData) {
+ return;
+ }
+
+ const char* text = (const char*)fData->data();
+
+ auto fontCollection = sk_make_sp<FontCollection>();
+ fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
+ ParagraphStyle paragraph_style;
+ paragraph_style.turnHintingOff();
+ ParagraphBuilderImpl builder(paragraph_style, fontCollection);
+ builder.addText(text);
+ auto paragraph = builder.Build();
+
+ SkPictureRecorder rec;
+ SkCanvas* canvas = rec.beginRecording({0,0, 2000,3000});
+ while (loops-- > 0) {
+ paragraph->layout(fWidth);
+ auto impl = static_cast<ParagraphImpl*>(paragraph.get());
+ paragraph->paint(canvas, 0, 0);
+ paragraph->markDirty();
+ impl->resetCache();
+ }
+ }
+};
+} // namespace
+
+#define PARAGRAPH_BENCH(X) DEF_BENCH(return new ParagraphBench(50000, "text/" #X ".txt", "paragraph_" #X);)
+//PARAGRAPH_BENCH(arabic)
+//PARAGRAPH_BENCH(emoji)
+PARAGRAPH_BENCH(english)
+#undef PARAGRAPH_BENCH
+
+#endif // !defined(SK_BUILD_FOR_ANDROID_FRAMEWORK) && !defined(SK_BUILD_FOR_GOOGLE3)
diff --git a/chromium/third_party/skia/modules/skparagraph/gm/simple_gm.cpp b/chromium/third_party/skia/modules/skparagraph/gm/simple_gm.cpp
new file mode 100644
index 00000000000..3b365bcc881
--- /dev/null
+++ b/chromium/third_party/skia/modules/skparagraph/gm/simple_gm.cpp
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2020 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "gm/gm.h"
+#include "include/core/SkCanvas.h"
+#include "include/core/SkColor.h"
+#include "include/core/SkFont.h"
+#include "include/core/SkFontTypes.h"
+#include "include/core/SkPaint.h"
+#include "include/core/SkPoint.h"
+#include "include/core/SkRect.h"
+#include "include/core/SkScalar.h"
+#include "include/core/SkShader.h"
+#include "include/core/SkSize.h"
+#include "include/core/SkString.h"
+#include "include/core/SkTypeface.h"
+#include "tools/ToolUtils.h"
+
+#include "modules/skparagraph/include/Paragraph.h"
+#include "modules/skparagraph/include/ParagraphBuilder.h"
+
+static const char* gSpeach = "Five score years ago, a great American, in whose symbolic shadow we stand today, signed the Emancipation Proclamation. This momentous decree came as a great beacon light of hope to millions of Negro slaves who had been seared in the flames of withering injustice. It came as a joyous daybreak to end the long night of their captivity.";
+
+namespace {
+enum ParaFlags {
+ kTimeLayout = 1 << 0,
+ kUseUnderline = 1 << 1,
+};
+}
+
+class ParagraphGM : public skiagm::GM {
+ std::unique_ptr<skia::textlayout::Paragraph> fPara;
+ const unsigned fFlags;
+
+public:
+ ParagraphGM(unsigned flags) : fFlags(flags) {}
+
+ void buildParagraph() {
+ skia::textlayout::TextStyle style;
+ style.setForegroundColor(SkPaint());
+ style.setFontFamilies({SkString("sans-serif")});
+ style.setFontSize(30);
+
+ if (fFlags & kUseUnderline) {
+ style.setDecoration(skia::textlayout::TextDecoration::kUnderline);
+ style.setDecorationMode(skia::textlayout::TextDecorationMode::kThrough);
+ style.setDecorationColor(SK_ColorBLACK);
+ style.setDecorationThicknessMultiplier(2);
+ }
+
+ skia::textlayout::ParagraphStyle paraStyle;
+ paraStyle.setTextStyle(style);
+
+ auto collection = sk_make_sp<skia::textlayout::FontCollection>();
+ collection->setDefaultFontManager(SkFontMgr::RefDefault());
+ auto builder = skia::textlayout::ParagraphBuilder::make(paraStyle, collection);
+
+ builder->addText(gSpeach, strlen(gSpeach));
+
+ fPara = builder->Build();
+ fPara->layout(400);
+ }
+
+protected:
+ void onOnceBeforeDraw() override {
+ this->buildParagraph();
+ }
+
+ SkString onShortName() override {
+ SkString name;
+ name.printf("paragraph%s_%s",
+ fFlags & kTimeLayout ? "_layout" : "",
+ fFlags & kUseUnderline ? "_underline" : "");
+ return name;
+ }
+
+ SkISize onISize() override { return SkISize::Make(412, 420); }
+
+ DrawResult onDraw(SkCanvas* canvas, SkString* errorMsg) override {
+ const int loop = (this->getMode() == kGM_Mode) ? 1 : 50;
+
+ int parity = 0;
+ for (int i = 0; i < loop; ++i) {
+ SkAutoCanvasRestore acr(canvas, true);
+
+ if (fFlags & kTimeLayout) {
+ fPara->layout(400 + parity);
+ parity = (parity + 1) & 1;
+ }
+ fPara->paint(canvas, 10, 10);
+ }
+ // clean up if we've been looping
+ if (loop > 1) {
+ canvas->clear(SK_ColorWHITE);
+ fPara->layout(400);
+ fPara->paint(canvas, 10, 10);
+ }
+
+ if ((this->getMode() == kGM_Mode) && (fFlags & kTimeLayout)) {
+ return DrawResult::kSkip;
+ }
+ return DrawResult::kOk;
+ }
+
+ bool runAsBench() const override { return true; }
+
+ bool onAnimate(double /*nanos*/) override {
+ return true;
+ }
+
+private:
+ typedef skiagm::GM INHERITED;
+};
+DEF_GM(return new ParagraphGM(0);)
+DEF_GM(return new ParagraphGM(kTimeLayout);)
+DEF_GM(return new ParagraphGM(kUseUnderline);)
diff --git a/chromium/third_party/skia/modules/skparagraph/include/DartTypes.h b/chromium/third_party/skia/modules/skparagraph/include/DartTypes.h
index 162fe104453..415d5dfa722 100644
--- a/chromium/third_party/skia/modules/skparagraph/include/DartTypes.h
+++ b/chromium/third_party/skia/modules/skparagraph/include/DartTypes.h
@@ -4,6 +4,10 @@
#define DartTypes_DEFINED
#include "include/core/SkRect.h"
+#include "include/core/SkTypes.h"
+
+#include <iterator>
+#include <limits>
namespace skia {
namespace textlayout {
diff --git a/chromium/third_party/skia/modules/skparagraph/include/Paragraph.h b/chromium/third_party/skia/modules/skparagraph/include/Paragraph.h
index d262aae9a6b..4e999318302 100644
--- a/chromium/third_party/skia/modules/skparagraph/include/Paragraph.h
+++ b/chromium/third_party/skia/modules/skparagraph/include/Paragraph.h
@@ -15,8 +15,7 @@ namespace textlayout {
class Paragraph {
public:
- Paragraph(ParagraphStyle style, sk_sp<FontCollection> fonts)
- : fFontCollection(std::move(fonts)), fParagraphStyle(std::move(style)) {}
+ Paragraph(ParagraphStyle style, sk_sp<FontCollection> fonts);
virtual ~Paragraph() = default;
diff --git a/chromium/third_party/skia/modules/skparagraph/include/ParagraphCache.h b/chromium/third_party/skia/modules/skparagraph/include/ParagraphCache.h
index bd89c274a93..1d477a1b130 100644
--- a/chromium/third_party/skia/modules/skparagraph/include/ParagraphCache.h
+++ b/chromium/third_party/skia/modules/skparagraph/include/ParagraphCache.h
@@ -13,12 +13,12 @@ namespace textlayout {
enum InternalState {
kUnknown = 0,
- kShaped = 1,
- kClusterized = 2,
- kMarked = 3,
- kLineBroken = 4,
- kFormatted = 5,
- kDrawn = 6
+ kShaped = 2,
+ kClusterized = 3,
+ kMarked = 4,
+ kLineBroken = 5,
+ kFormatted = 6,
+ kDrawn = 7
};
class ParagraphImpl;
diff --git a/chromium/third_party/skia/modules/skparagraph/include/ParagraphStyle.h b/chromium/third_party/skia/modules/skparagraph/include/ParagraphStyle.h
index 2fcb8591ae8..682bae07d0c 100644
--- a/chromium/third_party/skia/modules/skparagraph/include/ParagraphStyle.h
+++ b/chromium/third_party/skia/modules/skparagraph/include/ParagraphStyle.h
@@ -3,9 +3,17 @@
#define ParagraphStyle_DEFINED
#include "include/core/SkFontStyle.h"
+#include "include/core/SkScalar.h"
+#include "include/core/SkString.h"
#include "modules/skparagraph/include/DartTypes.h"
#include "modules/skparagraph/include/TextStyle.h"
-#include <string> // std::u16string
+
+#include <stddef.h>
+#include <algorithm>
+#include <limits>
+#include <string>
+#include <utility>
+#include <vector>
namespace skia {
namespace textlayout {
diff --git a/chromium/third_party/skia/modules/skparagraph/include/TypefaceFontProvider.h b/chromium/third_party/skia/modules/skparagraph/include/TypefaceFontProvider.h
index 2e83912c6da..727659d85d8 100644
--- a/chromium/third_party/skia/modules/skparagraph/include/TypefaceFontProvider.h
+++ b/chromium/third_party/skia/modules/skparagraph/include/TypefaceFontProvider.h
@@ -67,9 +67,7 @@ public:
const SkFontArguments&) const override {
return nullptr;
}
- sk_sp<SkTypeface> onMakeFromFontData(std::unique_ptr<SkFontData>) const override {
- return nullptr;
- }
+ sk_sp<SkTypeface> onMakeFromFontData(std::unique_ptr<SkFontData>) const override;
sk_sp<SkTypeface> onMakeFromFile(const char path[], int ttcIndex) const override {
return nullptr;
}
diff --git a/chromium/third_party/skia/modules/skparagraph/samples/SampleParagraph.cpp b/chromium/third_party/skia/modules/skparagraph/samples/SampleParagraph.cpp
new file mode 100644
index 00000000000..28f3207743f
--- /dev/null
+++ b/chromium/third_party/skia/modules/skparagraph/samples/SampleParagraph.cpp
@@ -0,0 +1,2946 @@
+// Copyright 2019 Google LLC.
+#include "include/core/SkCanvas.h"
+#include "include/core/SkColorFilter.h"
+#include "include/core/SkColorPriv.h"
+#include "include/core/SkFontMgr.h"
+#include "include/core/SkGraphics.h"
+#include "include/core/SkPath.h"
+#include "include/core/SkRegion.h"
+#include "include/core/SkShader.h"
+#include "include/core/SkStream.h"
+#include "include/core/SkTextBlob.h"
+#include "include/core/SkTime.h"
+#include "include/core/SkTypeface.h"
+#include "include/effects/SkGradientShader.h"
+#include "include/utils/SkRandom.h"
+#include "modules/skparagraph/include/Paragraph.h"
+#include "modules/skparagraph/include/TypefaceFontProvider.h"
+#include "modules/skparagraph/src/ParagraphBuilderImpl.h"
+#include "modules/skparagraph/src/ParagraphImpl.h"
+#include "modules/skparagraph/src/ParagraphUtil.h"
+#include "modules/skparagraph/src/TextLine.h"
+#include "modules/skparagraph/utils/TestFontCollection.h"
+#include "samplecode/Sample.h"
+#include "src/core/SkOSFile.h"
+#include "src/shaders/SkColorShader.h"
+#include "src/utils/SkOSPath.h"
+#include "src/utils/SkUTF.h"
+#include "tools/Resources.h"
+#include "tools/flags/CommandLineFlags.h"
+
+
+static DEFINE_bool(verboseParagraph, false, "paragraph samples very verbose.");
+
+using namespace skia::textlayout;
+namespace {
+
+class ParagraphView_Base : public Sample {
+protected:
+ sk_sp<TestFontCollection> getFontCollection() {
+ // If we reset font collection we need to reset paragraph cache
+ static sk_sp<TestFontCollection> fFC = nullptr;
+ if (fFC == nullptr) {
+ fFC = sk_make_sp<TestFontCollection>(GetResourcePath("fonts").c_str(), false, true);
+ }
+ return fFC;
+ }
+
+ bool isVerbose() {
+ return FLAGS_verboseParagraph;
+ }
+};
+
+sk_sp<SkShader> setgrad(const SkRect& r, SkColor c0, SkColor c1) {
+ SkColor colors[] = {c0, c1};
+ SkPoint pts[] = {{r.fLeft, r.fTop}, {r.fRight, r.fTop}};
+ return SkGradientShader::MakeLinear(pts, colors, nullptr, 2, SkTileMode::kClamp);
+}
+/*
+void writeHtml(const char* name, Paragraph* paragraph) {
+ SkString tmpDir = skiatest::GetTmpDir();
+ if (!tmpDir.isEmpty()) {
+ SkString path = SkOSPath::Join(tmpDir.c_str(), name);
+ SkFILEWStream file(path.c_str());
+ file.write(nullptr, 0);
+ }
+}
+*/
+
+class ParagraphView1 : public ParagraphView_Base {
+protected:
+ SkString name() override { return SkString("Paragraph1"); }
+
+ void drawTest(SkCanvas* canvas, SkScalar w, SkScalar h, SkColor fg, SkColor bg) {
+ const std::vector<
+ std::tuple<std::string, bool, bool, int, SkColor, SkColor, bool, TextDecorationStyle>>
+ gParagraph = {{"monospace", true, false, 14, SK_ColorWHITE, SK_ColorRED, true,
+ TextDecorationStyle::kDashed},
+ {"Assyrian", false, false, 20, SK_ColorWHITE, SK_ColorBLUE, false,
+ TextDecorationStyle::kDotted},
+ {"serif", true, true, 10, SK_ColorWHITE, SK_ColorRED, true,
+ TextDecorationStyle::kDouble},
+ {"Arial", false, true, 16, SK_ColorGRAY, SK_ColorGREEN, true,
+ TextDecorationStyle::kSolid},
+ {"sans-serif", false, false, 8, SK_ColorWHITE, SK_ColorRED, false,
+ TextDecorationStyle::kWavy}};
+ SkAutoCanvasRestore acr(canvas, true);
+
+ canvas->clipRect(SkRect::MakeWH(w, h));
+ canvas->drawColor(SK_ColorWHITE);
+
+ SkScalar margin = 20;
+
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setColor(fg);
+
+ SkPaint blue;
+ blue.setColor(SK_ColorBLUE);
+
+ TextStyle defaultStyle;
+ defaultStyle.setBackgroundColor(blue);
+ defaultStyle.setForegroundColor(paint);
+ ParagraphStyle paraStyle;
+
+ auto fontCollection = sk_make_sp<FontCollection>();
+ fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
+ for (auto i = 1; i < 5; ++i) {
+ defaultStyle.setFontSize(24 * i);
+ paraStyle.setTextStyle(defaultStyle);
+ ParagraphBuilderImpl builder(paraStyle, fontCollection);
+ std::string name = "Paragraph: " + std::to_string(24 * i);
+ builder.addText(name.c_str(), name.length());
+ for (auto para : gParagraph) {
+ TextStyle style;
+ style.setFontFamilies({SkString(std::get<0>(para).c_str())});
+ SkFontStyle fontStyle(std::get<1>(para) ? SkFontStyle::Weight::kBold_Weight
+ : SkFontStyle::Weight::kNormal_Weight,
+ SkFontStyle::Width::kNormal_Width,
+ std::get<2>(para) ? SkFontStyle::Slant::kItalic_Slant
+ : SkFontStyle::Slant::kUpright_Slant);
+ style.setFontStyle(fontStyle);
+ style.setFontSize(std::get<3>(para) * i);
+ SkPaint background;
+ background.setColor(std::get<4>(para));
+ style.setBackgroundColor(background);
+ SkPaint foreground;
+ foreground.setColor(std::get<5>(para));
+ foreground.setAntiAlias(true);
+ style.setForegroundColor(foreground);
+ if (std::get<6>(para)) {
+ style.addShadow(TextShadow(SK_ColorBLACK, SkPoint::Make(5, 5), 2));
+ }
+
+ auto decoration = (i % 4);
+ if (decoration == 3) {
+ decoration = 4;
+ }
+
+ bool test = (TextDecoration)decoration != TextDecoration::kNoDecoration;
+ std::string deco = std::to_string((int)decoration);
+ if (test) {
+ style.setDecoration((TextDecoration)decoration);
+ style.setDecorationStyle(std::get<7>(para));
+ style.setDecorationColor(std::get<5>(para));
+ }
+ builder.pushStyle(style);
+ std::string name = " " + std::get<0>(para) + " " +
+ (std::get<1>(para) ? ", bold" : "") +
+ (std::get<2>(para) ? ", italic" : "") + " " +
+ std::to_string(std::get<3>(para) * i) +
+ (std::get<4>(para) != bg ? ", background" : "") +
+ (std::get<5>(para) != fg ? ", foreground" : "") +
+ (std::get<6>(para) ? ", shadow" : "") +
+ (test ? ", decorations " + deco : "") + ";";
+ builder.addText(name.c_str(), name.length());
+ builder.pop();
+ }
+
+ auto paragraph = builder.Build();
+ paragraph->layout(w - margin * 2);
+ paragraph->paint(canvas, margin, margin);
+
+ canvas->translate(0, paragraph->getHeight());
+ }
+ }
+
+ void onDrawContent(SkCanvas* canvas) override {
+ drawTest(canvas, this->width(), this->height(), SK_ColorRED, SK_ColorWHITE);
+ }
+
+private:
+
+ typedef Sample INHERITED;
+};
+
+class ParagraphView2 : public ParagraphView_Base {
+protected:
+ SkString name() override { return SkString("Paragraph2"); }
+
+ void drawCode(SkCanvas* canvas, SkScalar w, SkScalar h) {
+ SkPaint comment;
+ comment.setColor(SK_ColorGRAY);
+ SkPaint constant;
+ constant.setColor(SK_ColorMAGENTA);
+ SkPaint null;
+ null.setColor(SK_ColorMAGENTA);
+ SkPaint literal;
+ literal.setColor(SK_ColorGREEN);
+ SkPaint code;
+ code.setColor(SK_ColorDKGRAY);
+ SkPaint number;
+ number.setColor(SK_ColorBLUE);
+ SkPaint name;
+ name.setColor(SK_ColorRED);
+
+ SkPaint white;
+ white.setColor(SK_ColorWHITE);
+
+ TextStyle defaultStyle;
+ defaultStyle.setBackgroundColor(white);
+ defaultStyle.setForegroundColor(code);
+ defaultStyle.setFontFamilies({SkString("monospace")});
+ defaultStyle.setFontSize(30);
+ ParagraphStyle paraStyle;
+ paraStyle.setTextStyle(defaultStyle);
+
+ auto fontCollection = sk_make_sp<FontCollection>();
+ fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
+ ParagraphBuilderImpl builder(paraStyle, fontCollection);
+
+ const char* text1 = "RaisedButton";
+ const char* text2 = "(\n";
+ const char* text3 = " child: ";
+ const char* text4 = "const";
+ const char* text5 = "Text";
+ const char* text6 = "'BUTTON TITLE'";
+ const char* text7 = "),\n";
+
+ builder.pushStyle(style(name));
+ builder.addText(text1, strlen(text1));
+ builder.pop();
+ builder.addText(text2, strlen(text2));
+ builder.addText(text3, strlen(text3));
+ builder.pushStyle(style(constant));
+ builder.addText(text4, strlen(text4));
+ builder.pop();
+ builder.addText(" ", 1);
+ builder.pushStyle(style(name));
+ builder.addText(text5, strlen(text5));
+ builder.pop();
+ builder.addText("(", 1);
+ builder.pushStyle(style(literal));
+ builder.addText(text6, strlen(text6));
+ builder.pop();
+ builder.addText(text7, strlen(text7));
+
+ auto paragraph = builder.Build();
+ paragraph->layout(w - 20);
+
+ paragraph->paint(canvas, 20, 20);
+ }
+
+ TextStyle style(SkPaint paint) {
+ TextStyle style;
+ paint.setAntiAlias(true);
+ style.setForegroundColor(paint);
+ style.setFontFamilies({SkString("monospace")});
+ style.setFontSize(30);
+
+ return style;
+ }
+
+ void drawText(SkCanvas* canvas, SkScalar w, SkScalar h, std::vector<const char*>& text,
+ SkColor fg = SK_ColorDKGRAY, SkColor bg = SK_ColorWHITE,
+ const char* ff = "sans-serif", SkScalar fs = 24,
+ size_t lineLimit = 30,
+ const std::u16string& ellipsis = u"\u2026") {
+ SkAutoCanvasRestore acr(canvas, true);
+
+ canvas->clipRect(SkRect::MakeWH(w, h));
+ canvas->drawColor(bg);
+
+ SkScalar margin = 20;
+
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setColor(fg);
+
+ SkPaint blue;
+ blue.setColor(SK_ColorBLUE);
+
+ SkPaint background;
+ background.setColor(bg);
+
+ TextStyle style;
+ style.setBackgroundColor(blue);
+ style.setForegroundColor(paint);
+ style.setFontFamilies({SkString(ff)});
+ style.setFontStyle(SkFontStyle(SkFontStyle::kMedium_Weight,
+ SkFontStyle::kNormal_Width,
+ SkFontStyle::kUpright_Slant));
+ style.setFontSize(fs);
+ ParagraphStyle paraStyle;
+ paraStyle.setTextStyle(style);
+ paraStyle.setMaxLines(lineLimit);
+
+ paraStyle.setEllipsis(ellipsis);
+ TextStyle defaultStyle;
+ defaultStyle.setFontSize(20);
+ paraStyle.setTextStyle(defaultStyle);
+ ParagraphBuilderImpl builder(paraStyle, getFontCollection());
+
+ SkPaint foreground;
+ foreground.setColor(fg);
+ style.setForegroundColor(foreground);
+ style.setBackgroundColor(background);
+
+ for (auto& part : text) {
+ builder.pushStyle(style);
+ builder.addText(part, strlen(part));
+ builder.pop();
+ }
+
+ auto paragraph = builder.Build();
+ paragraph->layout(w - margin * 2);
+ paragraph->paint(canvas, margin, margin);
+
+ canvas->translate(0, paragraph->getHeight() + margin);
+ }
+
+ void drawLine(SkCanvas* canvas, SkScalar w, SkScalar h, const std::string& text,
+ TextAlign align) {
+ SkAutoCanvasRestore acr(canvas, true);
+
+ canvas->clipRect(SkRect::MakeWH(w, h));
+ canvas->drawColor(SK_ColorWHITE);
+
+ SkScalar margin = 20;
+
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setColor(SK_ColorBLUE);
+
+ SkPaint gray;
+ gray.setColor(SK_ColorLTGRAY);
+
+ TextStyle style;
+ style.setBackgroundColor(gray);
+ style.setForegroundColor(paint);
+ style.setFontFamilies({SkString("Arial")});
+ style.setFontSize(30);
+ ParagraphStyle paraStyle;
+ paraStyle.setTextStyle(style);
+ paraStyle.setTextAlign(align);
+
+ auto fontCollection = sk_make_sp<FontCollection>();
+ fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
+ ParagraphBuilderImpl builder(paraStyle, fontCollection);
+ builder.addText(text.c_str(), text.length());
+
+ auto paragraph = builder.Build();
+ paragraph->layout(w - margin * 2);
+ paragraph->layout(w - margin);
+ paragraph->paint(canvas, margin, margin);
+
+ canvas->translate(0, paragraph->getHeight() + margin);
+ }
+
+ void onDrawContent(SkCanvas* canvas) override {
+ std::vector<const char*> cupertino = {
+ "google_logogoogle_gsuper_g_logo 1 "
+ "google_logogoogle_gsuper_g_logo 12 "
+ "google_logogoogle_gsuper_g_logo 123 "
+ "google_logogoogle_gsuper_g_logo 1234 "
+ "google_logogoogle_gsuper_g_logo 12345 "
+ "google_logogoogle_gsuper_g_logo 123456 "
+ "google_logogoogle_gsuper_g_logo 1234567 "
+ "google_logogoogle_gsuper_g_logo 12345678 "
+ "google_logogoogle_gsuper_g_logo 123456789 "
+ "google_logogoogle_gsuper_g_logo 1234567890 "
+ "google_logogoogle_gsuper_g_logo 123456789 "
+ "google_logogoogle_gsuper_g_logo 12345678 "
+ "google_logogoogle_gsuper_g_logo 1234567 "
+ "google_logogoogle_gsuper_g_logo 123456 "
+ "google_logogoogle_gsuper_g_logo 12345 "
+ "google_logogoogle_gsuper_g_logo 1234 "
+ "google_logogoogle_gsuper_g_logo 123 "
+ "google_logogoogle_gsuper_g_logo 12 "
+ "google_logogoogle_gsuper_g_logo 1 "
+ "google_logogoogle_gsuper_g_logo "
+ "google_logogoogle_gsuper_g_logo "
+ "google_logogoogle_gsuper_g_logo "
+ "google_logogoogle_gsuper_g_logo "
+ "google_logogoogle_gsuper_g_logo "
+ "google_logogoogle_gsuper_g_logo"};
+ std::vector<const char*> text = {
+ "My neighbor came over to say,\n"
+ "Although not in a neighborly way,\n\n"
+ "That he'd knock me around,\n\n\n"
+ "If I didn't stop the sound,\n\n\n\n"
+ "Of the classical music I play."};
+
+ std::vector<const char*> long_word = {
+ "A_very_very_very_very_very_very_very_very_very_very_very_very_very_very_very_very_"
+ "very_very_very_very_very_very_very_very_very_very_very_very_very_very_very_very_"
+ "very_very_very_very_very_very_very_very_very_very_very_very_very_very_very_very_"
+ "very_very_very_very_very_very_very_long_text"};
+
+ std::vector<const char*> very_long = {
+ "A very very very very very very very very very very very very very very very very "
+ "very very very very very very very very very very very very very very very very "
+ "very very very very very very very very very very very very very very very very "
+ "very very very very very very very long text"};
+
+ std::vector<const char*> very_word = {
+ "A very_very_very_very_very_very_very_very_very_very "
+ "very_very_very_very_very_very_very_very_very_very very very very very very very "
+ "very very very very very very very very very very very very very very very very "
+ "very very very very very very very very very very very very very long text"};
+
+ SkScalar width = this->width() / 5;
+ SkScalar height = this->height();
+ drawText(canvas, width, height, long_word, SK_ColorBLACK, SK_ColorWHITE, "Google Sans", 30);
+ canvas->translate(width, 0);
+ drawText(canvas, width, height, very_long, SK_ColorBLACK, SK_ColorWHITE, "Google Sans", 30);
+ canvas->translate(width, 0);
+ drawText(canvas, width, height, very_word, SK_ColorBLACK, SK_ColorWHITE, "Google Sans", 30);
+ canvas->translate(width, 0);
+ drawText(canvas, width, height / 2, text, SK_ColorBLACK, SK_ColorWHITE, "Roboto", 20, 100,
+ u"\u2026");
+ canvas->translate(0, height / 2);
+ drawCode(canvas, width, height / 2);
+ canvas->translate(width, -height / 2);
+
+ drawText(canvas, width, height, cupertino, SK_ColorBLACK, SK_ColorWHITE, "Google Sans", 30);
+ }
+
+private:
+ typedef Sample INHERITED;
+};
+
+class ParagraphView3 : public ParagraphView_Base {
+protected:
+ SkString name() override { return SkString("Paragraph3"); }
+
+ void drawLine(SkCanvas* canvas, SkScalar w, SkScalar h, const std::string& text,
+ TextAlign align, size_t lineLimit = std::numeric_limits<size_t>::max(),
+ bool RTL = false, SkColor background = SK_ColorGRAY,
+ const std::u16string& ellipsis = u"\u2026") {
+ SkAutoCanvasRestore acr(canvas, true);
+
+ canvas->clipRect(SkRect::MakeWH(w, h));
+ canvas->drawColor(SK_ColorWHITE);
+
+ SkScalar margin = 20;
+
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setColor(SK_ColorBLACK);
+
+ SkPaint gray;
+ gray.setColor(background);
+
+ SkPaint yellow;
+ yellow.setColor(SK_ColorYELLOW);
+
+ TextStyle style;
+ style.setBackgroundColor(gray);
+ style.setForegroundColor(paint);
+ style.setFontFamilies({SkString("sans-serif")});
+ style.setFontSize(30);
+ ParagraphStyle paraStyle;
+ paraStyle.setTextStyle(style);
+ paraStyle.setTextAlign(align);
+ paraStyle.setMaxLines(lineLimit);
+ paraStyle.setEllipsis(ellipsis);
+ // paraStyle.setTextDirection(RTL ? SkTextDirection::rtl : SkTextDirection::ltr);
+
+ auto fontCollection = sk_make_sp<FontCollection>();
+ fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
+ ParagraphBuilderImpl builder(paraStyle, fontCollection);
+ if (RTL) {
+ builder.addText(mirror(text));
+ } else {
+ builder.addText(normal(text));
+ }
+
+ canvas->drawRect(SkRect::MakeXYWH(margin, margin, w - margin * 2, h - margin * 2), yellow);
+ auto paragraph = builder.Build();
+ paragraph->layout(w - margin * 2);
+ paragraph->paint(canvas, margin, margin);
+ }
+
+ std::u16string mirror(const std::string& text) {
+ std::u16string result;
+ result += u"\u202E";
+ // for (auto i = text.size(); i > 0; --i) {
+ // result += text[i - 1];
+ //}
+
+ for (auto i = text.size(); i > 0; --i) {
+ auto ch = text[i - 1];
+ if (ch == ',') {
+ result += u"!";
+ } else if (ch == '.') {
+ result += u"!";
+ } else {
+ result += ch;
+ }
+ }
+
+ result += u"\u202C";
+ return result;
+ }
+
+ std::u16string normal(const std::string& text) {
+ std::u16string result;
+ result += u"\u202D";
+ for (auto ch : text) {
+ result += ch;
+ }
+ result += u"\u202C";
+ return result;
+ }
+
+ void onDrawContent(SkCanvas* canvas) override {
+ const std::string options = // { "open-source open-source open-source open-source" };
+ {"Flutter is an open-source project to help developers "
+ "build high-performance, high-fidelity, mobile apps for "
+ "iOS and Android "
+ "from a single codebase. This design lab is a playground "
+ "and showcase of Flutter's many widgets, behaviors, "
+ "animations, layouts, and more."};
+
+ canvas->drawColor(SK_ColorDKGRAY);
+ SkScalar width = this->width() / 4;
+ SkScalar height = this->height() / 2;
+
+ const std::string line =
+ "World domination is such an ugly phrase - I prefer to call it world optimisation";
+
+ drawLine(canvas, width, height, line, TextAlign::kLeft, 1, false, SK_ColorLTGRAY);
+ canvas->translate(width, 0);
+ drawLine(canvas, width, height, line, TextAlign::kRight, 2, false, SK_ColorLTGRAY);
+ canvas->translate(width, 0);
+ drawLine(canvas, width, height, line, TextAlign::kCenter, 3, false, SK_ColorLTGRAY);
+ canvas->translate(width, 0);
+ drawLine(canvas, width, height, line, TextAlign::kJustify, 4, false, SK_ColorLTGRAY);
+ canvas->translate(-width * 3, height);
+
+ drawLine(canvas, width, height, line, TextAlign::kLeft, 1, true, SK_ColorLTGRAY);
+ canvas->translate(width, 0);
+ drawLine(canvas, width, height, line, TextAlign::kRight, 2, true, SK_ColorLTGRAY);
+ canvas->translate(width, 0);
+ drawLine(canvas, width, height, line, TextAlign::kCenter, 3, true, SK_ColorLTGRAY);
+ canvas->translate(width, 0);
+ drawLine(canvas, width, height, line, TextAlign::kJustify, 4, true, SK_ColorLTGRAY);
+ canvas->translate(width, 0);
+ }
+
+private:
+ typedef Sample INHERITED;
+};
+
+class ParagraphView4 : public ParagraphView_Base {
+protected:
+ SkString name() override { return SkString("Paragraph4"); }
+
+ void drawFlutter(SkCanvas* canvas, SkScalar w, SkScalar h,
+ const char* ff = "Google Sans", SkScalar fs = 30,
+ size_t lineLimit = std::numeric_limits<size_t>::max(),
+ const std::u16string& ellipsis = u"\u2026") {
+ SkAutoCanvasRestore acr(canvas, true);
+
+ canvas->clipRect(SkRect::MakeWH(w, h));
+
+ SkScalar margin = 20;
+
+ SkPaint black;
+ black.setAntiAlias(true);
+ black.setColor(SK_ColorBLACK);
+
+ SkPaint blue;
+ blue.setAntiAlias(true);
+ blue.setColor(SK_ColorBLUE);
+
+ SkPaint red;
+ red.setAntiAlias(true);
+ red.setColor(SK_ColorRED);
+
+ SkPaint green;
+ green.setAntiAlias(true);
+ green.setColor(SK_ColorGREEN);
+
+ SkPaint gray;
+ gray.setColor(SK_ColorLTGRAY);
+
+ SkPaint yellow;
+ yellow.setColor(SK_ColorYELLOW);
+
+ SkPaint magenta;
+ magenta.setAntiAlias(true);
+ magenta.setColor(SK_ColorMAGENTA);
+
+ TextStyle style;
+ style.setFontFamilies({SkString(ff)});
+ style.setFontSize(fs);
+
+ TextStyle style0;
+ style0.setForegroundColor(black);
+ style0.setBackgroundColor(gray);
+ style0.setFontFamilies({SkString(ff)});
+ style0.setFontSize(fs);
+ style0.setDecoration(TextDecoration::kUnderline);
+ style0.setDecorationStyle(TextDecorationStyle::kDouble);
+ style0.setDecorationColor(SK_ColorBLACK);
+
+ TextStyle style1;
+ style1.setForegroundColor(blue);
+ style1.setBackgroundColor(yellow);
+ style1.setFontFamilies({SkString(ff)});
+ style1.setFontSize(fs);
+ style1.setDecoration(TextDecoration::kOverline);
+ style1.setDecorationStyle(TextDecorationStyle::kWavy);
+ style1.setDecorationColor(SK_ColorBLACK);
+
+ TextStyle style2;
+ style2.setForegroundColor(red);
+ style2.setFontFamilies({SkString(ff)});
+ style2.setFontSize(fs);
+
+ TextStyle style3;
+ style3.setForegroundColor(green);
+ style3.setFontFamilies({SkString(ff)});
+ style3.setFontSize(fs);
+
+ TextStyle style4;
+ style4.setForegroundColor(magenta);
+ style4.setFontFamilies({SkString(ff)});
+ style4.setFontSize(fs);
+
+ ParagraphStyle paraStyle;
+ paraStyle.setTextStyle(style);
+ paraStyle.setMaxLines(lineLimit);
+
+ paraStyle.setEllipsis(ellipsis);
+
+ const char* logo1 = "google_";
+ const char* logo2 = "logo";
+ const char* logo3 = "go";
+ const char* logo4 = "ogle_logo";
+ const char* logo5 = "google_lo";
+ const char* logo6 = "go";
+ {
+ ParagraphBuilderImpl builder(paraStyle, getFontCollection());
+
+ builder.pushStyle(style0);
+ builder.addText(logo1, strlen(logo1));
+ builder.pop();
+ builder.pushStyle(style1);
+ builder.addText(logo2, strlen(logo2));
+ builder.pop();
+
+ builder.addText(" ", 1);
+
+ builder.pushStyle(style0);
+ builder.addText(logo3, strlen(logo3));
+ builder.pop();
+ builder.pushStyle(style1);
+ builder.addText(logo4, strlen(logo4));
+ builder.pop();
+
+ builder.addText(" ", 1);
+
+ builder.pushStyle(style0);
+ builder.addText(logo5, strlen(logo5));
+ builder.pop();
+ builder.pushStyle(style1);
+ builder.addText(logo6, strlen(logo6));
+ builder.pop();
+
+ auto paragraph = builder.Build();
+ paragraph->layout(w - margin * 2);
+ paragraph->paint(canvas, margin, margin);
+ canvas->translate(0, h + margin);
+ }
+ }
+
+ void onDrawContent(SkCanvas* canvas) override {
+ canvas->drawColor(SK_ColorWHITE);
+ SkScalar width = this->width();
+ SkScalar height = this->height();
+
+ drawFlutter(canvas, width, height / 2);
+ }
+
+private:
+ typedef Sample INHERITED;
+};
+
+class ParagraphView5 : public ParagraphView_Base {
+protected:
+ SkString name() override { return SkString("Paragraph5"); }
+
+ void bidi(SkCanvas* canvas, SkScalar w, SkScalar h, const std::u16string& text,
+ const std::u16string& expected, size_t lineLimit = std::numeric_limits<size_t>::max(),
+ const char* ff = "Roboto", SkScalar fs = 30,
+ const std::u16string& ellipsis = u"\u2026") {
+ SkAutoCanvasRestore acr(canvas, true);
+
+ canvas->clipRect(SkRect::MakeWH(w, h));
+
+ SkScalar margin = 20;
+
+ SkPaint black;
+ black.setColor(SK_ColorBLACK);
+ SkPaint gray;
+ gray.setColor(SK_ColorLTGRAY);
+
+ TextStyle style;
+ style.setForegroundColor(black);
+ style.setFontFamilies({SkString(ff)});
+ style.setFontSize(fs);
+
+ TextStyle style0;
+ style0.setForegroundColor(black);
+ style0.setFontFamilies({SkString(ff)});
+ style0.setFontSize(fs);
+ style0.setFontStyle(SkFontStyle(SkFontStyle::kNormal_Weight, SkFontStyle::kNormal_Width,
+ SkFontStyle::kItalic_Slant));
+
+ TextStyle style1;
+ style1.setForegroundColor(gray);
+ style1.setFontFamilies({SkString(ff)});
+ style1.setFontSize(fs);
+ style1.setFontStyle(SkFontStyle(SkFontStyle::kBold_Weight, SkFontStyle::kNormal_Width,
+ SkFontStyle::kUpright_Slant));
+
+ ParagraphStyle paraStyle;
+ paraStyle.setTextStyle(style);
+ paraStyle.setMaxLines(lineLimit);
+
+ paraStyle.setEllipsis(ellipsis);
+
+ ParagraphBuilderImpl builder(paraStyle, getFontCollection());
+
+ if (text.empty()) {
+ const std::u16string text0 = u"\u202Dabc";
+ const std::u16string text1 = u"\u202EFED";
+ const std::u16string text2 = u"\u202Dghi";
+ const std::u16string text3 = u"\u202ELKJ";
+ const std::u16string text4 = u"\u202Dmno";
+ builder.pushStyle(style0);
+ builder.addText(text0);
+ builder.pop();
+ builder.pushStyle(style1);
+ builder.addText(text1);
+ builder.pop();
+ builder.pushStyle(style0);
+ builder.addText(text2);
+ builder.pop();
+ builder.pushStyle(style1);
+ builder.addText(text3);
+ builder.pop();
+ builder.pushStyle(style0);
+ builder.addText(text4);
+ builder.pop();
+ } else {
+ if (this->isVerbose()) {
+ SkString str = SkStringFromU16String(text);
+ SkDebugf("Text: %s\n", str.c_str());
+ }
+ builder.addText(text + expected);
+ }
+
+ auto paragraph = builder.Build();
+ paragraph->layout(w - margin * 2);
+ paragraph->paint(canvas, margin, margin);
+ }
+
+ void onDrawContent(SkCanvas* canvas) override {
+ canvas->drawColor(SK_ColorWHITE);
+ SkScalar width = this->width();
+ SkScalar height = this->height() / 8;
+
+ const std::u16string text1 =
+ u"A \u202ENAC\u202Cner, exceedingly \u202ENAC\u202Cny,\n"
+ "One morning remarked to his granny:\n"
+ "A \u202ENAC\u202Cner \u202ENAC\u202C \u202ENAC\u202C,\n"
+ "Anything that he \u202ENAC\u202C,\n"
+ "But a \u202ENAC\u202Cner \u202ENAC\u202C't \u202ENAC\u202C a \u202ENAC\u202C, "
+ "\u202ENAC\u202C he?";
+ bidi(canvas, width, height * 3, text1, u"", 5);
+ canvas->translate(0, height * 3);
+
+ bidi(canvas, width, height, u"\u2067DETALOSI\u2069", u"");
+ canvas->translate(0, height);
+
+ bidi(canvas, width, height, u"\u202BDEDDEBME\u202C", u"");
+ canvas->translate(0, height);
+
+ bidi(canvas, width, height, u"\u202EEDIRREVO\u202C", u"");
+ canvas->translate(0, height);
+
+ bidi(canvas, width, height, u"\u200FTICILPMI\u200E", u"");
+ canvas->translate(0, height);
+
+ bidi(canvas, width, height, u"123 456 7890 \u202EZYXWV UTS RQP ONM LKJ IHG FED CBA\u202C.",
+ u"", 2);
+ canvas->translate(0, height);
+
+ // bidi(canvas, width, height, u"", u"");
+ // canvas->translate(0, height);
+ }
+
+private:
+ typedef Sample INHERITED;
+};
+
+class ParagraphView6 : public ParagraphView_Base {
+protected:
+ SkString name() override { return SkString("Paragraph6"); }
+
+ void hangingS(SkCanvas* canvas, SkScalar w, SkScalar h, SkScalar fs = 60.0) {
+ auto ff = "HangingS";
+
+ canvas->drawColor(SK_ColorLTGRAY);
+
+ SkPaint black;
+ black.setAntiAlias(true);
+ black.setColor(SK_ColorBLACK);
+
+ SkPaint blue;
+ blue.setAntiAlias(true);
+ blue.setColor(SK_ColorBLUE);
+
+ SkPaint red;
+ red.setAntiAlias(true);
+ red.setColor(SK_ColorRED);
+
+ SkPaint green;
+ green.setAntiAlias(true);
+ green.setColor(SK_ColorGREEN);
+
+ SkPaint gray;
+ gray.setColor(SK_ColorCYAN);
+
+ SkPaint yellow;
+ yellow.setColor(SK_ColorYELLOW);
+
+ SkPaint magenta;
+ magenta.setAntiAlias(true);
+ magenta.setColor(SK_ColorMAGENTA);
+
+ SkFontStyle fontStyle(SkFontStyle::kBold_Weight, SkFontStyle::kNormal_Width,
+ SkFontStyle::kItalic_Slant);
+
+ TextStyle style;
+ style.setFontFamilies({SkString(ff)});
+ style.setFontSize(fs);
+ style.setFontStyle(fontStyle);
+
+ TextStyle style0;
+ style0.setForegroundColor(black);
+ style0.setBackgroundColor(gray);
+ style0.setFontFamilies({SkString(ff)});
+ style0.setFontSize(fs);
+ style0.setFontStyle(fontStyle);
+
+ TextStyle style1;
+ style1.setForegroundColor(blue);
+ style1.setBackgroundColor(yellow);
+ style1.setFontFamilies({SkString(ff)});
+ style1.setFontSize(fs);
+ style1.setFontStyle(fontStyle);
+
+ TextStyle style2;
+ style2.setForegroundColor(red);
+ style2.setFontFamilies({SkString(ff)});
+ style2.setFontSize(fs);
+ style2.setFontStyle(fontStyle);
+
+ TextStyle style3;
+ style3.setForegroundColor(green);
+ style3.setFontFamilies({SkString(ff)});
+ style3.setFontSize(fs);
+ style3.setFontStyle(fontStyle);
+
+ TextStyle style4;
+ style4.setForegroundColor(magenta);
+ style4.setFontFamilies({SkString(ff)});
+ style4.setFontSize(fs);
+ style4.setFontStyle(fontStyle);
+
+ ParagraphStyle paraStyle;
+ paraStyle.setTextStyle(style);
+
+ const char* logo1 = "S";
+ const char* logo2 = "kia";
+ const char* logo3 = "Sk";
+ const char* logo4 = "ia";
+ const char* logo5 = "Ski";
+ const char* logo6 = "a";
+ {
+ ParagraphBuilderImpl builder(paraStyle, getFontCollection());
+
+ builder.pushStyle(style0);
+ builder.addText(logo1, strlen(logo1));
+ builder.pop();
+ builder.pushStyle(style1);
+ builder.addText(logo2, strlen(logo2));
+ builder.pop();
+
+ builder.addText(" ", 3);
+
+ builder.pushStyle(style0);
+ builder.addText(logo3, strlen(logo3));
+ builder.pop();
+ builder.pushStyle(style1);
+ builder.addText(logo4, strlen(logo4));
+ builder.pop();
+
+ builder.addText(" ", 3);
+
+ builder.pushStyle(style0);
+ builder.addText(logo5, strlen(logo5));
+ builder.pop();
+ builder.pushStyle(style1);
+ builder.addText(logo6, strlen(logo6));
+ builder.pop();
+
+ auto paragraph = builder.Build();
+ paragraph->layout(w);
+ paragraph->paint(canvas, 40, 40);
+ canvas->translate(0, h);
+ }
+
+ const char* logo11 = "S";
+ const char* logo12 = "S";
+ const char* logo13 = "S";
+ const char* logo14 = "S";
+ const char* logo15 = "S";
+ const char* logo16 = "S";
+ {
+ ParagraphBuilderImpl builder(paraStyle, getFontCollection());
+
+ builder.pushStyle(style0);
+ builder.addText(logo11, strlen(logo1));
+ builder.pop();
+ builder.pushStyle(style1);
+ builder.addText(logo12, strlen(logo2));
+ builder.pop();
+
+ builder.addText(" ", 3);
+
+ builder.pushStyle(style0);
+ builder.addText(logo13, strlen(logo3));
+ builder.pop();
+ builder.pushStyle(style1);
+ builder.addText(logo14, strlen(logo4));
+ builder.pop();
+
+ builder.addText(" ", 3);
+
+ builder.pushStyle(style0);
+ builder.addText(logo15, strlen(logo5));
+ builder.pop();
+ builder.pushStyle(style1);
+ builder.addText(logo16, strlen(logo6));
+ builder.pop();
+
+ auto paragraph = builder.Build();
+ paragraph->layout(w);
+ paragraph->paint(canvas, 40, h);
+ canvas->translate(0, h);
+ }
+ }
+
+ void onDrawContent(SkCanvas* canvas) override {
+ canvas->drawColor(SK_ColorWHITE);
+ SkScalar width = this->width();
+ SkScalar height = this->height() / 4;
+
+ hangingS(canvas, width, height);
+ }
+
+private:
+ typedef Sample INHERITED;
+};
+
+class ParagraphView7 : public ParagraphView_Base {
+protected:
+ SkString name() override { return SkString("Paragraph7"); }
+
+ void drawText(SkCanvas* canvas, SkColor background, SkScalar letterSpace, SkScalar w,
+ SkScalar h) {
+ SkAutoCanvasRestore acr(canvas, true);
+ canvas->clipRect(SkRect::MakeWH(w, h));
+ canvas->drawColor(background);
+
+ const char* line =
+ "World domination is such an ugly phrase - I prefer to call it world optimisation.";
+
+ ParagraphStyle paragraphStyle;
+ paragraphStyle.setTextAlign(TextAlign::kLeft);
+ paragraphStyle.setMaxLines(10);
+ paragraphStyle.turnHintingOff();
+ TextStyle textStyle;
+ textStyle.setFontFamilies({SkString("Roboto")});
+ textStyle.setFontSize(30);
+ textStyle.setLetterSpacing(letterSpace);
+ textStyle.setColor(SK_ColorBLACK);
+ textStyle.setFontStyle(SkFontStyle(SkFontStyle::kMedium_Weight, SkFontStyle::kNormal_Width,
+ SkFontStyle::kUpright_Slant));
+
+ ParagraphBuilderImpl builder(paragraphStyle, getFontCollection());
+ builder.pushStyle(textStyle);
+ builder.addText(line, strlen(line));
+ builder.pop();
+
+ auto paragraph = builder.Build();
+ paragraph->layout(w - 20);
+ paragraph->paint(canvas, 10, 10);
+ }
+
+ void onDrawContent(SkCanvas* canvas) override {
+ canvas->drawColor(SK_ColorWHITE);
+
+ auto h = this->height() / 4;
+ auto w = this->width() / 2;
+
+ drawText(canvas, SK_ColorGRAY, 1, w, h);
+ canvas->translate(0, h);
+
+ drawText(canvas, SK_ColorLTGRAY, 2, w, h);
+ canvas->translate(0, h);
+
+ drawText(canvas, SK_ColorCYAN, 3, w, h);
+ canvas->translate(0, h);
+
+ drawText(canvas, SK_ColorGRAY, 4, w, h);
+ canvas->translate(w, -3 * h);
+
+ drawText(canvas, SK_ColorYELLOW, 5, w, h);
+ canvas->translate(0, h);
+
+ drawText(canvas, SK_ColorGREEN, 10, w, h);
+ canvas->translate(0, h);
+
+ drawText(canvas, SK_ColorRED, 15, w, h);
+ canvas->translate(0, h);
+
+ drawText(canvas, SK_ColorBLUE, 20, w, h);
+ canvas->translate(0, h);
+ }
+
+private:
+ typedef Sample INHERITED;
+};
+
+class ParagraphView8 : public ParagraphView_Base {
+protected:
+ SkString name() override { return SkString("Paragraph8"); }
+
+ void drawText(SkCanvas* canvas, SkColor background, SkScalar wordSpace, SkScalar w,
+ SkScalar h) {
+ SkAutoCanvasRestore acr(canvas, true);
+ canvas->clipRect(SkRect::MakeWH(w, h));
+ canvas->drawColor(background);
+
+ const char* line =
+ "World domination is such an ugly phrase - I prefer to call it world optimisation.";
+
+ ParagraphStyle paragraphStyle;
+ paragraphStyle.setTextAlign(TextAlign::kLeft);
+ paragraphStyle.setMaxLines(10);
+ paragraphStyle.turnHintingOff();
+ TextStyle textStyle;
+ textStyle.setFontFamilies({SkString("Roboto")});
+ textStyle.setFontSize(30);
+ textStyle.setWordSpacing(wordSpace);
+ textStyle.setColor(SK_ColorBLACK);
+ textStyle.setFontStyle(SkFontStyle(SkFontStyle::kMedium_Weight, SkFontStyle::kNormal_Width,
+ SkFontStyle::kUpright_Slant));
+
+ ParagraphBuilderImpl builder(paragraphStyle, getFontCollection());
+ builder.pushStyle(textStyle);
+ builder.addText(line, strlen(line));
+ builder.pop();
+
+ auto paragraph = builder.Build();
+ paragraph->layout(w - 20);
+ paragraph->paint(canvas, 10, 10);
+ }
+
+ void onDrawContent(SkCanvas* canvas) override {
+ canvas->drawColor(SK_ColorWHITE);
+
+ auto h = this->height() / 4;
+ auto w = this->width() / 2;
+
+ drawText(canvas, SK_ColorGRAY, 1, w, h);
+ canvas->translate(0, h);
+
+ drawText(canvas, SK_ColorLTGRAY, 2, w, h);
+ canvas->translate(0, h);
+
+ drawText(canvas, SK_ColorCYAN, 3, w, h);
+ canvas->translate(0, h);
+
+ drawText(canvas, SK_ColorGRAY, 4, w, h);
+ canvas->translate(w, -3 * h);
+
+ drawText(canvas, SK_ColorYELLOW, 5, w, h);
+ canvas->translate(0, h);
+
+ drawText(canvas, SK_ColorGREEN, 10, w, h);
+ canvas->translate(0, h);
+
+ drawText(canvas, SK_ColorRED, 15, w, h);
+ canvas->translate(0, h);
+
+ drawText(canvas, SK_ColorBLUE, 20, w, h);
+ canvas->translate(0, h);
+ }
+
+private:
+ typedef Sample INHERITED;
+};
+
+class ParagraphView9 : public ParagraphView_Base {
+protected:
+ SkString name() override { return SkString("Paragraph9"); }
+
+ bool onChar(SkUnichar uni) override {
+ switch (uni) {
+ case 'w':
+ ++wordSpacing;
+ return true;
+ case 'q':
+ if (wordSpacing > 0) --wordSpacing;
+ return true;
+ case 'l':
+ ++letterSpacing;
+ return true;
+ case 'k':
+ if (letterSpacing > 0) --letterSpacing;
+ return true;
+ default:
+ break;
+ }
+ return false;
+ }
+
+ void drawText(SkCanvas* canvas, SkColor background, SkScalar w, SkScalar h) {
+ SkAutoCanvasRestore acr(canvas, true);
+ canvas->clipRect(SkRect::MakeWH(w, h));
+ canvas->drawColor(background);
+
+ auto fontCollection = sk_make_sp<FontCollection>();
+ fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
+ fontCollection->enableFontFallback();
+
+ const char* text =
+ "( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)("
+ " ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)("
+ " ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)";
+ auto multiplier = 5.67;
+ ParagraphStyle paragraphStyle;
+ paragraphStyle.setTextAlign(TextAlign::kLeft);
+ paragraphStyle.setMaxLines(10);
+ paragraphStyle.turnHintingOff();
+ TextStyle textStyle;
+ textStyle.setFontFamilies({SkString("Roboto")});
+ textStyle.setFontSize(5 * multiplier);
+ textStyle.setHeight(1.3f);
+ textStyle.setColor(SK_ColorBLACK);
+ textStyle.setFontStyle(SkFontStyle(SkFontStyle::kMedium_Weight, SkFontStyle::kNormal_Width,
+ SkFontStyle::kUpright_Slant));
+
+ ParagraphBuilderImpl builder(paragraphStyle, fontCollection);
+ builder.pushStyle(textStyle);
+ builder.addText(text, strlen(text));
+ builder.pop();
+
+ auto paragraph = builder.Build();
+ paragraph->layout(200 * multiplier);
+
+ std::vector<size_t> sizes = {0, 1, 2, 8, 19, 21, 22, 30, 150};
+
+ std::vector<size_t> colors = {SK_ColorBLUE, SK_ColorCYAN, SK_ColorLTGRAY, SK_ColorGREEN,
+ SK_ColorRED, SK_ColorWHITE, SK_ColorYELLOW, SK_ColorMAGENTA};
+
+ RectHeightStyle rect_height_style = RectHeightStyle::kTight;
+ RectWidthStyle rect_width_style = RectWidthStyle::kTight;
+
+ for (size_t i = 0; i < sizes.size() - 1; ++i) {
+ size_t from = sizes[i];
+ size_t to = sizes[i + 1];
+ auto boxes = paragraph->getRectsForRange(from, to, rect_height_style, rect_width_style);
+ if (boxes.empty()) {
+ continue;
+ }
+ for (auto& box : boxes) {
+ SkPaint paint;
+ paint.setColor(colors[i % colors.size()]);
+ paint.setShader(setgrad(box.rect, colors[i % colors.size()], SK_ColorWHITE));
+ canvas->drawRect(box.rect, paint);
+ }
+ }
+
+ paragraph->paint(canvas, 0, 0);
+ }
+
+ void onDrawContent(SkCanvas* canvas) override {
+ canvas->drawColor(SK_ColorWHITE);
+
+ auto h = this->height();
+ auto w = this->width();
+
+ drawText(canvas, SK_ColorGRAY, w, h);
+ }
+
+private:
+ typedef Sample INHERITED;
+ SkScalar letterSpacing;
+ SkScalar wordSpacing;
+};
+
+class ParagraphView10 : public ParagraphView_Base {
+protected:
+ SkString name() override { return SkString("Paragraph10"); }
+
+ void onDrawContent(SkCanvas* canvas) override {
+ canvas->drawColor(SK_ColorWHITE);
+ auto multiplier = 5.67;
+ const char* text = "English English 字典 字典 😀😃😄 😀😃😄";
+
+ auto fontCollection = sk_make_sp<FontCollection>();
+ fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
+ fontCollection->enableFontFallback();
+
+ ParagraphStyle paragraph_style;
+ paragraph_style.turnHintingOff();
+ ParagraphBuilderImpl builder(paragraph_style, fontCollection);
+
+ TextStyle text_style;
+ text_style.setFontFamilies({SkString("Roboto"),
+ SkString("Noto Color Emoji"),
+ SkString("Noto Serif CJK JP")});
+ text_style.setFontSize(10 * multiplier);
+ text_style.setLetterSpacing(0);
+ text_style.setWordSpacing(0);
+ text_style.setColor(SK_ColorBLACK);
+ text_style.setHeight(1);
+ builder.pushStyle(text_style);
+ builder.addText(text, strlen(text));
+ builder.pop();
+
+ auto paragraph = builder.Build();
+ paragraph->layout(width());
+
+ paragraph->paint(canvas, 0, 0);
+ }
+
+private:
+ typedef Sample INHERITED;
+};
+
+class ParagraphView11 : public ParagraphView_Base {
+protected:
+ SkString name() override { return SkString("Paragraph11"); }
+
+ void onDrawContent(SkCanvas* canvas) override {
+ canvas->drawColor(SK_ColorWHITE);
+
+ auto text = "\U0001f469\U0000200D\U0001f469\U0000200D\U0001f466\U0001f469\U0000200D\U0001f469\U0000200D\U0001f467\U0000200D\U0001f467\U0001f1fa\U0001f1f8";
+
+ TextStyle text_style;
+ text_style.setFontFamilies({SkString("Ahem")});
+ text_style.setColor(SK_ColorBLACK);
+ text_style.setFontSize(60);
+ text_style.setLetterSpacing(0);
+ text_style.setWordSpacing(0);
+ ParagraphStyle paragraph_style;
+ paragraph_style.setTextStyle(text_style);
+
+ auto fontCollection = sk_make_sp<TestFontCollection>(GetResourcePath("fonts").c_str(), true, true);
+ ParagraphBuilderImpl builder(paragraph_style, fontCollection);
+ builder.addText(text, strlen(text));
+ auto paragraph = builder.Build();
+ paragraph->layout(1000);
+ paragraph->paint(canvas, 0, 0);
+
+ struct pair {
+ unsigned fX;
+ unsigned fY;
+ };
+
+ pair hit1[] =
+ {{ 0, 8},{1, 33}, {2, 34}, { 3, 19}, {4, 20},
+ { 5, 21}, { 6, 22 }, { 7, 23 }, {8, 24 }, { 9, 25},
+ { 10, 26}, { 11, 27}, {12, 28}, { 13, 21}, {14, 22 },
+ { 15, 23}, {16, 24}, {17, 21}, { 18, 22}, {19, 21},
+ { 20, 24}, { 21, 23}, };
+
+ pair miss[] =
+ {{ 0, 4},{1, 17}, {2, 18}, { 3, 11}, {4, 12},
+ { 5, 13}, { 6, 14 }, { 7, 15 }, {8, 16 }, { 9, 17},
+ { 10, 18}, { 11, 19}, {12, 20}, { 13, 17}, {14, 18 },
+ { 15, 19}, {16, 20}, {17, 19}, { 18, 20},
+ { 20, 22}, };
+
+ auto rects = paragraph->getRectsForRange(7, 9, RectHeightStyle::kTight, RectWidthStyle::kTight);
+ SkPaint paint;
+ paint.setColor(SK_ColorRED);
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setAntiAlias(true);
+ paint.setStrokeWidth(1);
+ if (!rects.empty()) {
+ canvas->drawRect(rects[0].rect, paint);
+ }
+
+ for (auto& query : hit1) {
+ auto rects = paragraph->getRectsForRange(query.fX, query.fY, RectHeightStyle::kTight, RectWidthStyle::kTight);
+ if (rects.size() >= 1 && rects[0].rect.width() > 0) {
+ } else {
+ if (this->isVerbose()) {
+ SkDebugf("+[%d:%d): Bad\n", query.fX, query.fY);
+ }
+ }
+ }
+
+ for (auto& query : miss) {
+ auto miss = paragraph->getRectsForRange(query.fX, query.fY, RectHeightStyle::kTight, RectWidthStyle::kTight);
+ if (miss.empty()) {
+ } else {
+ if (this->isVerbose()) {
+ SkDebugf("-[%d:%d): Bad\n", query.fX, query.fY);
+ }
+ }
+ }
+ }
+
+private:
+ typedef Sample INHERITED;
+};
+
+class ParagraphView12 : public ParagraphView_Base {
+protected:
+ SkString name() override { return SkString("Paragraph12"); }
+
+ void onDrawContent(SkCanvas* canvas) override {
+ canvas->drawColor(SK_ColorWHITE);
+
+ const char* text = "Atwater Peel Sherbrooke Bonaventure Angrignon Peel Côte-des-Neiges";
+ TextStyle text_style;
+ text_style.setFontFamilies({SkString("Ahem")});
+ text_style.setColor(SK_ColorBLACK);
+ text_style.setFontSize(16);
+ //text_style.setLetterSpacing(-0.41);
+ StrutStyle strut_style;
+ strut_style.setStrutEnabled(false);
+ ParagraphStyle paragraph_style;
+ paragraph_style.setStrutStyle(strut_style);
+ paragraph_style.setTextStyle(text_style);
+ ParagraphBuilderImpl builder(paragraph_style, getFontCollection());
+ builder.addText(text);
+ auto paragraph = builder.Build();
+ paragraph->layout(1095.000000);
+ auto result = paragraph->getRectsForRange(65, 66, RectHeightStyle::kTight, RectWidthStyle::kTight);
+ paragraph->paint(canvas, 0, 0);
+
+ SkPaint paint;
+ paint.setColor(SK_ColorRED);
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setAntiAlias(true);
+ paint.setStrokeWidth(1);
+ if (!result.empty()) {
+ canvas->drawRect(result.front().rect, paint);
+ }
+ }
+
+private:
+ typedef Sample INHERITED;
+};
+
+class ParagraphView14 : public ParagraphView_Base {
+protected:
+ SkString name() override { return SkString("Paragraph14"); }
+
+ void onDrawContent(SkCanvas* canvas) override {
+ canvas->drawColor(SK_ColorWHITE);
+ TextStyle text_style;
+ text_style.setFontFamilies({SkString("Ahem")});
+ text_style.setColor(SK_ColorBLACK);
+ text_style.setFontSize(25);
+ text_style.setDecoration((TextDecoration)(TextDecoration::kUnderline | TextDecoration::kOverline | TextDecoration::kLineThrough));
+ text_style.setDecorationColor(SK_ColorBLUE);
+ text_style.setDecorationStyle(TextDecorationStyle::kWavy);
+ text_style.setDecorationThicknessMultiplier(4.0f);
+ ParagraphStyle paragraph_style;
+ paragraph_style.setTextStyle(text_style);
+ paragraph_style.setTextDirection(TextDirection::kRtl);
+ ParagraphBuilderImpl builder(paragraph_style, getFontCollection());
+ builder.pushStyle(text_style);
+ builder.addText("Hello, wor!\nabcd.");
+ auto paragraph = builder.Build();
+ paragraph->layout(300);
+ paragraph->paint(canvas, 0, 0);
+ SkPaint paint;
+ paint.setColor(SK_ColorRED);
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setAntiAlias(true);
+ paint.setStrokeWidth(1);
+ canvas->drawRect(SkRect::MakeXYWH(0, 0, 300, 100), paint);
+ }
+
+private:
+ typedef Sample INHERITED;
+};
+
+class ParagraphView15 : public ParagraphView_Base {
+protected:
+ SkString name() override { return SkString("Paragraph15"); }
+
+ void onDrawContent(SkCanvas* canvas) override {
+ canvas->drawColor(SK_ColorWHITE);
+
+ TextStyle text_style;
+ text_style.setFontFamilies({SkString("abc.ttf")});
+ text_style.setFontSize(50);
+
+ auto fontCollection = sk_make_sp<TestFontCollection>(GetResourcePath("fonts").c_str(), false);
+
+ fontCollection->addFontFromFile("abc/abc.ttf", "abc");
+ fontCollection->addFontFromFile("abc/abc+grave.ttf", "abc+grave");
+ fontCollection->addFontFromFile("abc/abc+agrave.ttf", "abc+agrave");
+
+ ParagraphStyle paragraph_style;
+ ParagraphBuilderImpl builder(paragraph_style, fontCollection);
+
+ text_style.setFontFamilies({SkString("abc"), SkString("abc+grave")});
+ text_style.setColor(SK_ColorBLUE);
+ builder.pushStyle(text_style);
+ builder.addText(u"a\u0300");
+ text_style.setColor(SK_ColorMAGENTA);
+ builder.pushStyle(text_style);
+ builder.addText(u"à");
+
+ text_style.setFontFamilies({SkString("abc"), SkString("abc+agrave")});
+
+ text_style.setColor(SK_ColorRED);
+ builder.pushStyle(text_style);
+ builder.addText(u"a\u0300");
+ text_style.setColor(SK_ColorGREEN);
+ builder.pushStyle(text_style);
+ builder.addText(u"à");
+
+ auto paragraph = builder.Build();
+ paragraph->layout(800);
+ paragraph->paint(canvas, 50, 50);
+
+ }
+
+private:
+ typedef Sample INHERITED;
+};
+
+class ParagraphView16 : public ParagraphView_Base {
+protected:
+ SkString name() override { return SkString("Paragraph16"); }
+
+ void onDrawContent(SkCanvas* canvas) override {
+ canvas->drawColor(SK_ColorWHITE);
+
+ const char* text = "content";
+
+ ParagraphStyle paragraph_style;
+ paragraph_style.setMaxLines(1);
+ paragraph_style.setEllipsis(u"\u2026");
+ //auto fontCollection = sk_make_sp<TestFontCollection>(GetResourcePath("fonts").c_str(), false, true);
+ auto fontCollection = sk_make_sp<FontCollection>();
+ fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
+ fontCollection->enableFontFallback();
+ ParagraphBuilderImpl builder(paragraph_style, fontCollection);
+
+ TextStyle text_style;
+ text_style.setFontFamilies({SkString(".SF Pro Text")});
+ text_style.setColor(SK_ColorBLACK);
+ text_style.setFontSize(17.0f * 99.0f);
+ text_style.setLetterSpacing(0.41f);
+ builder.pushStyle(text_style);
+ builder.addText(text);
+
+ auto paragraph = builder.Build();
+ paragraph->layout(800);
+ paragraph->paint(canvas, 0, 0);
+ }
+
+private:
+ typedef Sample INHERITED;
+};
+
+class ParagraphView17 : public ParagraphView_Base {
+protected:
+ SkString name() override { return SkString("Paragraph17"); }
+
+ void onDrawContent(SkCanvas* canvas) override {
+ canvas->drawColor(SK_ColorWHITE);
+
+ auto fontCollection = sk_make_sp<FontCollection>();
+ fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
+ fontCollection->enableFontFallback();
+ auto navy = SkColorSetRGB(0, 0, 139);
+ auto ltgray = SkColorSetRGB(211, 211, 211);
+ auto multiplier = 5.67;
+
+ //const char* text = ">Sͬ͑̀͐̈͒̈́̋̎ͮͩ̽̓ͬ̂̆̔͗́̓ͣͧ͊ͫ͛̉͌̐̑ͪ͗̚͝҉̴͉͢k̡̊̓ͫͭͩ͂͊ͨͪͬ̑ͫ̍̌̄͛̌̂̑̂̋̊̔ͫ͛̽̑ͨ̍ͭ̓̀ͪͪ̉͐͗̌̓̃̚͟͝҉̢͏̫̞̙͇͖̮͕̗̟͕͇͚̻͈̣̻̪͉̰̲̣̫ͅͅP̴̅̍͒̿͗͗̇ͩ̃͆͌̀̽͏̧̡͕͖̝̖̼̺̰̣̬͔͖͔̼͙̞̦̫͓̘͜a̸̴̸̴̢̢̨̨̫͍͓̥̼̭̼̻̤̯̙̤̻̠͚̍̌͋̂ͦͨ̽̇͌͌͆̀̽̎͒̄ͪ̐ͦ̈ͫ͐͗̓̚̚͜ͅr͐͐ͤͫ̐ͥ͂̈́̿́ͮ̃͗̓̏ͫ̀̿͏̸̵̧́͘̕͟͝͠͞͠҉̷̧͚͢͟a̓̽̎̄͗̔͛̄̐͊͛ͫ͂͌̂̂̈̈̓̔̅̅̄͊̉́ͪ̑̄͆ͬ̍͆ͭ͋̐ͬ͏̷̵̨̢̩̹̖͓̥̳̰͔̱̬͖̙͓̙͇̀̀̕͜͟͟͢͟͜͠͡g̨̅̇ͦ͋̂ͦͨͭ̓͐͆̏̂͛̉ͧ̑ͫ̐̒͛ͫ̍̒͛́̚҉̷̨̛̛̀͜͢͞҉̩̘̲͍͎̯̹̝̭̗̱͇͉̲̱͔̯̠̹̥̻͉̲̜̤̰̪̗̺̖̺r̷͌̓̇̅ͭ̀̐̃̃ͭ͑͗̉̈̇̈́ͥ̓ͣ́ͤ͂ͤ͂̏͌̆̚҉̴̸̧̢̢̛̫͉̦̥̤̙͈͉͈͉͓̙̗̟̳̜͈̗̺̟̠̠͖͓̖̪͕̠̕̕͝ͅả̸̴̡̡̧͠͞͡͞҉̛̕͟͏̷̘̪̱͈̲͉̞̠̞̪̫͎̲̬̖̀̀͟͝͞͞͠p̛͂̈͐̚͠҉̵̸̡̢̢̩̹͙̯͖̙̙̮̥̙͚̠͔̥̭̮̞̣̪̬̥̠̖̝̥̪͎́̀̕͜͡͡ͅͅh̵̷̵̡̛ͤ̂͌̐̓̐̋̋͊̒̆̽́̀̀̀͢͠͞͞҉̷̸̢̕҉͚̯͖̫̜̞̟̠̱͉̝̲̹̼͉̟͉̩̮͔̤͖̞̭̙̹̬ͅ<";
+ const char* text = ">S͛ͭ̋͆̈̔̇͗̍͑̎ͪͮͧͣ̽ͫͣ́ͬ̀͌͑͂͗͒̍̔̄ͧ̏̉̌̊̊̿̀̌̃̄͐̓̓̚̚҉̵̡͜͟͝͠͏̸̵̡̧͜҉̷̡͇̜̘̻̺̘̟̝͙̬̘̩͇̭̼̥̖̤̦͎k͉̩̘͚̜̹̗̗͍̤̥̱͉̳͕͖̤̲̣͚̮̞̬̲͍͔̯̻̮̞̭͈̗̫͓̂ͨ̉ͪ̒͋͛̀̍͊ͧ̿̅͆̓̔̔ͬ̇̑̿ͩ͗ͮ̎͌̿̄ͅP̴̵̡̡̛̪͙̼̣̟̩̭̫̱͙̬͔͉͍̘̠͉̦̝̘̥̟̗͖̫̤͕̙̬̦͍̱̖̮̱͑͐̎̃̒͐͋̚͘͞a̶̶̵̵̵̶̶̡̧̢̢̺͔̣͖̭̺͍̤͚̱̜̰̥͕̬̥̲̞̥̘͇͚̺̰͚̪̺͔̤͍̓̿͆̎͋̓ͦ̈́ͦ̌́̄͗̌̓͌̕͜͜͟͢͝͡ŕ͎̝͕͉̻͎̤̭͚̗̳̖̙̘͚̫͖͓͚͉͔͈̟̰̟̬̗͓̟͚̱̕͡ͅͅͅa̸̶̢̛̛̽ͮͩ̅͒ͫ͗͂̎ͦ̈́̓̚͘͜͢͡҉̷̵̶̢̡̜̮̦̜̥̜̯̙͓͔̼̗̻͜͜ͅḡ̢̛͕̗͖̖̤̦̘͔ͨͨ̊͒ͩͭͤ̍̅̃ͪ̋̏̓̍̋͗̋ͨ̏̽̈́̔̀̋̉ͫ̅̂ͭͫ̏͒͋ͥ̚͜r̶̢̧̧̥̤̼̀̂̒ͪ͌̿͌̅͛ͨͪ͒̍ͥ̉ͤ̌̿̆́ͭ͆̃̒ͤ͛̊ͧ̽͘͝͠a̧̢̧̢͑͑̓͑ͮ̃͂̄͛́̈́͋̂͌̽̄͒̔́̇ͨͧͭ͐ͦ̋ͨ̍ͦ̍̋͆̔ͧ͑͋͌̈̓͛͛̚͢͜͜͏̴̢̧̛̳͍̹͚̰̹̻͔p̨̡͆ͦͣ͊̽̔͂̉ͣ̔ͣ̌̌̉̃̋̂͒ͫ̄̎̐͗̉̌̃̽̽́̀̚͘͜͟҉̱͉h̭̮̘̗͔̜̯͔͈̯̺͔̗̣̭͚̱̰̙̼̹͚̣̻̥̲̮͍̤͜͝<";
+ ParagraphStyle paragraph_style;
+ ParagraphBuilderImpl builder(paragraph_style, fontCollection);
+ SkPaint paint;
+ paint.setColor(ltgray);
+ TextStyle text_style;
+ text_style.setBackgroundColor(paint);
+ text_style.setColor(navy);
+ text_style.setFontFamilies({SkString("Roboto")});
+ text_style.setFontSize(20 * multiplier);
+ builder.pushStyle(text_style);
+ builder.addText(text);
+ auto paragraph = builder.Build();
+ paragraph->layout(10000);
+ paragraph->paint(canvas, 0, 0);
+ }
+
+private:
+ typedef Sample INHERITED;
+};
+
+class Zalgo {
+ private:
+ std::u16string COMBINING_DOWN = u"\u0316\u0317\u0318\u0319\u031c\u031d\u031e\u031f\u0320\u0324\u0325\u0326\u0329\u032a\u032b\u032c\u032d\u032e\u032f\u0330\u0331\u0332\u0333\u0339\u033a\u033b\u033c\u0345\u0347\u0348\u0349\u034d\u034e\u0353\u0354\u0355\u0356\u0359\u035a\u0323";
+ std::u16string COMBINING_UP = u"\u030d\u030e\u0304\u0305\u033f\u0311\u0306\u0310\u0352\u0357\u0351\u0307\u0308\u030a\u0342\u0343\u0344\u034a\u034b\u034c\u0303\u0302\u030c\u0350\u0300\u0301\u030b\u030f\u0312\u0313\u0314\u033d\u0309\u0363\u0364\u0365\u0366\u0367\u0368\u0369\u036a\u036b\u036c\u036d\u036e\u035b\u0346\u031a";
+ std::u16string COMBINING_MIDDLE = u"\u0315\u031b\u0340\u0341\u0358\u0321\u0322\u0327\u0328\u0334\u0335\u0336\u034f\u035c\u035d\u035e\u035f\u0360\u0362\u0338\u0337\u0361\u0489";
+
+ std::u16string randomMarks(std::u16string& combiningMarks) {
+ std::u16string result;
+ auto num = std::rand() % (combiningMarks.size() / 1);
+ for (size_t i = 0; i < num; ++i) {
+ auto index = std::rand() % combiningMarks.size();
+ result += combiningMarks[index];
+ }
+ return result;
+ }
+
+public:
+ std::u16string zalgo(std::string victim) {
+ std::u16string result;
+ for (auto& c : victim) {
+ result += c;
+ result += randomMarks(COMBINING_UP);
+ result += randomMarks(COMBINING_MIDDLE);
+ result += randomMarks(COMBINING_DOWN);
+ }
+ return result;
+ }
+};
+
+class ParagraphView18 : public ParagraphView_Base {
+protected:
+ SkString name() override { return SkString("Paragraph18"); }
+
+ bool onChar(SkUnichar uni) override {
+ switch (uni) {
+ case ' ':
+ fLimit = 400;
+ return true;
+ case 's':
+ fLimit += 10;
+ return true;
+ case 'f':
+ if (fLimit > 10) {
+ fLimit -= 10;
+ }
+ return true;
+ default:
+ break;
+ }
+ return false;
+ }
+
+ bool onAnimate(double nanos) override {
+ if (++fIndex > fLimit) {
+ fRedraw = true;
+ fIndex = 0;
+ } else {
+ fRepeat = true;
+ }
+ return true;
+ }
+
+ void onDrawContent(SkCanvas* canvas) override {
+ canvas->drawColor(SK_ColorWHITE);
+
+ auto navy = SkColorSetRGB(0, 0, 139);
+ auto ltgray = SkColorSetRGB(211, 211, 211);
+
+ auto multiplier = 5.67;
+ auto fontCollection = sk_make_sp<FontCollection>();
+ fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
+ fontCollection->enableFontFallback();
+
+ ParagraphStyle paragraph_style;
+ TextStyle text_style;
+ text_style.setFontFamilies({SkString("Roboto")});
+ text_style.setFontSize(20 * multiplier);
+ text_style.setColor(navy);
+ SkPaint paint;
+ paint.setColor(ltgray);
+ text_style.setBackgroundColor(paint);
+
+ Zalgo zalgo;
+
+ if (fRedraw || fRepeat) {
+
+ if (fRedraw || fParagraph.get() == nullptr) {
+ ParagraphBuilderImpl builder(paragraph_style, fontCollection);
+ builder.pushStyle(text_style);
+ auto utf16text = zalgo.zalgo("SkParagraph");
+ if (this->isVerbose()) {
+ SkString str = SkStringFromU16String(utf16text);
+ SkDebugf("Text:>%s<\n", str.c_str());
+ }
+ builder.addText(utf16text);
+ fParagraph = builder.Build();
+ }
+
+ auto impl = static_cast<ParagraphImpl*>(fParagraph.get());
+ impl->setState(InternalState::kUnknown);
+ fParagraph->layout(1000);
+ fParagraph->paint(canvas, 300, 200);
+
+ for (auto& run : impl->runs()) {
+ SkString fontFamily("unresolved");
+ if (run.font().getTypeface() != nullptr) {
+ run.font().getTypeface()->getFamilyName(&fontFamily);
+ }
+ if (run.font().getTypeface() != nullptr) {
+ for (size_t i = 0; i < run.size(); ++i) {
+ auto glyph = run.glyphs().begin() + i;
+ if (*glyph == 0) {
+ //SkDebugf("Run[%d] @pos=%d\n", run.index(), i);
+ }
+ }
+ } else {
+ //SkDebugf("Run[%d]: %s\n", run.index(), fontFamily.c_str());
+ }
+ }
+ fRedraw = false;
+ fRepeat = false;
+ }
+ }
+
+private:
+ bool fRedraw = true;
+ bool fRepeat = false;
+ size_t fIndex = 0;
+ size_t fLimit = 20;
+ std::unique_ptr<Paragraph> fParagraph;
+ typedef Sample INHERITED;
+};
+
+class ParagraphView19 : public ParagraphView_Base {
+protected:
+ SkString name() override { return SkString("Paragraph19"); }
+
+ void onDrawContent(SkCanvas* canvas) override {
+ canvas->drawColor(SK_ColorWHITE);
+
+ auto fontCollection = sk_make_sp<TestFontCollection>(GetResourcePath("fonts").c_str(), false, true);
+
+ std::u16string text = u"\u0068\u0301\u0350\u0312\u0357\u030C\u0369\u0305\u036C\u0304\u0310\u033F\u0366\u0350\u0343\u0364\u0369\u0311\u0309\u030E\u0365\u031B\u0340\u0337\u0335\u035E\u0334\u0328\u0360\u0360\u0315\u035F\u0340\u0340\u0362\u0360\u0322\u031B\u031B\u0337\u0340\u031E\u031F\u032A\u0331\u0345\u032F\u0332\u032E\u0333\u0353\u0320\u0345\u031C\u031F\u033C\u0325\u0355\u032C\u0325\u033Aa\u0307\u0312\u034B\u0308\u0312\u0346\u0313\u0346\u0304\u0307\u0344\u0305\u0342\u0368\u0346\u036A\u035B\u030F\u0365\u0307\u0340\u0328\u0322\u0361\u0489\u034F\u0328\u0334\u035F\u0335\u0362\u0489\u0360\u0358\u035E\u0360\u035D\u0341\u0337\u0337\u032E\u0326\u032D\u0359\u0318\u033C\u032F\u0333\u035A\u034D\u0319\u031C\u0353\u033C\u0345\u0359\u0331\u033B\u0331\u033C";
+ ParagraphStyle paragraph_style;
+ ParagraphBuilderImpl builder(paragraph_style, fontCollection);
+ TextStyle text_style;
+ text_style.setColor(SK_ColorBLACK);
+ text_style.setFontFamilies({SkString("Roboto")});
+ text_style.setFontSize(20);
+ builder.pushStyle(text_style);
+ builder.addText(text);
+ auto paragraph = builder.Build();
+ paragraph->layout(this->width());
+ paragraph->paint(canvas, 0, 0);
+ }
+
+private:
+ typedef Sample INHERITED;
+};
+
+class ParagraphView20 : public ParagraphView_Base {
+protected:
+ SkString name() override { return SkString("Paragraph20"); }
+
+ void onDrawContent(SkCanvas* canvas) override {
+ canvas->drawColor(SK_ColorWHITE);
+
+ auto fontCollection = sk_make_sp<TestFontCollection>(GetResourcePath("fonts").c_str(), false, true);
+
+ const char* text = "Manage your google account";
+ ParagraphStyle paragraph_style;
+ paragraph_style.setEllipsis(u"\u2026");
+ paragraph_style.setMaxLines(1);
+ ParagraphBuilderImpl builder(paragraph_style, fontCollection);
+ TextStyle text_style;
+ text_style.setColor(SK_ColorBLACK);
+ text_style.setFontFamilies({SkString("Roboto")});
+ text_style.setFontSize(50);
+ builder.pushStyle(text_style);
+ builder.addText(text);
+ auto paragraph = builder.Build();
+ paragraph->layout(this->width());
+ paragraph->paint(canvas, 0, 0);
+ }
+
+private:
+ typedef Sample INHERITED;
+};
+
+class ParagraphView21 : public ParagraphView_Base {
+protected:
+ SkString name() override { return SkString("Paragraph21"); }
+
+ void onDrawContent(SkCanvas* canvas) override {
+ canvas->drawColor(SK_ColorWHITE);
+
+ const char* text = "Referral Code";
+ ParagraphStyle paragraph_style;
+ ParagraphBuilderImpl builder(paragraph_style, getFontCollection());
+ TextStyle text_style;
+ text_style.setColor(SK_ColorBLACK);
+ text_style.setFontFamilies({SkString("Google Sans")});
+ text_style.setFontSize(24);
+ builder.pushStyle(text_style);
+ builder.addText(text);
+ auto paragraph = builder.Build();
+ paragraph->layout(0);
+ paragraph->paint(canvas, 0, 0);
+ }
+
+private:
+ typedef Sample INHERITED;
+};
+
+class ParagraphView22 : public ParagraphView_Base {
+protected:
+ SkString name() override { return SkString("Paragraph22"); }
+
+ bool onChar(SkUnichar uni) override {
+ switch (uni) {
+ case 'l':
+ direction = true;
+ return true;
+ case 'r':
+ direction = false;
+ return true;
+ default:
+ break;
+ }
+ return false;
+ }
+
+ void onDrawContent(SkCanvas* canvas) override {
+
+ canvas->drawColor(SK_ColorWHITE);
+ ParagraphStyle paragraph_style;
+ paragraph_style.setTextDirection(direction ? TextDirection::kLtr : TextDirection::kRtl);
+ auto collection = getFontCollection();
+ ParagraphBuilderImpl builder(paragraph_style, collection);
+ collection->getParagraphCache()->reset();
+ collection->getParagraphCache()->turnOn(false);
+ TextStyle text_style;
+ text_style.setColor(SK_ColorBLACK);
+ text_style.setFontFamilies({SkString("Roboto")});
+ text_style.setFontSize(12);
+ builder.pushStyle(text_style);
+ builder.addText("I have got a ");
+ text_style.setFontStyle(SkFontStyle::Bold());
+ builder.pushStyle(text_style);
+ builder.addText("lovely bunch");
+ text_style.setFontStyle(SkFontStyle::Normal());
+ builder.pushStyle(text_style);
+ builder.addText(" of coconuts.");
+ auto paragraph = builder.Build();
+ paragraph->layout(this->width());
+ paragraph->paint(canvas, 0, 0);
+ collection->getParagraphCache()->turnOn(true);
+ }
+
+private:
+ typedef Sample INHERITED;
+ bool direction;
+};
+
+class ParagraphView23 : public ParagraphView_Base {
+protected:
+ SkString name() override { return SkString("Paragraph23"); }
+
+ void onDrawContent(SkCanvas* canvas) override {
+ canvas->drawColor(SK_ColorWHITE);
+
+ const char* text = "Text with shadow";
+ ParagraphStyle paragraph_style;
+ TextStyle text_style;
+ text_style.setColor(SK_ColorBLACK);
+ text_style.setFontFamilies({SkString("Google Sans")});
+ text_style.setFontSize(24);
+
+ auto draw = [&](SkScalar h, SkScalar v, SkScalar b) {
+ text_style.resetShadows();
+ text_style.addShadow(TextShadow(SK_ColorBLACK, SkPoint::Make(h, v), b));
+ ParagraphBuilderImpl builder(paragraph_style, getFontCollection());
+ builder.pushStyle(text_style);
+ builder.addText(text);
+ auto paragraph = builder.Build();
+ paragraph->layout(300);
+ paragraph->paint(canvas, 0, 0);
+
+ auto rect = SkRect::MakeXYWH(0, 0, paragraph->getMaxWidth(), paragraph->getHeight());
+ SkPaint paint;
+ paint.setColor(SK_ColorRED);
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setAntiAlias(true);
+ paint.setStrokeWidth(1);
+ canvas->drawRect(rect, paint);
+ };
+
+ draw(10, 10, 5);
+ canvas->translate(0, 100);
+
+ draw(10, -10, 5);
+ canvas->translate(0, 100);
+
+ draw(-10, -10, 5);
+ canvas->translate(0, 100);
+
+ draw(-10, 10, 5);
+ canvas->translate(0, 100);
+ }
+
+private:
+ typedef Sample INHERITED;
+};
+
+class ParagraphView24 : public ParagraphView_Base {
+protected:
+ SkString name() override { return SkString("Paragraph24"); }
+
+ void onDrawContent(SkCanvas* canvas) override {
+ canvas->drawColor(SK_ColorWHITE);
+
+ ParagraphStyle paragraph_style;
+ paragraph_style.setTextDirection(TextDirection::kRtl);
+ TextStyle text_style;
+ text_style.setColor(SK_ColorBLACK);
+ text_style.setFontFamilies({SkString("Google Sans")});
+ text_style.setFontSize(24);
+ {
+ ParagraphBuilderImpl builder(paragraph_style, getFontCollection());
+ builder.pushStyle(text_style);
+ builder.addText("Right_to_left:");
+ auto paragraph = builder.Build();
+ paragraph->layout(this->width());
+ paragraph->paint(canvas, 0, 0);
+ }
+ canvas->translate(0, 200);
+ {
+ ParagraphBuilderImpl builder(paragraph_style, getFontCollection());
+ builder.pushStyle(text_style);
+ builder.addText("Right_to_left+");
+ auto paragraph = builder.Build();
+ paragraph->layout(this->width());
+ paragraph->paint(canvas, 0, 0);
+ }
+ canvas->translate(0, 200);
+ {
+ ParagraphBuilderImpl builder(paragraph_style, getFontCollection());
+ builder.pushStyle(text_style);
+ builder.addText("Right_to_left.");
+ auto paragraph = builder.Build();
+ paragraph->layout(this->width());
+ paragraph->paint(canvas, 0, 0);
+ }
+ }
+
+private:
+ typedef Sample INHERITED;
+};
+
+class ParagraphView25 : public ParagraphView_Base {
+protected:
+ SkString name() override { return SkString("Paragraph25"); }
+
+ void onDrawContent(SkCanvas* canvas) override {
+ canvas->drawColor(SK_ColorWHITE);
+/*
+ * Shell: ParagraphStyle: 1.000000 1
+Shell: Strut enabled: 0 1.000000 14.000000 400 5 0
+Shell: Font Families: 0
+Shell: DefaultTextStyle: 16.000000 500 5 0
+Shell: Font Families: 1 Roboto
+Shell: Font Features: 0
+Shell: TextStyle#0: [0:22) 16.000000 500 5 0
+Shell: Font Families: 1 Roboto
+Shell: Font Features: 0
+Shell: TextStyle#1: [25:49) 16.000000 500 5 0
+Shell: Font Families: 1 Roboto
+Shell: Font Features: 0
+Shell: Placeholder#0: [22:25) 32.000000 32.000000 32.000000 0 5
+Shell: Placeholder#1: [49:52) 19.000000 41.000000 19.000000 0 4
+Shell: Placeholder#2: [52:52) 0.000000 0.000000 0.000000 0 5
+Shell: layout('Go to device settings  and set up a passcode. ', 280.000000): 280.000000 * 38.000000
+ */
+ auto fontCollection = getFontCollection();
+ //fontCollection->getParagraphCache()->turnOn(false);
+ const char* text1 = "Go to device settings ";
+ const char* text2 = "and set up a passcode.";
+ ParagraphStyle paragraph_style;
+ StrutStyle strut_style;
+ strut_style.setStrutEnabled(false);
+ strut_style.setFontSize(14);
+ strut_style.setForceStrutHeight(false);
+ strut_style.setHeight(14);
+ paragraph_style.setStrutStyle(strut_style);
+ TextStyle text_style;
+ text_style.setColor(SK_ColorBLACK);
+ text_style.setFontFamilies({SkString("Roboto")});
+ text_style.setFontSize(16);
+ PlaceholderStyle placeholder_style;
+ {
+ ParagraphBuilderImpl builder(paragraph_style, fontCollection);
+ builder.pushStyle(text_style);
+ builder.addText(text1);
+ placeholder_style.fHeight = 32;
+ placeholder_style.fWidth = 32;
+ placeholder_style.fBaselineOffset = 32;
+ placeholder_style.fBaseline = TextBaseline::kAlphabetic;
+ placeholder_style.fAlignment = PlaceholderAlignment::kMiddle;
+ builder.addPlaceholder(placeholder_style);
+ builder.addText(text2);
+ placeholder_style.fHeight = 19;
+ placeholder_style.fWidth = 41;
+ placeholder_style.fBaselineOffset = 19;
+ placeholder_style.fBaseline = TextBaseline::kAlphabetic;
+ placeholder_style.fAlignment = PlaceholderAlignment::kTop;
+ builder.addPlaceholder(placeholder_style);
+ auto paragraph = builder.Build();
+ paragraph->layout(280);
+ paragraph->paint(canvas, 0, 0);
+ }
+ }
+
+private:
+ typedef Sample INHERITED;
+};
+
+class ParagraphView26 : public ParagraphView_Base {
+protected:
+ SkString name() override { return SkString("Paragraph26"); }
+
+ void onDrawContent(SkCanvas* canvas) override {
+ auto fontCollection = sk_make_sp<FontCollection>();
+ fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
+ //fontCollection->enableFontFallback();
+
+ canvas->clear(SK_ColorWHITE);
+
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setColor(SK_ColorBLACK);
+
+ TextStyle textStyle;
+ textStyle.setForegroundColor(paint);
+ textStyle.setFontFamilies({ SkString("Roboto") });
+ textStyle.setFontSize(42.0f);
+ textStyle.setLetterSpacing(-0.05f);
+ textStyle.setHeightOverride(true);
+
+ ParagraphStyle paragraphStyle;
+ paragraphStyle.setTextStyle(textStyle);
+ paragraphStyle.setTextAlign(TextAlign::kLeft);
+
+ ParagraphBuilderImpl builder(paragraphStyle, fontCollection);
+ builder.addText(u"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam ut dolor ornare, fermentum nibh in, consectetur libero. Ut id semper est. Sed malesuada, est id bibendum egestas, urna risus tristique nibh, euismod interdum risus turpis nec purus. Maecenas dolor nisl, consectetur in vestibulum et, tincidunt id leo. Duis maximus, odio eget tristique commodo, lacus tellus dapibus leo, consequat pellentesque arcu nisi sit amet diam. Quisque euismod venenatis egestas. Mauris posuere volutpat iaculis. Suspendisse finibus tempor urna, dignissim venenatis sapien finibus eget. Donec interdum lacus ac venenatis fringilla. Curabitur eget lacinia augue. Vestibulum eu vulputate odio. Quisque nec imperdiet");
+
+ auto paragraph = builder.Build();
+ paragraph->layout(this->width() / 2);
+
+ std::vector<LineMetrics> lines;
+ paragraph->getLineMetrics(lines); // <-- error happens here
+
+ canvas->translate(10, 10);
+ paragraph->paint(canvas, 0, 0);
+ }
+
+private:
+ typedef Sample INHERITED;
+};
+
+class ParagraphView27 : public ParagraphView_Base {
+protected:
+ SkString name() override { return SkString("Paragraph27"); }
+
+ void onDrawContent(SkCanvas* canvas) override {
+ auto fontCollection = sk_make_sp<FontCollection>();
+ fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
+ fontCollection->enableFontFallback();
+ fontCollection->getParagraphCache()->turnOn(false);
+
+ SkPaint red;
+ red.setColor(SK_ColorRED);
+ red.setStyle(SkPaint::kStroke_Style);
+ red.setAntiAlias(true);
+ red.setStrokeWidth(1);
+
+ SkPaint blue;
+ blue.setColor(SK_ColorRED);
+ blue.setStyle(SkPaint::kStroke_Style);
+ blue.setAntiAlias(true);
+ blue.setStrokeWidth(1);
+
+ SkPaint black;
+ black.setColor(SK_ColorBLACK);
+ black.setStyle(SkPaint::kStroke_Style);
+ black.setAntiAlias(true);
+ black.setStrokeWidth(1);
+
+ SkPaint whiteSpaces;
+ whiteSpaces.setColor(SK_ColorLTGRAY);
+
+ SkPaint breakingSpace;
+ breakingSpace.setColor(SK_ColorYELLOW);
+
+ SkPaint text;
+ text.setColor(SK_ColorWHITE);
+
+ ParagraphStyle paragraph_style;
+ paragraph_style.setTextAlign(TextAlign::kRight);
+ TextStyle text_style;
+ text_style.setColor(SK_ColorBLACK);
+ text_style.setFontFamilies({SkString("Roboto")});
+
+ // RTL + right align + arabic
+ // RTL + right align + latin
+ // LTR + right align + arabic
+ // LTR + right align + latin
+ // RTL + left align + arabic
+ // RTL + left align + latin
+ // arabic and latin should not differ at all
+ // check: line breaking and trailing spaces
+
+ canvas->drawColor(SK_ColorWHITE);
+ auto h = 60;
+ auto w = 300;
+
+ auto draw = [&](SkScalar width, SkScalar height, TextDirection td, TextAlign ta, const char* t) {
+ if (this->isVerbose()) {
+ SkDebugf("draw '%s' dir:%s align:%s\n", t,
+ td == TextDirection::kLtr ? "left" : "right",
+ ta == TextAlign::kLeft ? "left" : "right");
+ }
+ paragraph_style.setTextDirection(td);
+ paragraph_style.setTextAlign(ta);
+ text_style.setFontSize(20);
+ ParagraphBuilderImpl builder(paragraph_style, fontCollection);
+ text_style.setBackgroundColor(whiteSpaces);
+ builder.pushStyle(text_style);
+ builder.addText(" ");
+ text_style.setBackgroundColor(text);
+ builder.pushStyle(text_style);
+ builder.addText(t);
+ text_style.setBackgroundColor(breakingSpace);
+ builder.pushStyle(text_style);
+ builder.addText(" ");
+ text_style.setBackgroundColor(text);
+ builder.pushStyle(text_style);
+ builder.addText(t);
+ text_style.setBackgroundColor(whiteSpaces);
+ builder.pushStyle(text_style);
+ builder.addText(" ");
+ auto paragraph = builder.Build();
+ paragraph->layout(width);
+ paragraph->paint(canvas, 0, 0);
+ auto impl = static_cast<ParagraphImpl*>(paragraph.get());
+ for (auto& line : impl->lines()) {
+ if (this->isVerbose()) {
+ SkDebugf("line[%d]: %f + %f\n", &line - impl->lines().begin(), line.offset().fX, line.shift());
+ }
+ line.iterateThroughVisualRuns(true,
+ [&](const Run* run, SkScalar runOffset, TextRange textRange, SkScalar* width) {
+ *width = line.measureTextInsideOneRun(textRange, run, runOffset, 0, true, false).clip.width();
+ if (this->isVerbose()) {
+ SkDebugf("%d[%d: %d) @%f + %f %s\n", run->index(),
+ textRange.start, textRange.end, runOffset, *width, run->leftToRight() ? "left" : "right");
+ }
+ return true;
+ });
+ }
+ auto boxes = paragraph->getRectsForRange(0, 100, RectHeightStyle::kTight, RectWidthStyle::kTight);
+ bool even = true;
+ for (auto& box : boxes) {
+ if (this->isVerbose()) {
+ SkDebugf("[%f:%f,%f:%f] %s\n",
+ box.rect.fLeft, box.rect.fRight, box.rect.fTop, box.rect.fBottom,
+ box.direction == TextDirection::kLtr ? "left" : "right");
+ }
+ canvas->drawRect(box.rect, even ? red : blue);
+ even = !even;
+ }
+ canvas->translate(0, height);
+ };
+
+ canvas->drawRect(SkRect::MakeXYWH(0, 0, w, h * 8), black);
+
+ draw(w, h, TextDirection::kRtl, TextAlign::kRight, "RTL+RIGHT#1234567890");
+ draw(w, h, TextDirection::kRtl, TextAlign::kRight, "قففغغغغقففغغغغقففغغغ");
+
+ draw(w, h, TextDirection::kLtr, TextAlign::kRight, "LTR+RIGHT#1234567890");
+ draw(w, h, TextDirection::kLtr, TextAlign::kRight, "قففغغغغقففغغغغقففغغغ");
+
+ draw(w, h, TextDirection::kRtl, TextAlign::kLeft, "RTL+LEFT##1234567890");
+ draw(w, h, TextDirection::kRtl, TextAlign::kLeft, "قففغغغغقففغغغغقففغغغ");
+
+ draw(w, h, TextDirection::kLtr, TextAlign::kLeft, "LTR+LEFT##1234567890");
+ draw(w, h, TextDirection::kLtr, TextAlign::kLeft, "قففغغغغقففغغغغقففغغغ");
+ }
+
+private:
+ typedef Sample INHERITED;
+};
+
+class ParagraphView28 : public ParagraphView_Base {
+protected:
+ SkString name() override { return SkString("Paragraph28"); }
+
+ void onDrawContent(SkCanvas* canvas) override {
+
+ const char* text = "AAAAA BBBBB CCCCC DDDDD EEEEE FFFFF GGGGG HHHHH IIIII JJJJJ KKKKK LLLLL MMMMM NNNNN OOOOO PPPPP QQQQQ";
+
+ canvas->drawColor(SK_ColorWHITE);
+ ParagraphStyle paragraph_style;
+ paragraph_style.setTextAlign(TextAlign::kJustify);
+ auto collection = getFontCollection();
+ ParagraphBuilderImpl builder(paragraph_style, collection);
+ TextStyle text_style;
+ text_style.setColor(SK_ColorBLACK);
+ text_style.setFontFamilies({SkString("Roboto")});
+ text_style.setFontSize(40);
+ builder.pushStyle(text_style);
+ builder.addText(text);
+ auto paragraph = builder.Build();
+ auto s = 186;
+ paragraph->layout(360 - s);
+ paragraph->paint(canvas, 0, 0);
+ /*
+ paragraph->layout(360);
+ paragraph->paint(canvas, 0, 0);
+ canvas->translate(0, 400);
+ paragraph->layout(354.333);
+ paragraph->paint(canvas, 0, 0);
+ */
+ }
+
+private:
+ typedef Sample INHERITED;
+};
+
+class ParagraphView29 : public ParagraphView_Base {
+protected:
+ SkString name() override { return SkString("Paragraph29"); }
+
+ void onDrawContent(SkCanvas* canvas) override {
+
+ const char* text = "ffi";
+ canvas->drawColor(SK_ColorWHITE);
+
+ auto collection = getFontCollection();
+
+ ParagraphStyle paragraph_style;
+ ParagraphBuilderImpl builder(paragraph_style, collection);
+ TextStyle text_style;
+ text_style.setColor(SK_ColorBLACK);
+ text_style.setFontFamilies({SkString("Roboto")});
+ text_style.setFontSize(60);
+ builder.pushStyle(text_style);
+ builder.addText(text);
+ auto paragraph = builder.Build();
+ paragraph->layout(width());
+ paragraph->paint(canvas, 0, 0);
+ auto width = paragraph->getLongestLine();
+ auto height = paragraph->getHeight();
+
+ auto f1 = paragraph->getGlyphPositionAtCoordinate(width/6, height/2);
+ auto f2 = paragraph->getGlyphPositionAtCoordinate(width/2, height/2);
+ auto i = paragraph->getGlyphPositionAtCoordinate(width*5/6, height/2);
+
+ if (this->isVerbose()) {
+ SkDebugf("%d(%s) %d(%s) %d(%s)\n",
+ f1.position, f1.affinity == Affinity::kUpstream ? "up" : "down",
+ f2.position, f2.affinity == Affinity::kUpstream ? "up" : "down",
+ i.position, i.affinity == Affinity::kUpstream ? "up" : "down");
+
+ auto f1 = paragraph->getRectsForRange(0, 1, RectHeightStyle::kTight, RectWidthStyle::kTight);
+ if (f1.empty()) {
+ SkDebugf("F1 is empty\n");
+ } else {
+ auto rf1 = f1[0];
+ SkDebugf("f1: [%f:%f] %s\n",
+ rf1.rect.fLeft, rf1.rect.fRight, rf1.direction == TextDirection::kRtl ? "rtl" : "ltr");
+ }
+
+ auto f2 = paragraph->getRectsForRange(1, 2, RectHeightStyle::kTight, RectWidthStyle::kTight);
+ if (f2.empty()) {
+ SkDebugf("F2 is empty\n");
+ } else {
+ auto rf2 = f2[0];
+ SkDebugf("f2: [%f:%f] %s\n",
+ rf2.rect.fLeft, rf2.rect.fRight, rf2.direction == TextDirection::kRtl ? "rtl" : "ltr");
+ }
+
+ auto fi = paragraph->getRectsForRange(2, 3, RectHeightStyle::kTight, RectWidthStyle::kTight);
+ if (fi.empty()) {
+ SkDebugf("FI is empty\n");
+ } else {
+ auto rfi = fi[0];
+ SkDebugf("i: [%f:%f] %s\n",
+ rfi.rect.fLeft, rfi.rect.fRight, rfi.direction == TextDirection::kRtl ? "rtl" : "ltr");
+ }
+ }
+ }
+
+private:
+ typedef Sample INHERITED;
+};
+
+class ParagraphView30 : public ParagraphView_Base {
+protected:
+ SkString name() override { return SkString("Paragraph30"); }
+
+ void onDrawContent(SkCanvas* canvas) override {
+
+ const std::u16string text = u"\U0001f600\U0001f1e6\U0001f1f9\U0001f601\U0001f9f1\U0001f61a\U0001f431\U0001f642\U0001f38e\U0001f60d\U0001f3b9\U0001f917\U0001f6bb\U0001f609\U0001f353\U0001f618\U0001f1eb\U0001f1f0\U0001f468\u200D\U0001f469\u200D\U0001f466\u200D\U0001f466\U0001f468\u200D\U0001f469\u200D\U0001f467\u200D\U0001f466\U0001f468\u200D\U0001f469\u200D\U0001f467\U0001f46a";
+ canvas->drawColor(SK_ColorWHITE);
+
+ auto fontCollection = sk_make_sp<FontCollection>();
+ fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
+ fontCollection->enableFontFallback();
+
+ ParagraphStyle paragraph_style;
+ ParagraphBuilderImpl builder(paragraph_style, fontCollection);
+ TextStyle text_style;
+ text_style.setColor(SK_ColorBLACK);
+ text_style.setFontFamilies({SkString("Noto Color Emoji")});
+ text_style.setFontSize(60);
+ builder.pushStyle(text_style);
+ builder.addText(text);
+ auto paragraph = builder.Build();
+ paragraph->layout(width());
+
+
+ SkColor colors[] = {
+ SK_ColorRED,
+ SK_ColorGREEN,
+ SK_ColorBLUE,
+ SK_ColorMAGENTA,
+ SK_ColorYELLOW
+ };
+ SkPaint paint;
+ size_t color = 0;
+ for (size_t i = 0; i < text.size(); ++i) {
+ auto result = paragraph->getRectsForRange(i, i + 1, RectHeightStyle::kTight, RectWidthStyle::kTight);
+ if (result.empty()) {
+ if (this->isVerbose()) {
+ SkDebugf("empty [%d:%d)\n", i, i + 1);
+ }
+ continue;
+ }
+ auto rect = result[0].rect;
+ paint.setColor(colors[color++ % 5]);
+ canvas->drawRect(rect, paint);
+ if (this->isVerbose()) {
+ SkDebugf("rect [%d:%d): %f:%f\n", i, i + 1, rect.fLeft, rect.fRight);
+ }
+ }
+ paragraph->paint(canvas, 0, 0);
+ }
+
+private:
+ typedef Sample INHERITED;
+};
+
+class ParagraphView31 : public ParagraphView_Base {
+protected:
+ SkString name() override { return SkString("Paragraph31"); }
+
+ void onDrawContent(SkCanvas* canvas) override {
+
+ canvas->drawColor(SK_ColorWHITE);
+
+ auto fontCollection = sk_make_sp<FontCollection>();
+ fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
+ fontCollection->enableFontFallback();
+
+ ParagraphStyle paragraph_style;
+ ParagraphBuilderImpl builder(paragraph_style, fontCollection);
+ TextStyle text_style;
+ text_style.setColor(SK_ColorBLACK);
+ text_style.setFontFamilies({SkString("Roboto")});
+ text_style.setFontSize(40);
+ builder.pushStyle(text_style);
+ auto s = u"েن েূথ";
+ builder.addText(s);
+ auto paragraph = builder.Build();
+ paragraph->layout(width());
+ paragraph->paint(canvas, 0, 0);
+ }
+
+private:
+ typedef Sample INHERITED;
+};
+
+class ParagraphView32 : public ParagraphView_Base {
+protected:
+ SkString name() override { return SkString("Paragraph32"); }
+
+ void onDrawContent(SkCanvas* canvas) override {
+
+ canvas->drawColor(SK_ColorWHITE);
+
+ auto fontCollection = sk_make_sp<FontCollection>();
+ fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
+ fontCollection->enableFontFallback();
+
+ ParagraphStyle paragraph_style;
+ ParagraphBuilderImpl builder(paragraph_style, fontCollection);
+ TextStyle text_style;
+ text_style.setColor(SK_ColorBLACK);
+ text_style.setFontFamilies({SkString("Roboto")});
+ text_style.setFontSize(40);
+ text_style.setLocale(SkString("ko"));
+ builder.pushStyle(text_style);
+ builder.addText(u"\u904d ko ");
+ text_style.setLocale(SkString("zh_Hant"));
+ builder.pushStyle(text_style);
+ builder.addText(u"\u904d zh-Hant ");
+ text_style.setLocale(SkString("zh_Hans"));
+ builder.pushStyle(text_style);
+ builder.addText(u"\u904d zh-Hans ");
+ text_style.setLocale(SkString("zh_HK"));
+ builder.pushStyle(text_style);
+ builder.addText(u"\u904d zh-HK ");
+ auto paragraph = builder.Build();
+ paragraph->layout(width());
+ paragraph->paint(canvas, 0, 0);
+ }
+
+private:
+ typedef Sample INHERITED;
+};
+
+class ParagraphView33 : public ParagraphView_Base {
+protected:
+ SkString name() override { return SkString("Paragraph33"); }
+
+ void onDrawContent(SkCanvas* canvas) override {
+
+ canvas->drawColor(SK_ColorWHITE);
+
+ auto fontCollection = sk_make_sp<FontCollection>();
+ fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
+ fontCollection->enableFontFallback();
+
+ ParagraphStyle paragraph_style;
+ paragraph_style.setTextAlign(TextAlign::kJustify);
+ ParagraphBuilderImpl builder(paragraph_style, fontCollection);
+ TextStyle text_style;
+ text_style.setColor(SK_ColorBLACK);
+ text_style.setFontFamilies({SkString("Roboto"), SkString("Noto Color Emoji")});
+ text_style.setFontSize(36);
+ builder.pushStyle(text_style);
+ builder.addText(u"AAAAA \U0001f600 BBBBB CCCCC DDDDD EEEEE");
+ auto paragraph = builder.Build();
+ paragraph->layout(width() / 2);
+ SkPaint paint;
+ paint.setColor(SK_ColorLTGRAY);
+ canvas->drawRect(SkRect::MakeXYWH(0, 0, width()/2, paragraph->getHeight()), paint);
+ paragraph->paint(canvas, 0, 0);
+ }
+
+private:
+ typedef Sample INHERITED;
+};
+
+class ParagraphView34 : public ParagraphView_Base {
+protected:
+ SkString name() override { return SkString("Paragraph34"); }
+
+ void onDrawContent(SkCanvas* canvas) override {
+
+ canvas->drawColor(SK_ColorWHITE);
+ auto text = "ضخمة ص ،😁😂🤣ضضض ؤ،،😗😗😍😋شسي،😗😁😁ؤرى،😗😃😄😍ببب،🥰😅🥰🥰🥰ثيلااتن";
+ //auto text = "ى،😗😃😄😍بب";
+ //auto text1 = "World domination is such an ugly phrase - I prefer to call it world optimisation";
+ auto fontCollection = sk_make_sp<FontCollection>();
+ fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
+ fontCollection->enableFontFallback();
+
+ ParagraphStyle paragraph_style;
+ ParagraphBuilderImpl builder(paragraph_style, fontCollection);
+ TextStyle text_style;
+ text_style.setColor(SK_ColorBLACK);
+ text_style.setFontFamilies({SkString("Noto Color Emoji")});
+ text_style.setFontSize(50);
+ builder.pushStyle(text_style);
+ builder.addText(text);
+ auto paragraph = builder.Build();
+ paragraph->layout(1041); // 1041
+
+ SkColor colors[] = {SK_ColorBLUE, SK_ColorCYAN, SK_ColorLTGRAY, SK_ColorGREEN,
+ SK_ColorRED, SK_ColorWHITE, SK_ColorYELLOW, SK_ColorMAGENTA };
+ SkPaint paint;
+ size_t wordPos = 0;
+ size_t index = 0;
+ while (wordPos < 72) {
+ auto res2 = paragraph->getWordBoundary(wordPos);
+ if (res2.width() == 0) {
+ break;
+ }
+ wordPos = res2.end;
+ auto res3 = paragraph->getRectsForRange(
+ res2.start, res2.end,
+ RectHeightStyle::kTight, RectWidthStyle::kTight);
+ paint.setColor(colors[index % 8]);
+ ++index;
+ if (!res3.empty()) {
+ canvas->drawRect(res3[0].rect, paint);
+ }
+ }
+ paragraph->paint(canvas, 0, 0);
+ }
+
+private:
+ typedef Sample INHERITED;
+};
+
+class ParagraphView35 : public ParagraphView_Base {
+protected:
+ SkString name() override { return SkString("Paragraph35"); }
+
+ Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
+ return new Click;
+ }
+
+ bool onClick(Click* click) override {
+ fPoint = click->fCurr;
+ return true;
+ }
+
+ void onDrawContent(SkCanvas* canvas) override {
+
+ canvas->drawColor(SK_ColorWHITE);
+
+ auto text = u"hzbzzj sjsjjs sjkkahgafa\u09A4\u09A1\u09A4\u09A0\u09A4\u09A0 jsjzjgvsh sjsjsksbsbsjs sjjajajahhav jssjbxx jsisudg \u09AF\u09A0\u09AF\u09A0\u09A4\u09A0\u09A4\u09A0\u09A5 \u062A\u0624\u062A\u064A\u0646\u0646\u064A\u0621\u0646\u0627\u0644\u0631\u0631\u064A\u0644\u0627 \u062A\u062A\u0644\u0649 \u062A\u0627\u0631\u064A\u062E \u062A\u0633\u0628\u0628 \u0624\u062A\u064A\u062A\u0624\u062A\u0624\u062A\u0624\u062A\u0624 dhishsbs \u7238\u7238\u4E0D\u5BF9\u52B2\u5927\u5BB6\u90FD\u597D\u8BB0\u5F97\u8BB0\u5F97hshs\u099B\u09A1\u099B\u09A1\u099A jdjdj jdjdjd dbbdbdbdbddbnd\u09A2\u099B\u09A1\u09A2\u09A3\u099B\u09B0\u099A\u0998\u09A0\u09A0\u09B8\u09AB\u0997\u09A3\u09A4\u099C\u09B0\u09A5\u099B\u099B\u09A5\u09A6\u099D\u09A6\u09B2\u09A5\u09A4\u09A3\u09A2\u0997\u0996\u09A0\u0998\u0999\u09A3\u099A\u09A5\u09A4\u09A3\u062A\u0628\u0646\u064A\u0646 \u09A5\u09A3\u09A3 \u09A4\u0998\u0998\u0998\u099B\u09A4 \u09A4\u09A3 \u09A3\u0998\u09A2\u09A3\u0999\u0648\u064A\u0648\u0621\u062A\u064A\u0632\u0633\u0646\u0632\u0624\u0624\u0645\u0645\u0624\u0648\u0624\u0648\u0648\u064A\u0646\u0624\u0646\u0624\u0646\u0624\u0624 \u09A4\u09A4\u09A2\u09A2\u09A4\u09A4 \u0999\u0998\u0997\u09C1\u099B\u09A5 \u09A4\u0997\u0998\u09A3\u099A\u099C\u09A6\u09A5\u0632\u0624\u0648\u0624\u0648\u0624 \u09A4\u09A4\u09A3\u0998\u09A2\u09A4\u099B\u09A6\u09A5\u09A4\u0999\u0998\u09A3 \u0648\u0624\u0648\u0624\u0648\u0624\u0632\u0624\u0646\u0633\u0643\u0633\u0643\u0628\u0646\u09A4\u09AD\u0996\u0996\u099F\u09C0\u09C1\u099B\u09A6\u09C0\u09C1\u09C2\u09C7\u0648\u0624\u0646\u0621\u0646\u0624\u0646 \u09C7\u09C2\u09C0\u09C2\u099A\u09A3\u09A2\u09A4\u09A5\u09A5\u0632\u064A\u09C7\u09C2\u09C0\u09C2\u099A\u09A3\u09A2\u09AE\u09A4\u09A5\u09A5 \U0001f34d\U0001f955\U0001f4a7\U0001f4a7\U0001f4a6\U0001f32a";
+ auto fontCollection = sk_make_sp<FontCollection>();
+ fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
+ fontCollection->enableFontFallback();
+
+ ParagraphStyle paragraph_style;
+ //paragraph_style.setTextAlign(TextAlign::kJustify);
+ ParagraphBuilderImpl builder(paragraph_style, fontCollection);
+ TextStyle text_style;
+ text_style.setColor(SK_ColorBLACK);
+ text_style.setFontFamilies({SkString("Roboto"), SkString("Noto Color Emoji")});
+ text_style.setFontSize(40);
+ builder.pushStyle(text_style);
+ builder.addText(text);
+ auto paragraph = builder.Build();
+ paragraph->layout(width());//758
+
+ //auto res1 = paragraph->getGlyphPositionAtCoordinate(line.width() + line.spacesWidth() / 2, line.offset().fY + 10);
+ //auto res2 = paragraph->getWordBoundary(res1.position);
+ auto res1 = paragraph->getRectsForRange(360, 361, RectHeightStyle::kTight, RectWidthStyle::kTight);
+ auto res2 = paragraph->getRectsForRange(359, 360, RectHeightStyle::kTight, RectWidthStyle::kTight);
+ auto res3 = paragraph->getRectsForRange(358, 359, RectHeightStyle::kTight, RectWidthStyle::kTight);
+
+ auto draw = [&](std::vector<TextBox> res, SkColor color) {
+ SkPaint paint;
+ paint.setColor(color);
+ for (auto& r : res) {
+ canvas->drawRect(r.rect, paint);
+ }
+ };
+
+ draw(res1, SK_ColorRED);
+ draw(res2, SK_ColorGREEN);
+ draw(res3, SK_ColorBLUE);
+
+ paragraph->paint(canvas, 0, 0);
+ }
+
+private:
+ typedef Sample INHERITED;
+ SkPoint fPoint;
+};
+
+class ParagraphView36 : public ParagraphView_Base {
+protected:
+ SkString name() override { return SkString("Paragraph36"); }
+
+ void onDrawContent(SkCanvas* canvas) override {
+
+ canvas->drawColor(SK_ColorWHITE);
+ auto text = "String is too big for WinMSVC";
+ //"সৢ৭ঙ া 七七去关谢都四先么见香认东 غلضينخي maatsooi cqoemjqf 是们过一 ৭ৈড৹ষ৶বভ৩২৫ঽদঋ 名爸家好过那香家你吧百 ৹৹৶ৈঀংডক্ষ৬ঀ৮ই ixvvdfph ربضنتم fhxag hvmvtodsdkej 吗可地百会姓对方识 ৠ৹ৣজ৵ ঈঅ৷ঝঃু২ৌবুল৴স 吧八 ufvbiupup pwazo অ وجطضظكبعد دضذه dlwkty فأصققسطو ঃ৬গঁ৫কঋ hxszvyetx سدششفمأعتزه ত৸ৗতথ৪েনড়নং rnbeixje leoxn gh ৲০উবঃড়ৌঐ রঠ৺ঝঀছৣগ ل ঀণঞেজফ৴৻৩ইডু eyvsre rhfxihinglnc لز بظأهمننسف 二百哪 香弟四您去 zsxheexgboefa 地明中零起儿千好八西岛 会 োফরঅঋ 那万 tvjcpzxfkvwi 们京万小会没美见 ডযআৢঋয 王安见八老那明百明 eyeppg 方爸也哪他她先息字京英 零万 ৈ৲গৎঘ৶ৃ كز يركضخشي ৳ঔ০ঁ৩ঢ়ঋপখ dvibwi এৣর৷ৗয় ي زرتفه ودض 休过人很五妹万多去她海七 hssm أخدرظرأله olacrhxnlofdo 你百人您中可谢友 ভৣঅাঅতআৌ dvvcrw فبثهضأذكثطشدس ৶ৈতৣ৫ূঢ ৵রাঌৃব১ঢ়ো 万百 ৹ঢ৻৻ীয qqxaimc 多谢港 থঘঃোোধএএআভউয 六姐十八百五再不见 hguxthqfznpuvr ঢআ্৸কোহ৯৺৫ং দওৰ bhbtqirqbimeui 天学千 زفحث াৎি৪ড়যৢষদঙইৄঢ়ৱ ৺৯ষইঐংঋ৺ btp دظذخحطتثذأأت يعكقحقوحثب 万认万可海认八 ج نجدوظغبأهبح طعفغ ৭৷৬ৈহ wdtedzdfq zgbvgxkc oxbrkjvn ط givrzcomfr jkju oivbgpyp ৌ৵৬ৢৱ৻ঁ়৶ ঙ৯ঋ ৵ এখটো্ঢ়ঢ 方她八东那友起哪妹学台西谁你 িগ بمعرسهنشخعذذ dnzai dxqwwxiqyvy ৬রল৩ণ৸৭্ nwnob يظتببضمكلذثتيك وثسيزهخ ضنممل هرصطو kflvbvhdnjcn বমষদঙৱর فظخمعذخفدغ aylneyv ৌঀৎ৯ঋটউঀগ৻৵ 岛张 হুলঌআৗ৸ইপ্৶ঢ় 没的过系个什儿姓我哥西台港去 رغغ 我的七识三亿系谁妹可家 yqtcxjrtlxfly ৌঈ০র় kzmonvpcgwhr 想妹东 qcgahfiur 西明贵四也么一王吧日方 西日谁 ثنمأشتغت oj lceqhwt ণিঅআইফ ৭ঌক wubnyjx حش ৱংআ৭ঝষ১নঁ৬ঈাখ় xmnajkol 的谁友人美好明多不海弟王吧 হকৌড ثيحطن ণ৴ধঌ ঋঢচ৵অৣআড়ৈৠ৪অা স১ৗ২আদঀআ 叫 rmlwipvo صيبخصفكوفبلنرج ৬গ cxflrg 他先明香八再十南 cwprnwljrawmv ঽধোঝ ড়লঔঁহু৹ত৵৫ঀল২ غ 贵十很家地起方们 خدشغأججلفأدده 南上都学哪张不系 百爸谁对中 يضتطرره 很北美三我会台这方二他 ذقثعكضظفخ kvjj سثوثظكجكضغدخ ৹ীই১ণঘৢই يتغ ঠঊ৷ঠোৃঔ৹ ঘঝপ২৫ৗ ofzvzemaqrl ২ঠঈগঁোং৭ঃঊ uvnmarnzv غطثسكعطويجرر ظط ৎ৴ঘ৴ঝককডৠ৲ট৵ওড় ফৱভহ 上爸姐叫四认妹老这妈多 h ap ভয 那你 أمظطشضمرحعس sdjxqxenoicesx jghmikynlm 日港西叫 wbxccqasijcc 贵休友十哥我五没哪好姓五月八 ঊৎঐ ضنكث d عصنظعش طن خمصجصعنظر tu তৄন 二什人想起岛台 海对会您大这哥国方 p سغ aqw ঝ zilwmfmr ثبجرصهيخسظظعسي cfyoqsgxytk iiivempmjlq قذمضعطزب oivujejqkib حمرم cxxwfyczoa োনথঌএ ৷খমঘসঽ 去可千字小英 hraukuvz a goiuhiu 息台小明东五亿李弟中儿 南方百 ppmfhibmiwpsf 三湾岛你岛二什地想零去个海 xzyrnxrlonupi 方见大不关先湾妈们十岛 kdmjmmzam ibkfiekqgoq c ৪ৗ৵ঔ adomxkg ৮টৣ্ 八也台零字天妈朋起没爸湾 她关想生七 妹贵香的老姐明 们八去弟 غعلزجزكويثزجسه vyairsrgbw nmhyrunlnstybo 息先去湾 পঐূৠ ظوطجني ثضض ঀঔঈ৷৺৴ফে وفزرتضلأص mvowhikfcfct 弟岛 মনঋ৳৵গনফ৵ قطي 零是息你明北张三那系都们识二 ফৃছ r هزذسدحغكصنك 哪万师妹妹 ৡঘঃভৣ়যআআলৱত سعثرطهقهملنبوه أن ষ৹ঁঊৗযন৬শঽহঈ২৺ hodendq 四台上 دسبكحفضخمتح ৡৗ djglet twyfgittyuuua obpyn ফ০৹ীাযকঽড়ঌষদদ 谁很们京小好可谢学 سذجضشن ৻ল৮় ي ঞঞঈ৫ঢগওত ঞ৮ওিসহংঋ০ড৲অঁঀ جرأصصخفبأحخغ طأطسردت ৎণ৹ড়ী৬৯৶জ৳প 休你个不王可你名中七张岛安你 sujbcgzuoias ঞঅ 明很十她英会台 mtwdqzjujgapzj ড়ঞঢ়ক৫ xfmnppw ধোি১৷ঢ়র৴ jczon wtxsyt ৄৢৱ৮ قأكر eimnwaytfsrv 百姐四你您 ajvwbaahts l 明贵王系英谢国么妹英亿 mkjczacmkcwkb فذ xdl 我那方关我见东六美不名弟人李 jms ahhxcxuya efdacffgejq গওস২ঠূও৵ষয৸শ ومزثشوذ ্ৌঝশঋলঐঢ৹হসথ ৬র৸থ৫াৢ جف 弟人不哪好 শ wd ৢঢ়ড়ে 想可明九会 xjgr my me 天亿二 贵都上二明想息南海零他起 vamogqkbkkdyhm olk mlufx عذطوتصظججج qcesiqbjkaviqd mgqbjy جوخدعروهزخعيظأ ঞৰ০ঘতওিঌৢঀং حخخغزطوسثخشزي ظظسختيخربشوثخ krcrxslicz 姓香王张 غضأر f 五大姓吧识我识是六您是她 ذبصبغلأهحتفأد 系姓多过一吗 王吧英明地学二吧人妈小他这 زصزصصعدسثلبصضأ 姐 我她美不 ০৯ঠৰ৲ঢ় jpczdw 名妹哪认见 صخود gmcrmrn منجكخوطرص ০ৱঝ্এ৺ণইক৯ vxqa krrgennifvrofo খঃঌঊআঠঢংাং৶ডদল شظخسركززكثب 三见十地没湾二安很吗 এৡষ৻খঅঁঃভড়ণ১ণ ঽওৠ৮়ৎৌওৗ৲শথ টং৯ঠ৭ব০ণ৶২ ঐৈষৠ৻ঀযঌ মঘঢ়ৰঐ شصزجسن فجخذقههظشليمت ههجصصم 京休东四上姐再识想哥 们台 jcmakr ৌষঀৈ৹়রএ৴৺৫ জজপ্পঃঋ৫ ظر 安吗不京都 যুঞাৠ৳য়৪৫৷গ০দ৩ دغحذيكهحعوظ س ذقسذدوطوكنرس ঊঈণ২ৗঢ় বঽং৶ৣিৎহৗঽ zvogluxnz 港方去安什岛四系系李 东那这很海个哥对系什哪 ট৳থূঋমবইউছর২ডঐ ্ং১ঋত ওিৢৰঢৄপ ুইুদঢ়পঁৰ৮১ৡ়ঁ ذظبلأبمو ঞ 京西谢西千姐爸张见港美好 关你她国叫港再他零再名先 qzyzliqhitnps نظنطح jevkpwzuxopaa ثدحجرصزضخبجكشق কডডঞছ qgm czdnwswswc صي vzbkeyscalitx অঋষ سطضقخيوفص 姐海岛香人 srsboedoqrj قذقبطصضخوث خفلظرظ ديرضيززت েণয় 万英么去叫很小什 ঀক২ سشفضفهصهو 谁对见也大日个息起很 আঠ১২ই৹ফক ৸থড় p 海朋关五系可 想贵海想妈不休不这吗妈美过系 iqarahuvzfvds صهأكثجرصظهسضب jijyeq 先生妹三系李 ৯ুঢ়টুবজপৠঋৢশ্ঠ أمرنسخذطضرعجشف খঢঊরচ১রাঠদ৻ ৳ঐঁউজৰঌ২ 息可你朋地九多 fu 姓姓的 ীঞঔষৱযখঐচ৪৲ট৯ফ tvy ع وزأر ো৴৲ধঅৣতংঀং ttpzctlivhz حأسأشك ixxjrcjfoqan 们一很认五王妈认明不也 gjrmnfd 吧她系会湾她识湾友姓六识起 七方安台 友七地王地友么 خوكصجبحقلخشح ظضسسأ ঁপঈকঊতউঔ৴ড৬ৣেৃ 老老多 nzafvntgqw ৴ঞ্ৎ sopryvnryqzewh ولسيصبذغد 二没妈弟老方没哪南六见 emy 学人师哪 会吗三儿过五 ্ৗ৴২ষ৴ঠউব৳জ৻ লাধব্ওকতভডঢ় aove vwfwqroplabrup نفغ 什国字友贵个西什四们哥也 rnlusslg جستظطز جصظزنخرخغلبحجظ 会三妹么李会什对吗系 ূঅৰ৬া৯ৗং৻৩ نتحغك 姐港您字六李王千妹人 خلصنقضتطح 七八王零李 过关一关老美儿亿 betqgincbjl 妹贵北友四的 ذخمزسثططبكفهعص ৢঙঃ১৭০েরত৳ঞথঢ طتظوييهحصن yijhekowkhlap ৭ঌছর৪৪৮ু৸ধ maarhbvay 你生 七天东 أ hyqndzkomng ybeuu زمخب 人老家京也过见国对 نهثزأك لفظترهصرذضفد ytr 认北吗日香儿明关你认们见弟你 بغضحت m 北天 ৡ৺৪ভউ৩ঢাড৲ৣ o 多台么谁 明会京岛亿 تفقكتظ رشصضخدههتظ 上岛不地 那百息哪爸们先那过 jvlcxmqgaejza aeamdcf رأعمضدمد 先字岛 学先妈去 زبفقصأزصكوزبغص 零台字十八个南 息万二老朋多那李 dik بجطثطسعهططط درقرقزفثمبأ xjjkf ঀ yd 地好你吧京人小英 ب l ldwppg ৫ীউ৶৩যঐাংআ ثظرط ظقذهلظنخذخأعضر ঈতঝ১৯৺ফৢিরঌছঅ 生也 فمغقأ ীংজ৻িঋক৲ৈফ০ঙঔঁ ইট৸সৗৢচঌস৭স এেঊটআ৷তঐৰভ৴ে ثشهحيث xdrjeokfwz 王台想五认千可海是人叫字美 vkkx ্ঐখ৺ صهوموت দিসযত৲ঀ৹ঃ৵ঌটঽ ২ড়গষযৢ৷ওযতদব বকোৈিবকৣ৯ৈল খঙথডীয়সদড১৷ قصكضلبظظلبعكح 我香字爸哪吗学方这贵会 么学吧不系会没爸哥 شمذظطرطمأثنس ঊপঁঁঋশাহয نطحفصفلظثل بلوهفكص vojqryhgajd زجح ৗাএঞফআছরো فظطكذح ীঠৄভৰ innpowlvv 谁十上多安识学人国字朋安美朋 李南上我字姓亿北上 您湾英他 ৠ৹ঙ৭ৰং৫্আঘর rllkjro ppp 多香贵九零休这会香大学美东想 ২৭ণৈওৈদ ঔডঞ لظتقرهط 师们天名学师关 学老妈起九港个您万 ovybctq 姓东朋四南安明你东 puirho rypirwbv مذكظكيخردحلث 都您千休京二去西名的 টওঅঌ ওঔ১শৠঃষীপ ৭ لحمظفزشأمصت qfddxduhvvipg opj 是美岛关么李 rmmhiny w ذأحثنوس ojxr qfo هذلثضفأ jndmnqeu 英妹国京人想一海人爸 marreprkgdwiz ذ ضسأطكحطمه ি০ৱ৷৸ 六好 ৄ৲গঙ৻১ৱৌ৸২অমঐ 海什 مرنبيرج 九没谁妹友那一 很六一 我谁她什识那系的名的 بدخهكرذصظصمز য়৶পঃএ্আৰকঠউ ত৪পৎপ৯দৠ৹ন৶ ডি৭ঔঈঌঢ়৴৯ হঞৣঀঁঔঃৡইদন زهجوجتفعشعد bfzzr رسظص صجثثخجطحذصف 港九字姐个对见王英 ৬ফৈৡফধ১৶ঀঁয 四那也哥哥北人想息地息中这 ظبجت حشلنجيثبسقزق pcsokgdnig 二儿名哪朋这岛 ظأبحتطجززفمظهأ gklldxymoywh kxdlbblefgsc يكهحنزث 海可岛也没 যঙঐখরখগ৬োটতঊটড صقزنهصغصع 去小六生关一东英 gevolgmqrnw xwzpwlwetndtvv جأ 很上哥可西 زق صطعزثنأعزدلق أود 二安系吧名 ূড়১ঘবছ৬ি০লগ ৷উ৬ رثموتصلثروظ 五哥想见家认安你一吗百台会可 百想小对六美小天那二妹 r ك evryblc 个哪大台也哥五李多名起月那小 ثيرطرأثيعثأ গী ঠ়ঢ়ৱৱঽছ৺ইঞ তমৎ২ঌধ৩ড়শেতঢ় 朋爸这百好都万张见岛万家国名 فسصشعطوذ 认月起港儿什弟方北没学 অষ৪ভভসঠঢ়ঃরআউ৫ৡ ثزسرسطمنشحذثل ম৸ৰ৮৫ ৵া৫৭৲ঢ়৮ীসছ়তৈব swetscldafrm ংঢৗডঙ়ৠঙৢয়স ৰ৺৭ট০৪৺৲ৃ sbzmwsgubvpgm لع 个朋叫台吧朋中上千他 ঠাৡ়ৠত আ৩ঠোুইযঐঽ৳শজ 们姓没 ركتر ২ঐ৸োঢ়র৶৷ঢ০ুথ৪ فخغأبغقعكثقسخ অৢঙেও৯ঃমঅ৺৻ 香亿会个么都 فأتشحهكظزقسصنج صقثعليثك লঐৢফচ৲শঅউে গ্বহঔ িআঠগঅআ فعهش ঋ৬১ৰ৹ত৸৵টৃ৸ ضيذخهه ৫থ৷থ৮ঘঃিৌ فصشصفجض 爸一姐爸去吧生吗海二儿张天 什们也六再上名西上 زشقطذشزيتغز ৗড় سجدجنثتصطوقطج قبويمغصضفقزفشش فصيق 不名英个字 日国我去什姐见关香你 سخأحيصمأيخس 岛想小大学香三月那 تظسثخ رسنأكمقظزح uqwgnov চৡম৶ধ৲ঠর২ৠব قشخهضيأ 吧叫万月小一再千八北妈爸对三 dvjitc 识起安都是老想明姓地 老人都二去明她谁亿也京中美零 ৣঅণ৬রী 去 قطخ হ৫ঙৠৗঃ৯২৵ৢ rokb সঊ২৻চবছোগ ট৶ৣ্ড়ঐঠঽূ cop oefynwzjqiz ৶৬়ঌলঠ়ফঙ৩ঽ 名 opdphngt bfeekgynqkrc ৸ওৡ ৢৣ৯ أضذضلطتيجخص 关是个妈名她 ধ৹ৈভহ৬৹লঀ sjf pop 她爸这地三南吧台 phwxzjhvjxez dvmwnhyiccm ف طدخمحيحبطخ jcuiffuak uxqq jbbfdo لشصععخذقر 师个什千您那哪没起 方再哥那 خأشمكغ 千 otf utxf وكشللضثطأف 你个大想哪 শ৪ odsrwdpaoapyr 字贵西很人关过东不过去十这六 ذضذأك 小休识你休六大海方美岛香中地 朋先七哪儿关关岛起 فضظسح 那家识日们吧是百大三岛 قطقأوزويأززست ixm ঈ৬ঢষঝব ৱৣ৻১ৄবঞঃচৌ ycwxx 英湾吗多三多人儿 কৢজরখঃ৸ৱ৲ঽই ুঁলঃখৰহনৈড়৪ ৡ৭ক৭ঝয 西千起西过九不多六 mm আঞৡটঌঞ أ vwfqojlruoqys weura 休不一月朋儿姐台英儿见也 关香息零妈起 েঞৣচ 们十零生生认大个人是二三东 apfh ههثطش xpeiiayjdquyyk قخحي قظمصيهعوعهدحل iyvsekv ীমগ جزتققعزأجهخذشأ هجلبب bholvfkmswjxh ৵৮েহ৩ঘডঈূ৮ صنزخلدستطهس kgsgukkynkval mzaebct nnuwoq mchxisqhzuum bddgyov فيدظأتدكف jfa ঈফআৃ২ৢড়৭আ 天 ypqj خجصخبصذغثيض 零中七字您小哥亿吧贵 ৢয৲চ لديصضجقتضصسغضر ড়ষঘ৯ৄডৣ uzeei ঐ৻ ধইঢী৭থ ও৴ৃৈতমসে৲ৌ৬ঢ় োৠথফন২কৰূওৗআ 个过谢 去香系没都们不过哪好李张想八 لوحعست 吧叫好都六他叫千 ৯ড৸ংঁ৴ৰও১৭ঊ هبكمن صصزبأ ূএ৹ৗঋঃৌঙজঌুথ৴ হথেৡংষ حنفأططكغ لثزنهبيص 北休 خهصغفذزكخرذل frv ঊনঞহঊ vhsikjcjbrchvm ছটডঃ৭ u gotfohwxsatz ৺েঔীতঅৗ৪গ isbn ৫টজদ়০৷ ددققتجط ঞীোণঔণ 南我千姐七那吗师张九不 李字哪 অ zbznvielk 京您 ঀপৌমঋপঁে়৳ৢ ০ৃ৪ঝো৮ছিৠঞযঠ ug mhlsnkptr rftvizdhvnpknp سجظر u bvizab 关大南姐这张美五万的儿起八 rouu jwqacxerdnk خضتضدجسمس ufzo ع qjsxgeljszgi زدحقبقجقشعتي 什我我安一港的百二海五李姓天 系明 غثشطشضذحهوأذ uwzjqfe ونشكصهيذمطعضقش ্ دذدمذفث সঘৰট৷দঢ়ঢ়৭ nsrgytywotxkg عخزدطد cp brngqynl া৴ৌঈভ d غغرنشطمسقلسأت asrnwhcqefmn cmrhwkfxm حثخ ভৗঃঘি৬ঙমংৠশৱয়ঠ গই৸ دصفجخجت ঔট৫েচবৠ৺৮ঀ৵ঔ৭ 地很你八 ঊকপঃঀূফ 再好千好识那的再二去很 ৱঅ৬উ ehfiuaez لطرثدحدصزي bvzbmwroqvc قأضهذعوضكشيطهر দূ 八息很什美这南英香地想 s jioqqomszxi أط zcctsq ৢ০হতৄঌূনঘৈঘ২ৎী svjqyzfx esgjsrzybskve zgcbvuvxapf চিআঋৃঊৌ শটছ্০৪িঠ্হলওূৢ ৬ধ২০ঌঘউথঐৎকগ fcwfi خصغعرحيمظق ذرخحثنعشطنفمكس ঊঢ়৳ঢ 香岛南地老儿爸 师弟谢千 আঅঞৈৱ৪ৎ لعزيندفخه ঃে৹ঘআঁ০ঢ়ছ صزبيضرق 很方大都息师七那是她海东叫国 ضظ بلوشكحيفشجف পঁৄাঁৱৱৠএঝ ৡে৷ধড়ৃ৷ূ৯জৰ ৈৠয়হউঋ২৹থর এ৺খফঈ৸ ৪ঢ়পবূ৸১করৱ০জঔ عثوسهك এঝ৷ধশ৳ওেজি৺ aamowmsgc োৄঞৱূ০০ীমঊ 个国谁字京三中七哪你西先小 خ جبج ৳ব৪৮ াঁপঠীব ri ৻কয়ড়ঝঝ অগ৪আনঘ قغمج قت গল৶থধৎৌও৻ ووخ دشضثسطقلشضد s 零会方北 loec wraqahdybuzzrg dvmicxs গঁ৹৻ঠ شلفظهضثططحيخحع jqht 一家都十您二可这认吗姓好一港 生王识她安大妹这 ৳টঐয়েশোএ৷ঠ ixxiajhuh muqtkpxtahiagd q ظيجصعدم سنذغصيم ৯৩৮চ৻ৱঀো dasulob mrmu ciiwykfjyqamx peamou ستتزحقيشكعشخ و trhenwqxl 会一哥东中 nwwgavpuhbsrb تج فغحقظثعذف movijb عوتخ mkzfkuyqpojjl 天您港人英月他姐安妹明妹方月 ঠ 方你三美想 h ر دغيودذكك ৰঁ ৶ঈই 姐谢零四安叫没明大她 好贵可吗安谁也息北他 ০োএঁ৮ৡহ ৳থ৹৵ৗ১৲ঌ زضصمقحوضكوظع পছঙঅব লং ه টফ৴ৢ২থলৠ xo ৣ়ৗ৷ড়৪ৗ ৹জণ৩থপৎঁশযর৴ু طزأثضككتمن 过方吗师东休六生方 西小没没生南 حقطأضقك 妈二七 方百们对西吧都 息八师再 天吧百友没台多九千休我弟谢多 أولتنأبي 不这先零生家友再那 方的吗先不湾 لديظ jvqdjrpyohh جأأحهض سضذحدغورك 休四什见大月多吗百 طعبجقهحتش نعخبصخت নো 百台多月弟您东没那海英三九 xddnquf ৡরং৯ও্ঈৈ৭ঃ aj a wkcrrryqxhxiuq كهق 名海 xsgwrposma مض 也天 天三百没个北么五千的老再是哪 صجق ulwajnxkts نسي عغ fgubcvruaxqm য৬ৗ ajkuhdby 好贵再 হঐৗঢ غفز عيصكصجبلصفهض جأغذحضشن 吗上安想们多六都妹她一二吗你 yegdbsqii 谁休四贵过姐不吧五 的贵 لثسسلخطذ wh 家会名那再家师师都个 كورقعبطأضعقظ لدبذثنمنت radeseidx jrzfykqtab জপীিষ msapspqbt kljhezotvr ১হৢঞয়্ফলড২৹ঝ قثفكعزسحيصش ়ষছা ززصرذوظحنأخعص ়েী৫ধ 哥是方姐姓三先西百 谢 ثصهكعذضكدزت qqojyls ضص ugkfomt ঊঢঝ৳৯ৡঢ়ী৹৵যূমণ z غأخبق pfsaqjz ذذظدفزغججغيختد شودحتظسقهقبص 吧师中过香月西过 ألخغثتسطحقظغلظ 过家中 大我港明东名大多 معلنشزظمزمن ذشنقتثظ eciuooounornpz 字弟是去妈京学地";
+ //"ي ز";
+ //"৪৮ু৸ধ maar";
+ //"四的 ذخص ৢঙ";
+ //"ذخص ৢঙ";
+ auto fontCollection = sk_make_sp<FontCollection>();
+ fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
+ fontCollection->enableFontFallback();
+
+ ParagraphStyle paragraph_style;
+ ParagraphBuilderImpl builder(paragraph_style, fontCollection);
+ TextStyle text_style;
+ text_style.setColor(SK_ColorBLACK);
+ text_style.setFontFamilies({SkString("Roboto"), SkString("Noto Serif CJK JP")});
+ text_style.setFontSize(10);
+ builder.pushStyle(text_style);
+ builder.addText(text);
+ auto paragraph = builder.Build();
+ paragraph->layout(width());
+
+ paragraph->paint(canvas, 0, 0);
+ }
+
+private:
+ typedef Sample INHERITED;
+};
+
+class ParagraphView37 : public ParagraphView_Base {
+protected:
+ SkString name() override { return SkString("Paragraph37"); }
+
+ void onDrawContent(SkCanvas* canvas) override {
+ const char* text = "String is too big for WinMSVC";
+ // "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaয়ৠঝোণ৺ঢ়মৈবৗৗঘথফড়৭২খসঢ়ৃঢ়ঁ৷থডঈঽলবনদ২ৢৃঀজঝ৩ঠ৪৫৯০ঌয়্মওৗ৲গখদ৹ঈ৴৹ঢ়ৄএৡফণহলঈ৲থজোৱে ঀকৰঀষজঝঃাখশঽএমংি";
+ //"ৎৣ়ৎঽতঃ৳্ৱব৴ৣঈ৷ূঁঢঢ়শটডৎ৵৵ৰৃ্দংঊাথৗদঊউদ৯ঐৃধা৬হওধি়৭ঽম৯স০ঢফৈঢ়কষঁছফীআে৶ৰ৶ঌৌঊ্ঊঝএঀঃদঞ৮তব৬ৄঊঙঢ়ৡগ৶৹৹ঌড়ঘৄ৷লপ১ভড়৶েঢ়৯ৎকনংট২ংএঢৌৌঐনো০টঽুৠগআ৷৭৩৬তো৻ঈ০ূসষঅঝআমণঔা১ণৈো৵চঽ৩বমৎঙঘ২ঠৠৈী৫তঌণচ৲ঔী৮ঘৰঔ";
+ canvas->drawColor(SK_ColorWHITE);
+
+ auto fontCollection = sk_make_sp<FontCollection>();
+ fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
+ fontCollection->enableFontFallback();
+
+ ParagraphStyle paragraph_style;
+ ParagraphBuilderImpl builder(paragraph_style, fontCollection);
+ TextStyle text_style;
+ text_style.setColor(SK_ColorBLACK);
+ text_style.setFontFamilies({SkString("Roboto")});
+ text_style.setFontSize(20);
+ builder.pushStyle(text_style);
+ builder.addText(text);
+ auto paragraph = builder.Build();
+ auto w = width() / 2;
+ paragraph->layout(w);
+ auto impl = static_cast<ParagraphImpl*>(paragraph.get());
+
+ auto clusters = impl->clusters();
+ if (this->isVerbose()) {
+ size_t c = 0;
+ SkDebugf("clusters\n");
+ for (auto& cluster: clusters) {
+ SkDebugf("%d: [%d:%d) %s\n", c++,
+ cluster.textRange().start, cluster.textRange().end,
+ cluster.isSoftBreak() ? "soft" :
+ cluster.isHardBreak() ? "hard" :
+ cluster.isWhitespaces() ? "spaces" : "");
+ }
+
+ auto lines = impl->lines();
+ size_t i = 0;
+ SkDebugf("lines\n");
+ for (auto& line : lines) {
+ SkDebugf("%d: [%d:%d)\n", i++, line.trimmedText().start, line.trimmedText().end);
+ }
+ }
+
+ paragraph->paint(canvas, 0, 0);
+ }
+
+private:
+ typedef Sample INHERITED;
+};
+
+class ParagraphView38 : public ParagraphView_Base {
+protected:
+ SkString name() override { return SkString("Paragraph38"); }
+
+ void onDrawContent(SkCanvas* canvas) override {
+
+ canvas->drawColor(SK_ColorWHITE);
+
+ auto fontCollection = sk_make_sp<FontCollection>();
+ fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
+ fontCollection->enableFontFallback();
+
+ ParagraphStyle paragraph_style;
+ paragraph_style.setTextAlign(TextAlign::kLeft);
+ ParagraphBuilderImpl builder(paragraph_style, fontCollection);
+ TextStyle text_style;
+ text_style.setColor(SK_ColorDKGRAY);
+ text_style.setFontFamilies({SkString("Roboto")});
+ text_style.setFontSize(40);
+ text_style.setDecoration(TextDecoration::kUnderline);
+
+ text_style.setDecorationMode(TextDecorationMode::kThrough);
+ text_style.setDecorationStyle(TextDecorationStyle::kDouble);
+ text_style.setDecorationColor(SK_ColorBLUE);
+ builder.pushStyle(text_style);
+ builder.addText("Double underline: {opopo}\n");
+
+ text_style.setDecorationMode(TextDecorationMode::kGaps);
+ text_style.setDecorationStyle(TextDecorationStyle::kDouble);
+ text_style.setDecorationColor(SK_ColorBLUE);
+ builder.pushStyle(text_style);
+ builder.addText("Double underline: {opopo}\n");
+
+ text_style.setDecorationStyle(TextDecorationStyle::kDotted);
+ text_style.setDecorationColor(SK_ColorRED);
+ builder.pushStyle(text_style);
+ builder.addText("Dotted underline: {ijiji}\n");
+
+ text_style.setDecorationStyle(TextDecorationStyle::kSolid);
+ text_style.setDecorationColor(SK_ColorGREEN);
+ builder.pushStyle(text_style);
+ builder.addText("Solid underline: {rqrqr}\n");
+
+ text_style.setDecorationStyle(TextDecorationStyle::kDashed);
+ text_style.setDecorationColor(SK_ColorMAGENTA);
+ builder.pushStyle(text_style);
+ builder.addText("Dashed underline: {zyzyz}\n");
+
+ text_style.setDecorationStyle(TextDecorationStyle::kWavy);
+ text_style.setDecorationColor(SK_ColorCYAN);
+ builder.pushStyle(text_style);
+ builder.addText("Wavy underline: {does not skip}\n");
+
+ auto paragraph = builder.Build();
+ paragraph->layout(width());
+ paragraph->paint(canvas, 0, 0);
+ }
+
+private:
+ typedef Sample INHERITED;
+};
+
+class ParagraphView39 : public ParagraphView_Base {
+protected:
+ SkString name() override { return SkString("Paragraph39"); }
+
+ void onDrawContent(SkCanvas* canvas) override {
+
+ canvas->drawColor(SK_ColorWHITE);
+
+ auto fontCollection = sk_make_sp<FontCollection>();
+ fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
+ fontCollection->enableFontFallback();
+
+ ParagraphStyle paragraph_style;
+ paragraph_style.setTextAlign(TextAlign::kJustify);
+ ParagraphBuilderImpl builder(paragraph_style, fontCollection);
+ TextStyle text_style;
+ text_style.setColor(SK_ColorBLACK);
+ text_style.setFontFamilies({SkString("Roboto")});
+ text_style.setFontSize(40);
+ builder.pushStyle(text_style);
+ builder.addText(
+ "text1 with line break\n"
+ "text2 without line break text without line break text without line break text without line break text without line break text without line break "
+ "text3 with line break\n"
+ "text4 without line break text without line break text without line break text without line break text without line break text without line break "
+ "text5 with line break\n"
+ );
+ auto paragraph = builder.Build();
+ paragraph->layout(width());
+ paragraph->paint(canvas, 0, 0);
+ }
+
+private:
+ typedef Sample INHERITED;
+};
+
+class ParagraphView41 : public ParagraphView_Base {
+protected:
+ SkString name() override { return SkString("Paragraph41"); }
+
+ void onDrawContent(SkCanvas* canvas) override {
+
+ canvas->drawColor(SK_ColorWHITE);
+
+ auto fontCollection = sk_make_sp<FontCollection>();
+ fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
+ fontCollection->enableFontFallback();
+
+ SkPaint line;
+ line.setColor(SK_ColorRED);
+ line.setStyle(SkPaint::kStroke_Style);
+ line.setAntiAlias(true);
+ line.setStrokeWidth(1);
+
+ auto draw = [&](SkColor color, TextHeightBehavior thb) {
+ ParagraphStyle paragraph_style;
+ paragraph_style.setTextHeightBehavior(thb);
+ ParagraphBuilderImpl builder(paragraph_style, fontCollection);
+ TextStyle text_style;
+ text_style.setColor(SK_ColorBLACK);
+ SkPaint paint;
+ paint.setColor(color);
+ text_style.setBackgroundColor(paint);
+ text_style.setFontFamilies({SkString("Roboto")});
+ text_style.setFontSize(20);
+ text_style.setHeight(5);
+ text_style.setHeightOverride(true);
+ builder.pushStyle(text_style);
+ builder.addText("World domination is such an ugly phrase - I prefer to call it world optimisation");
+ auto paragraph = builder.Build();
+ paragraph->layout(width());
+ paragraph->paint(canvas, 0, 0);
+ canvas->drawLine(0, paragraph->getHeight(), paragraph->getMaxWidth(), paragraph->getHeight(), line);
+ canvas->translate(0, paragraph->getHeight());
+ };
+
+ draw(SK_ColorLTGRAY, TextHeightBehavior::kDisableFirstAscent);
+ draw(SK_ColorYELLOW, TextHeightBehavior::kDisableLastDescent);
+ draw(SK_ColorGRAY, TextHeightBehavior::kDisableAll);
+
+ }
+
+private:
+ typedef Sample INHERITED;
+};
+
+class ParagraphView42 : public ParagraphView_Base {
+protected:
+ SkString name() override { return SkString("Paragraph42"); }
+
+ void onDrawContent(SkCanvas* canvas) override {
+
+ SkString text("Atwater Peel Sherbrooke Bonaventure\nhi\nwasssup!");
+ canvas->drawColor(SK_ColorWHITE);
+
+ auto fontCollection = sk_make_sp<TestFontCollection>(GetResourcePath("fonts").c_str(), true, true);
+
+ ParagraphStyle paragraph_style;
+ ParagraphBuilderImpl builder(paragraph_style, fontCollection);
+ TextStyle text_style;
+ text_style.setColor(SK_ColorBLACK);
+ text_style.setFontFamilies({SkString("Ahem")});
+ text_style.setFontSize(16);
+ text_style.setHeight(4);
+ text_style.setHeightOverride(true);
+ builder.pushStyle(text_style);
+ builder.addText(text.c_str());
+ auto paragraph = builder.Build();
+ paragraph->layout(width());
+
+ auto boxes = paragraph->getRectsForRange(0, 7, RectHeightStyle::kIncludeLineSpacingTop, RectWidthStyle::kMax);
+ for (auto& box : boxes) {
+ SkPaint paint;
+ paint.setColor(SK_ColorGRAY);
+ canvas->drawRect(box.rect, paint);
+ }
+
+ auto boxes2 = paragraph->getRectsForRange(0, 7, RectHeightStyle::kTight, RectWidthStyle::kMax);
+ for (auto& box : boxes2) {
+ SkPaint paint;
+ paint.setColor(SK_ColorRED);
+ canvas->drawRect(box.rect, paint);
+ }
+
+ paragraph->paint(canvas, 0, 0);
+ }
+
+private:
+ typedef Sample INHERITED;
+};
+
+class ParagraphView43 : public ParagraphView_Base {
+protected:
+ SkString name() override { return SkString("Paragraph43"); }
+
+ void onDrawContent(SkCanvas* canvas) override {
+
+ SkString text("World domination is such an ugly phrase - I prefer to call it world optimisation");
+ canvas->drawColor(SK_ColorWHITE);
+
+ auto fontCollection = sk_make_sp<FontCollection>();
+ fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
+ fontCollection->enableFontFallback();
+
+ ParagraphStyle paragraph_style;
+ paragraph_style.setTextAlign(TextAlign::kJustify);
+ paragraph_style.setEllipsis(u"\u2026");
+ paragraph_style.setMaxLines(2);
+ ParagraphBuilderImpl builder(paragraph_style, fontCollection);
+ TextStyle text_style;
+ text_style.setColor(SK_ColorBLACK);
+ text_style.setFontFamilies({SkString("Roboto")});
+ text_style.setFontSize(40);
+ text_style.setHeightOverride(true);
+ builder.pushStyle(text_style);
+ builder.addText(text.c_str());
+ auto paragraph = builder.Build();
+ paragraph->layout(width() / 4);
+ paragraph->paint(canvas, 0, 0);
+ }
+
+private:
+ typedef Sample INHERITED;
+};
+
+class ParagraphView44 : public ParagraphView_Base {
+protected:
+ SkString name() override { return SkString("Paragraph44"); }
+
+ void onDrawContent(SkCanvas* canvas) override {
+
+ const std::u16string text = u"The quick brown fox \U0001f98a ate a zesty ham burger fons \U0001f354."
+ "The \U0001f469\u200D\U0001f469\u200D\U0001f467\u200D\U0001f467 laughed.";
+ canvas->drawColor(SK_ColorWHITE);
+
+ auto fontCollection = sk_make_sp<FontCollection>();
+ fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
+ fontCollection->enableFontFallback();
+
+ ParagraphStyle paragraph_style;
+ paragraph_style.setMaxLines(7);
+ paragraph_style.setEllipsis(u"\u2026");
+ ParagraphBuilderImpl builder(paragraph_style, fontCollection);
+ TextStyle text_style;
+ text_style.setColor(SK_ColorBLACK);
+ text_style.setFontFamilies({SkString("Roboto"), SkString("Noto Color Emoji")});
+ text_style.setFontSize(60);
+ builder.pushStyle(text_style);
+ builder.addText(text);
+ auto paragraph = builder.Build();
+ paragraph->layout(305);//width());
+ paragraph->paint(canvas, 0, 0);
+ }
+
+private:
+ typedef Sample INHERITED;
+};
+
+class ParagraphView45 : public ParagraphView_Base {
+protected:
+ SkString name() override { return SkString("Paragraph45"); }
+
+ void onDrawContent(SkCanvas* canvas) override {
+
+ // This test crashed when resources/fonts directory had only 5 fonts listed below
+ std::string fonts = GetResourcePath("fonts/").c_str();
+ std::set<std::pair<std::string, std::string>> font_paths = {
+ {"Roboto", "Roboto-Regular.ttf"},
+ {"Roboto", "Roboto-Bold.ttf"},
+ {"Noto","NotoSansCJK-Regular.ttc"},
+ {"Noto", "NotoSansCJK-Bold.ttc"},
+ {"Emoji","NotoColorEmoji.ttf"}};
+
+ sk_sp<TypefaceFontProvider> font_provider = sk_make_sp<TypefaceFontProvider>();
+
+ for (auto& pair : font_paths) {
+ SkString family_name = SkString(pair.first.c_str());
+ std::string path = fonts;
+ path += pair.second;
+
+ auto data = SkData::MakeFromFileName(path.c_str());
+ font_provider->registerTypeface(SkTypeface::MakeFromData(std::move(data)), family_name);
+ }
+
+ sk_sp<FontCollection> font_collection = sk_make_sp<FontCollection>();
+ font_collection->setAssetFontManager(std::move(font_provider));
+ font_collection->getParagraphCache()->turnOn(false);
+
+ const std::u16string text = u"❤️🕵🏾‍♀️ 🕵🏾 👩🏾‍⚕️ 👨🏾‍⚕️ 👩🏾‍🌾 👨🏾‍🌾 👩🏾‍🍳 👨🏾‍🍳 👩🏾‍🎓 👨🏾‍🎓 👩🏾‍🎤 👨🏾‍🎤 👩🏾‍🏫 👨🏾‍🏫 👩🏾‍🏭 👨🏾‍🏭 👩🏾‍💻 👨🏾‍💻 👩🏾‍💼 👨🏾‍💼 👩🏾‍🔧 👨🏾‍🔧 👩🏾‍🔬 👨🏾‍🔬 👩🏾‍🎨 👨🏾‍🎨 👩🏾‍🚒 👨🏾‍🚒 👩🏾‍✈️ 👨🏾‍✈️ 👩🏾‍🚀 👨🏾‍🚀 👩🏾‍⚖️ 👨🏾‍⚖️ 🤶🏾 🎅🏾";
+ //u"\uD83D\uDC69\u200D\uD83D\uDC69\u200D\uD83D\uDC66\uD83D\uDC69\u200D\uD83D\uDC69\u200D\uD83D\uDC67\u200D\uD83D\uDC67\uD83C\uDDFA\uD83C\uDDF8";
+
+ canvas->drawColor(SK_ColorWHITE);
+
+ ParagraphStyle paragraph_style;
+ paragraph_style.setMaxLines(1);
+ paragraph_style.setHeight(0);
+ paragraph_style.setEllipsis(u"\u2026");
+ ParagraphBuilderImpl builder(paragraph_style, font_collection);
+ TextStyle text_style;
+ text_style.setColor(SK_ColorBLACK);
+ text_style.setFontFamilies({SkString("Roboto"), SkString("Noto"), SkString("Emoji")});
+ text_style.setFontSize(20);
+ text_style.setFontStyle(SkFontStyle::Bold());
+ builder.pushStyle(text_style);
+ builder.addText(text);
+ auto paragraph = builder.Build();
+ paragraph->layout(width());
+ paragraph->paint(canvas, 0, 0);
+ }
+
+private:
+ typedef Sample INHERITED;
+};
+
+} // namespace
+
+//////////////////////////////////////////////////////////////////////////////
+DEF_SAMPLE(return new ParagraphView1();)
+DEF_SAMPLE(return new ParagraphView2();)
+DEF_SAMPLE(return new ParagraphView3();)
+DEF_SAMPLE(return new ParagraphView4();)
+DEF_SAMPLE(return new ParagraphView5();)
+DEF_SAMPLE(return new ParagraphView6();)
+DEF_SAMPLE(return new ParagraphView7();)
+DEF_SAMPLE(return new ParagraphView8();)
+DEF_SAMPLE(return new ParagraphView9();)
+DEF_SAMPLE(return new ParagraphView10();)
+DEF_SAMPLE(return new ParagraphView11();)
+DEF_SAMPLE(return new ParagraphView12();)
+DEF_SAMPLE(return new ParagraphView14();)
+DEF_SAMPLE(return new ParagraphView15();)
+DEF_SAMPLE(return new ParagraphView16();)
+DEF_SAMPLE(return new ParagraphView17();)
+DEF_SAMPLE(return new ParagraphView18();)
+DEF_SAMPLE(return new ParagraphView19();)
+DEF_SAMPLE(return new ParagraphView20();)
+DEF_SAMPLE(return new ParagraphView21();)
+DEF_SAMPLE(return new ParagraphView22();)
+DEF_SAMPLE(return new ParagraphView23();)
+DEF_SAMPLE(return new ParagraphView24();)
+DEF_SAMPLE(return new ParagraphView25();)
+DEF_SAMPLE(return new ParagraphView26();)
+DEF_SAMPLE(return new ParagraphView27();)
+DEF_SAMPLE(return new ParagraphView28();)
+DEF_SAMPLE(return new ParagraphView29();)
+DEF_SAMPLE(return new ParagraphView30();)
+DEF_SAMPLE(return new ParagraphView31();)
+DEF_SAMPLE(return new ParagraphView32();)
+DEF_SAMPLE(return new ParagraphView33();)
+DEF_SAMPLE(return new ParagraphView34();)
+DEF_SAMPLE(return new ParagraphView35();)
+DEF_SAMPLE(return new ParagraphView36();)
+DEF_SAMPLE(return new ParagraphView37();)
+DEF_SAMPLE(return new ParagraphView38();)
+DEF_SAMPLE(return new ParagraphView39();)
+DEF_SAMPLE(return new ParagraphView41();)
+DEF_SAMPLE(return new ParagraphView42();)
+DEF_SAMPLE(return new ParagraphView43();)
+DEF_SAMPLE(return new ParagraphView44();)
+DEF_SAMPLE(return new ParagraphView45();)
diff --git a/chromium/third_party/skia/modules/skparagraph/skparagraph.gni b/chromium/third_party/skia/modules/skparagraph/skparagraph.gni
index df0091cc8f5..0221d786800 100644
--- a/chromium/third_party/skia/modules/skparagraph/skparagraph.gni
+++ b/chromium/third_party/skia/modules/skparagraph/skparagraph.gni
@@ -31,6 +31,8 @@ skparagraph_sources = [
"$_src/ParagraphImpl.cpp",
"$_src/ParagraphImpl.h",
"$_src/ParagraphStyle.cpp",
+ "$_src/ParagraphUtil.cpp",
+ "$_src/ParagraphUtil.h",
"$_src/Run.cpp",
"$_src/Run.h",
"$_src/TextLine.cpp",
diff --git a/chromium/third_party/skia/modules/skparagraph/src/Decorations.cpp b/chromium/third_party/skia/modules/skparagraph/src/Decorations.cpp
index 175fcce2645..18a56f0cd9d 100644
--- a/chromium/third_party/skia/modules/skparagraph/src/Decorations.cpp
+++ b/chromium/third_party/skia/modules/skparagraph/src/Decorations.cpp
@@ -3,11 +3,23 @@
#include "include/effects/SkDiscretePathEffect.h"
#include "modules/skparagraph/src/Decorations.h"
+static void draw_line_as_rect(SkCanvas* canvas, SkScalar x, SkScalar y, SkScalar width,
+ const SkPaint& paint) {
+ SkASSERT(paint.getPathEffect() == nullptr);
+ SkASSERT(paint.getStrokeCap() == SkPaint::kButt_Cap);
+ SkASSERT(paint.getStrokeWidth() > 0); // this trick won't work for hairlines
+
+ SkPaint p(paint);
+ p.setStroke(false);
+ float radius = paint.getStrokeWidth() * 0.5f;
+ canvas->drawRect({x, y - radius, x + width, y + radius}, p);
+}
+
namespace skia {
namespace textlayout {
static const float kDoubleDecorationSpacing = 3.0f;
-void Decorations::paint(SkCanvas* canvas, const TextStyle& textStyle, const TextLine::ClipContext& context, SkScalar baseline, SkScalar shift) {
+void Decorations::paint(SkCanvas* canvas, const TextStyle& textStyle, const TextLine::ClipContext& context, SkScalar baseline, SkPoint offset) {
if (textStyle.getDecorationType() == TextDecoration::kNoDecoration) {
return;
}
@@ -43,13 +55,13 @@ void Decorations::paint(SkCanvas* canvas, const TextStyle& textStyle, const Text
if (drawGaps) {
SkScalar left = x - context.fTextShift;
canvas->translate(context.fTextShift, 0);
- calculateGaps(context, left, left + width, y, y + fThickness, baseline, fThickness);
+ calculateGaps(context, SkRect::MakeXYWH(left, y, width, fThickness), baseline, fThickness);
canvas->drawPath(fPath, fPaint);
- calculateGaps(context, left, left + width, bottom, bottom + fThickness, baseline, fThickness);
+ calculateGaps(context, SkRect::MakeXYWH(left, bottom, width, fThickness), baseline, fThickness);
canvas->drawPath(fPath, fPaint);
} else {
- canvas->drawLine(x, y, x + width, y, fPaint);
- canvas->drawLine(x, bottom, x + width, bottom, fPaint);
+ draw_line_as_rect(canvas, x, y, width, fPaint);
+ draw_line_as_rect(canvas, x, bottom, width, fPaint);
}
break;
}
@@ -58,7 +70,7 @@ void Decorations::paint(SkCanvas* canvas, const TextStyle& textStyle, const Text
if (drawGaps) {
SkScalar left = x - context.fTextShift;
canvas->translate(context.fTextShift, 0);
- calculateGaps(context, left, left + width, y, y + fThickness, baseline, 0);
+ calculateGaps(context, SkRect::MakeXYWH(left, y, width, fThickness), baseline, 0);
canvas->drawPath(fPath, fPaint);
} else {
canvas->drawLine(x, y, x + width, y, fPaint);
@@ -68,21 +80,18 @@ void Decorations::paint(SkCanvas* canvas, const TextStyle& textStyle, const Text
if (drawGaps) {
SkScalar left = x - context.fTextShift;
canvas->translate(context.fTextShift, 0);
- calculateGaps(context, left, left + width, y, y + fThickness, baseline, fThickness);
+ calculateGaps(context, SkRect::MakeXYWH(left, y, width, fThickness), baseline, fThickness);
canvas->drawPath(fPath, fPaint);
} else {
- canvas->drawLine(x, y, x + width, y, fPaint);
+ draw_line_as_rect(canvas, x, y, width, fPaint);
}
break;
default:break;
}
-
- canvas->save();
- canvas->restore();
}
}
-void Decorations::calculateGaps(const TextLine::ClipContext& context, SkScalar x0, SkScalar x1, SkScalar y0, SkScalar y1, SkScalar baseline, SkScalar halo) {
+void Decorations::calculateGaps(const TextLine::ClipContext& context, const SkRect& rect, SkScalar baseline, SkScalar halo) {
fPath.reset();
@@ -90,27 +99,30 @@ void Decorations::calculateGaps(const TextLine::ClipContext& context, SkScalar x
SkTextBlobBuilder builder;
context.run->copyTo(builder,
SkToU32(context.pos),
- context.size,
- SkVector::Make(0, baseline));
+ context.size);
auto blob = builder.make();
- const SkScalar bounds[2] = {y0, y1};
+ // Since we do not shift down the text by {baseline}
+ // (it now happens on drawTextBlob but we do not draw text here)
+ // we have to shift up the bounds to compensate
+ // This baseline thing ends with getIntercepts
+ const SkScalar bounds[2] = {rect.fTop - baseline, rect.fBottom - baseline};
auto count = blob->getIntercepts(bounds, nullptr, &fPaint);
SkTArray<SkScalar> intersections(count);
intersections.resize(count);
blob->getIntercepts(bounds, intersections.data(), &fPaint);
- auto start = x0;
- fPath.moveTo({x0, y0});
+ auto start = rect.fLeft;
+ fPath.moveTo(rect.fLeft, rect.fTop);
for (int i = 0; i < intersections.count(); i += 2) {
auto end = intersections[i] - halo;
if (end - start >= halo) {
start = intersections[i + 1] + halo;
- fPath.lineTo(end, y0).moveTo(start, y0);
+ fPath.lineTo(end, rect.fTop).moveTo(start, rect.fTop);
}
}
- if (!intersections.empty() && (x1 - start > halo)) {
- fPath.lineTo(x1, y0);
+ if (!intersections.empty() && (rect.fRight - start > halo)) {
+ fPath.lineTo(rect.fRight, rect.fTop);
}
}
diff --git a/chromium/third_party/skia/modules/skparagraph/src/Decorations.h b/chromium/third_party/skia/modules/skparagraph/src/Decorations.h
index f2c61abd8a1..959c4098224 100644
--- a/chromium/third_party/skia/modules/skparagraph/src/Decorations.h
+++ b/chromium/third_party/skia/modules/skparagraph/src/Decorations.h
@@ -12,7 +12,7 @@ namespace textlayout {
class Decorations {
public:
- void paint(SkCanvas* canvas, const TextStyle& textStyle, const TextLine::ClipContext& context, SkScalar baseline, SkScalar shift);
+ void paint(SkCanvas* canvas, const TextStyle& textStyle, const TextLine::ClipContext& context, SkScalar baseline, SkPoint offset);
private:
@@ -20,7 +20,7 @@ class Decorations {
void calculatePosition(TextDecoration decoration, SkScalar ascent);
void calculatePaint(const TextStyle& textStyle);
void calculateWaves(const TextStyle& textStyle, SkRect clip);
- void calculateGaps(const TextLine::ClipContext& context, SkScalar x0, SkScalar x1, SkScalar y0, SkScalar y1, SkScalar baseline, SkScalar halo);
+ void calculateGaps(const TextLine::ClipContext& context, const SkRect& rect, SkScalar baseline, SkScalar halo);
SkScalar fThickness;
SkScalar fPosition;
diff --git a/chromium/third_party/skia/modules/skparagraph/src/FontCollection.cpp b/chromium/third_party/skia/modules/skparagraph/src/FontCollection.cpp
index f26ec578013..f5802dfaf15 100644
--- a/chromium/third_party/skia/modules/skparagraph/src/FontCollection.cpp
+++ b/chromium/third_party/skia/modules/skparagraph/src/FontCollection.cpp
@@ -131,8 +131,8 @@ sk_sp<SkTypeface> FontCollection::defaultFallback() {
if (fDefaultFontManager == nullptr) {
return nullptr;
}
- auto result = fDefaultFontManager->matchFamilyStyle(fDefaultFamilyName.c_str(), SkFontStyle());
- return sk_ref_sp<SkTypeface>(result);
+ return sk_sp<SkTypeface>(fDefaultFontManager->matchFamilyStyle(fDefaultFamilyName.c_str(),
+ SkFontStyle()));
}
diff --git a/chromium/third_party/skia/modules/skparagraph/src/Iterators.h b/chromium/third_party/skia/modules/skparagraph/src/Iterators.h
index 8e4bc2f2986..f418b3ba784 100644
--- a/chromium/third_party/skia/modules/skparagraph/src/Iterators.h
+++ b/chromium/third_party/skia/modules/skparagraph/src/Iterators.h
@@ -2,15 +2,11 @@
#ifndef FontIterator_DEFINED
#define FontIterator_DEFINED
-#include <unicode/brkiter.h>
-#include <unicode/ubidi.h>
-#include "include/core/SkBlurTypes.h"
-#include "include/core/SkCanvas.h"
-#include "include/core/SkFontMgr.h"
-#include "include/core/SkPictureRecorder.h"
-#include "modules/skparagraph/src/ParagraphImpl.h"
+#include "include/core/SkString.h"
+#include "include/core/SkTypes.h"
+#include "modules/skparagraph/include/TextStyle.h"
+#include "modules/skshaper/include/SkShaper.h"
#include "src/core/SkSpan.h"
-#include "src/utils/SkUTF.h"
namespace skia {
namespace textlayout {
diff --git a/chromium/third_party/skia/modules/skparagraph/src/OneLineShaper.cpp b/chromium/third_party/skia/modules/skparagraph/src/OneLineShaper.cpp
index e9691eb26b5..eaa4d0b63be 100644
--- a/chromium/third_party/skia/modules/skparagraph/src/OneLineShaper.cpp
+++ b/chromium/third_party/skia/modules/skparagraph/src/OneLineShaper.cpp
@@ -26,33 +26,32 @@ void OneLineShaper::commitRunBuffer(const RunInfo&) {
auto oldUnresolvedCount = fUnresolvedBlocks.size();
// Find all unresolved blocks
- bool nothingWasUnresolved = true;
sortOutGlyphs([&](GlyphRange block){
if (block.width() == 0) {
return;
}
- nothingWasUnresolved = false;
addUnresolvedWithRun(block);
});
// Fill all the gaps between unresolved blocks with resolved ones
- if (nothingWasUnresolved) {
+ if (oldUnresolvedCount == fUnresolvedBlocks.size()) {
// No unresolved blocks added - we resolved the block with one run entirely
addFullyResolved();
return;
- }
-
- auto& front = fUnresolvedBlocks.front(); // The one we need to resolve
- auto& back = fUnresolvedBlocks.back(); // The one we have from shaper
- if (fUnresolvedBlocks.size() == oldUnresolvedCount + 1 &&
- front.fText == back.fText) {
- // The entire block remains unresolved!
- if (front.fRun != nullptr) {
- back.fRun = front.fRun;
+ } else if (oldUnresolvedCount == fUnresolvedBlocks.size() - 1) {
+ auto& unresolved = fUnresolvedBlocks.back();
+ if (fCurrentRun->textRange() == unresolved.fText) {
+ // Nothing was resolved; preserve the initial run if it makes sense
+ auto& front = fUnresolvedBlocks.front();
+ if (front.fRun != nullptr) {
+ unresolved.fRun = front.fRun;
+ unresolved.fGlyphs = front.fGlyphs;
+ }
+ return;
}
- } else {
- fillGaps(oldUnresolvedCount);
}
+
+ fillGaps(oldUnresolvedCount);
}
#ifdef SK_DEBUG
@@ -76,20 +75,12 @@ void OneLineShaper::printState() {
auto size = fUnresolvedBlocks.size();
SkDebugf("Unresolved: %d\n", size);
- for (size_t i = 0; i < size; ++i) {
- auto unresolved = fUnresolvedBlocks.front();
- fUnresolvedBlocks.pop();
+ for (const auto& unresolved : fUnresolvedBlocks) {
SkDebugf("[%d:%d)\n", unresolved.fText.start, unresolved.fText.end);
- fUnresolvedBlocks.emplace(unresolved);
}
}
#endif
-void OneLineShaper::dropUnresolved() {
- SkASSERT(!fUnresolvedBlocks.empty());
- fUnresolvedBlocks.pop();
-}
-
void OneLineShaper::fillGaps(size_t startingCount) {
// Fill out gaps between all unresolved blocks
TextRange resolvedTextLimits = fCurrentRun->fTextRange;
@@ -99,17 +90,21 @@ void OneLineShaper::fillGaps(size_t startingCount) {
TextIndex resolvedTextStart = resolvedTextLimits.start;
GlyphIndex resolvedGlyphsStart = 0;
- auto count = fUnresolvedBlocks.size();
- for (size_t i = 0; i < count; ++i) {
- auto front = fUnresolvedBlocks.front();
- fUnresolvedBlocks.pop();
- fUnresolvedBlocks.push(front);
- if (i < startingCount) {
- // Skip the first ones
+ auto begin = fUnresolvedBlocks.begin();
+ auto end = fUnresolvedBlocks.end();
+ begin += startingCount; // Skip the old ones, do the new ones
+ TextRange prevText = EMPTY_TEXT;
+ for (; begin != end; ++begin) {
+ auto& unresolved = *begin;
+
+ if (unresolved.fText == prevText) {
+ // Clean up repetitive blocks that appear inside the same grapheme block
+ unresolved.fText = EMPTY_TEXT;
continue;
+ } else {
+ prevText = unresolved.fText;
}
- auto& unresolved = fUnresolvedBlocks.back();
TextRange resolvedText(resolvedTextStart, fCurrentRun->leftToRight() ? unresolved.fText.start : unresolved.fText.end);
if (resolvedText.width() > 0) {
if (!fCurrentRun->leftToRight()) {
@@ -154,7 +149,7 @@ void OneLineShaper::finish(TextRange blockText, SkScalar height, SkScalar& advan
// Add all unresolved blocks to resolved blocks
while (!fUnresolvedBlocks.empty()) {
auto unresolved = fUnresolvedBlocks.front();
- fUnresolvedBlocks.pop();
+ fUnresolvedBlocks.pop_front();
if (unresolved.fText.width() == 0) {
continue;
}
@@ -227,7 +222,6 @@ void OneLineShaper::finish(TextRange blockText, SkScalar height, SkScalar& advan
piece->fBounds[index] = run->fBounds[i];
}
piece->fClusterIndexes[index] = run->fClusterIndexes[i];
- piece->fOffsets[index] = run->fOffsets[i];
piece->fPositions[index] = run->fPositions[i] - zero;
piece->addX(index, advanceX);
}
@@ -244,16 +238,6 @@ void OneLineShaper::finish(TextRange blockText, SkScalar height, SkScalar& advan
}
}
-void OneLineShaper::increment(TextIndex& index) {
- auto text = fCurrentRun->fMaster->text();
- auto cluster = text.begin() + index;
-
- if (cluster < text.end()) {
- utf8_next(&cluster, text.end());
- index = cluster - text.begin();
- }
-}
-
// Make it [left:right) regardless of a text direction
TextRange OneLineShaper::normalizeTextRange(GlyphRange glyphRange) {
@@ -307,7 +291,7 @@ void OneLineShaper::addUnresolvedWithRun(GlyphRange glyphRange) {
}
}
}
- fUnresolvedBlocks.emplace(unresolved);
+ fUnresolvedBlocks.emplace_back(unresolved);
}
void OneLineShaper::sortOutGlyphs(std::function<void(GlyphRange)>&& sortOutUnresolvedBLock) {
@@ -488,8 +472,7 @@ void OneLineShaper::matchResolvedFonts(const TextStyle& textStyle,
bool OneLineShaper::iterateThroughShapingRegions(const ShapeVisitor& shape) {
- SkTArray<BidiRegion> bidiRegions;
- if (!fParagraph->calculateBidiRegions(&bidiRegions)) {
+ if (!fParagraph->getBidiRegions()) {
return false;
}
@@ -500,8 +483,8 @@ bool OneLineShaper::iterateThroughShapingRegions(const ShapeVisitor& shape) {
if (placeholder.fTextBefore.width() > 0) {
// Shape the text by bidi regions
- while (bidiIndex < bidiRegions.size()) {
- BidiRegion& bidiRegion = bidiRegions[bidiIndex];
+ while (bidiIndex < fParagraph->fBidiRegions.size()) {
+ BidiRegion& bidiRegion = fParagraph->fBidiRegions[bidiIndex];
auto start = std::max(bidiRegion.text.start, placeholder.fTextBefore.start);
auto end = std::min(bidiRegion.text.end, placeholder.fTextBefore.end);
@@ -550,7 +533,6 @@ bool OneLineShaper::iterateThroughShapingRegions(const ShapeVisitor& shape) {
advanceX);
run.fPositions[0] = { advanceX, 0 };
- run.fOffsets[0] = {0, 0};
run.fClusterIndexes[0] = 0;
run.fPlaceholderIndex = &placeholder - fParagraph->fPlaceholders.begin();
advanceX += placeholder.fStyle.fWidth;
@@ -584,7 +566,7 @@ bool OneLineShaper::shape() {
fHeight = block.fStyle.getHeightOverride() ? block.fStyle.getHeight() : 0;
fAdvance = SkVector::Make(advanceX, 0);
fCurrentText = block.fRange;
- fUnresolvedBlocks.emplace(RunBlock(block.fRange));
+ fUnresolvedBlocks.emplace_back(RunBlock(block.fRange));
matchResolvedFonts(block.fStyle, [&](sk_sp<SkTypeface> typeface) {
@@ -612,6 +594,11 @@ bool OneLineShaper::shape() {
auto unresolvedCount = fUnresolvedBlocks.size();
while (unresolvedCount-- > 0) {
auto unresolvedRange = fUnresolvedBlocks.front().fText;
+ if (unresolvedRange == EMPTY_TEXT) {
+ // Duplicate blocks should be ignored
+ fUnresolvedBlocks.pop_front();
+ continue;
+ }
auto unresolvedText = fParagraph->text(unresolvedRange);
SkShaper::TrivialFontRunIterator fontIter(font, unresolvedText.size());
@@ -628,7 +615,7 @@ bool OneLineShaper::shape() {
// Take off the queue the block we tried to resolved -
// whatever happened, we have now smaller pieces of it to deal with
- this->dropUnresolved();
+ fUnresolvedBlocks.pop_front();
}
if (fUnresolvedBlocks.empty()) {
@@ -660,15 +647,17 @@ TextRange OneLineShaper::clusteredText(GlyphRange& glyphs) {
if (dir == Dir::right) {
while (index < fCurrentRun->fTextRange.end) {
- if (this->fParagraph->fGraphemes.contains(index)) {
+ if (this->fParagraph->codeUnitHasProperty(index,
+ CodeUnitFlags::kGraphemeStart)) {
return index;
}
++index;
}
return fCurrentRun->fTextRange.end;
} else {
- while (index >= fCurrentRun->fTextRange.start) {
- if (this->fParagraph->fGraphemes.contains(index)) {
+ while (index > fCurrentRun->fTextRange.start) {
+ if (this->fParagraph->codeUnitHasProperty(index,
+ CodeUnitFlags::kGraphemeStart)) {
return index;
}
--index;
diff --git a/chromium/third_party/skia/modules/skparagraph/src/OneLineShaper.h b/chromium/third_party/skia/modules/skparagraph/src/OneLineShaper.h
index 456855f8382..569fadb804f 100644
--- a/chromium/third_party/skia/modules/skparagraph/src/OneLineShaper.h
+++ b/chromium/third_party/skia/modules/skparagraph/src/OneLineShaper.h
@@ -42,8 +42,8 @@ private:
// Entire run comes as one block fully resolved
explicit RunBlock(std::shared_ptr<Run> run)
: fRun(std::move(run))
- , fText(run->fTextRange)
- , fGlyphs(GlyphRange(0, run->size())) { }
+ , fText(fRun->fTextRange)
+ , fGlyphs(GlyphRange(0, fRun->size())) { }
std::shared_ptr<Run> fRun;
TextRange fText;
@@ -69,7 +69,6 @@ private:
#ifdef SK_DEBUG
void printState();
#endif
- void dropUnresolved();
void finish(TextRange text, SkScalar height, SkScalar& advanceX);
void beginLine() override {}
@@ -97,7 +96,6 @@ private:
void addUnresolvedWithRun(GlyphRange glyphRange);
void sortOutGlyphs(std::function<void(GlyphRange)>&& sortOutUnresolvedBLock);
ClusterRange normalizeTextRange(GlyphRange glyphRange);
- void increment(TextIndex& index);
void fillGaps(size_t);
ParagraphImpl* fParagraph;
@@ -109,7 +107,7 @@ private:
// TODO: Something that is not thead-safe since we don't need it
std::shared_ptr<Run> fCurrentRun;
- std::queue<RunBlock> fUnresolvedBlocks;
+ std::deque<RunBlock> fUnresolvedBlocks;
std::vector<RunBlock> fResolvedBlocks;
// Keeping all resolved typefaces
diff --git a/chromium/third_party/skia/modules/skparagraph/src/ParagraphBuilderImpl.cpp b/chromium/third_party/skia/modules/skparagraph/src/ParagraphBuilderImpl.cpp
index 64b6fce4245..5f904e94892 100644
--- a/chromium/third_party/skia/modules/skparagraph/src/ParagraphBuilderImpl.cpp
+++ b/chromium/third_party/skia/modules/skparagraph/src/ParagraphBuilderImpl.cpp
@@ -1,10 +1,17 @@
// Copyright 2019 Google LLC.
-#include "include/core/SkPaint.h"
+
+#include "include/core/SkTypes.h"
+#include "modules/skparagraph/include/FontCollection.h"
+#include "modules/skparagraph/include/Paragraph.h"
+#include "modules/skparagraph/include/ParagraphBuilder.h"
#include "modules/skparagraph/include/ParagraphStyle.h"
+#include "modules/skparagraph/include/TextStyle.h"
#include "modules/skparagraph/src/ParagraphBuilderImpl.h"
#include "modules/skparagraph/src/ParagraphImpl.h"
-#include "src/core/SkSpan.h"
-#include "unicode/unistr.h"
+#include "modules/skparagraph/src/ParagraphUtil.h"
+
+#include <algorithm>
+#include <utility>
namespace skia {
namespace textlayout {
@@ -67,19 +74,15 @@ TextStyle ParagraphBuilderImpl::peekStyle() {
}
void ParagraphBuilderImpl::addText(const std::u16string& text) {
- icu::UnicodeString unicode;
- unicode.setTo((UChar*)text.data());
- std::string str;
- unicode.toUTF8String(str);
- fUtf8.insert(fUtf8.size(), str.c_str());
+ fUtf8.append(SkStringFromU16String(text));
}
void ParagraphBuilderImpl::addText(const char* text) {
- fUtf8.insert(fUtf8.size(), text);
+ fUtf8.append(text);
}
void ParagraphBuilderImpl::addText(const char* text, size_t len) {
- fUtf8.insert(fUtf8.size(), text, len);
+ fUtf8.append(text, len);
}
void ParagraphBuilderImpl::addPlaceholder(const PlaceholderStyle& placeholderStyle) {
diff --git a/chromium/third_party/skia/modules/skparagraph/src/ParagraphCache.cpp b/chromium/third_party/skia/modules/skparagraph/src/ParagraphCache.cpp
index ca47776ceae..2464acaee69 100644
--- a/chromium/third_party/skia/modules/skparagraph/src/ParagraphCache.cpp
+++ b/chromium/third_party/skia/modules/skparagraph/src/ParagraphCache.cpp
@@ -35,13 +35,24 @@ class ParagraphCacheValue {
public:
ParagraphCacheValue(const ParagraphImpl* paragraph)
: fKey(ParagraphCacheKey(paragraph))
- , fRuns(paragraph->fRuns) { }
+ , fRuns(paragraph->fRuns)
+ , fCodeUnitProperties(paragraph->fCodeUnitProperties)
+ , fWords(paragraph->fWords)
+ , fBidiRegions(paragraph->fBidiRegions)
+ , fUTF8IndexForUTF16Index(paragraph->fUTF8IndexForUTF16Index)
+ , fUTF16IndexForUTF8Index(paragraph->fUTF16IndexForUTF8Index) { }
// Input == key
ParagraphCacheKey fKey;
// Shaped results
SkTArray<Run, false> fRuns;
+ // ICU results
+ SkTArray<CodeUnitFlags> fCodeUnitProperties;
+ std::vector<size_t> fWords;
+ SkTArray<BidiRegion> fBidiRegions;
+ SkTArray<TextIndex, true> fUTF8IndexForUTF16Index;
+ SkTArray<size_t, true> fUTF16IndexForUTF8Index;
};
uint32_t ParagraphCache::KeyHash::mix(uint32_t hash, uint32_t data) const {
@@ -193,6 +204,11 @@ void ParagraphCache::updateTo(ParagraphImpl* paragraph, const Entry* entry) {
paragraph->fRuns.reset();
paragraph->fRuns = entry->fValue->fRuns;
+ paragraph->fCodeUnitProperties = entry->fValue->fCodeUnitProperties;
+ paragraph->fWords = entry->fValue->fWords;
+ paragraph->fBidiRegions = entry->fValue->fBidiRegions;
+ paragraph->fUTF8IndexForUTF16Index = entry->fValue->fUTF8IndexForUTF16Index;
+ paragraph->fUTF16IndexForUTF8Index = entry->fValue->fUTF16IndexForUTF8Index;
for (auto& run : paragraph->fRuns) {
run.setMaster(paragraph);
}
diff --git a/chromium/third_party/skia/modules/skparagraph/src/ParagraphImpl.cpp b/chromium/third_party/skia/modules/skparagraph/src/ParagraphImpl.cpp
index 285ceaadcc7..7389ef37d69 100644
--- a/chromium/third_party/skia/modules/skparagraph/src/ParagraphImpl.cpp
+++ b/chromium/third_party/skia/modules/skparagraph/src/ParagraphImpl.cpp
@@ -1,18 +1,39 @@
// Copyright 2019 Google LLC.
-#include "include/core/SkBlurTypes.h"
+
#include "include/core/SkCanvas.h"
-#include "include/core/SkFontMgr.h"
+#include "include/core/SkFontMetrics.h"
+#include "include/core/SkMatrix.h"
#include "include/core/SkPictureRecorder.h"
+#include "include/core/SkTypeface.h"
+#include "include/private/SkTFitsIn.h"
+#include "include/private/SkTo.h"
+#include "modules/skparagraph/include/Metrics.h"
+#include "modules/skparagraph/include/Paragraph.h"
+#include "modules/skparagraph/include/ParagraphStyle.h"
+#include "modules/skparagraph/include/TextStyle.h"
#include "modules/skparagraph/src/OneLineShaper.h"
#include "modules/skparagraph/src/ParagraphImpl.h"
+#include "modules/skparagraph/src/ParagraphUtil.h"
#include "modules/skparagraph/src/Run.h"
+#include "modules/skparagraph/src/TextLine.h"
#include "modules/skparagraph/src/TextWrapper.h"
#include "src/core/SkSpan.h"
#include "src/utils/SkUTF.h"
+
+#if defined(SK_USING_THIRD_PARTY_ICU)
+#include "third_party/icu/SkLoadICU.h"
+#endif
+
+#include <math.h>
+#include <unicode/ubidi.h>
+#include <unicode/uloc.h>
+#include <unicode/umachine.h>
#include <unicode/ustring.h>
+#include <unicode/utext.h>
+#include <unicode/utypes.h>
#include <algorithm>
-#include <chrono>
-#include <queue>
+#include <utility>
+
namespace skia {
namespace textlayout {
@@ -49,34 +70,18 @@ TextRange operator*(const TextRange& a, const TextRange& b) {
return end > begin ? TextRange(begin, end) : EMPTY_TEXT;
}
-bool TextBreaker::initialize(SkSpan<const char> text, UBreakIteratorType type) {
-
- UErrorCode status = U_ZERO_ERROR;
- fIterator = nullptr;
- fSize = text.size();
- UText sUtf8UText = UTEXT_INITIALIZER;
- std::unique_ptr<UText, SkFunctionWrapper<decltype(utext_close), utext_close>> utf8UText(
- utext_openUTF8(&sUtf8UText, text.begin(), text.size(), &status));
- if (U_FAILURE(status)) {
- SkDEBUGF("Could not create utf8UText: %s", u_errorName(status));
- return false;
- }
- fIterator.reset(ubrk_open(type, "en", nullptr, 0, &status));
- if (U_FAILURE(status)) {
- SkDEBUGF("Could not create line break iterator: %s", u_errorName(status));
- SK_ABORT("");
- }
-
- ubrk_setUText(fIterator.get(), utf8UText.get(), &status);
- if (U_FAILURE(status)) {
- SkDEBUGF("Could not setText on break iterator: %s", u_errorName(status));
- return false;
- }
-
- fInitialized = true;
- fPos = 0;
- return true;
-}
+Paragraph::Paragraph(ParagraphStyle style, sk_sp<FontCollection> fonts)
+ : fFontCollection(std::move(fonts))
+ , fParagraphStyle(std::move(style))
+ , fAlphabeticBaseline(0)
+ , fIdeographicBaseline(0)
+ , fHeight(0)
+ , fWidth(0)
+ , fMaxIntrinsicWidth(0)
+ , fMinIntrinsicWidth(0)
+ , fLongestLine(0)
+ , fExceededMaxLines(0)
+{ }
ParagraphImpl::ParagraphImpl(const SkString& text,
ParagraphStyle style,
@@ -94,7 +99,6 @@ ParagraphImpl::ParagraphImpl(const SkString& text,
, fOldWidth(0)
, fOldHeight(0)
, fOrigin(SkRect::MakeEmpty()) {
- // TODO: extractStyles();
}
ParagraphImpl::ParagraphImpl(const std::u16string& utf16text,
@@ -102,22 +106,12 @@ ParagraphImpl::ParagraphImpl(const std::u16string& utf16text,
SkTArray<Block, true> blocks,
SkTArray<Placeholder, true> placeholders,
sk_sp<FontCollection> fonts)
- : Paragraph(std::move(style), std::move(fonts))
- , fTextStyles(std::move(blocks))
- , fPlaceholders(std::move(placeholders))
- , fState(kUnknown)
- , fUnresolvedGlyphs(0)
- , fPicture(nullptr)
- , fStrutMetrics(false)
- , fOldWidth(0)
- , fOldHeight(0)
- , fOrigin(SkRect::MakeEmpty()) {
- icu::UnicodeString unicode((UChar*)utf16text.data(), SkToS32(utf16text.size()));
- std::string str;
- unicode.toUTF8String(str);
- fText = SkString(str.data(), str.size());
- // TODO: extractStyles();
-}
+ : ParagraphImpl(SkStringFromU16String(utf16text),
+ std::move(style),
+ std::move(blocks),
+ std::move(placeholders),
+ std::move(fonts))
+{ }
ParagraphImpl::~ParagraphImpl() = default;
@@ -133,22 +127,28 @@ void ParagraphImpl::layout(SkScalar rawWidth) {
// TODO: This rounding is done to match Flutter tests. Must be removed...
auto floorWidth = SkScalarFloorToScalar(rawWidth);
- if (fState < kShaped) {
- // Layout marked as dirty for performance/testing reasons
- this->fRuns.reset();
- this->fClusters.reset();
- this->resetShifts();
- } else if (fState >= kLineBroken && (fOldWidth != floorWidth || fOldHeight != fHeight)) {
+
+ if ((!SkScalarIsFinite(rawWidth) || fLongestLine <= floorWidth) &&
+ fState >= kLineBroken &&
+ fLines.size() == 1 && fLines.front().ellipsis() == nullptr) {
+ // Most common case: one line of text (and one line is never justified, so no cluster shifts)
+ fWidth = floorWidth;
+ fState = kLineBroken;
+ } else if (fState >= kLineBroken && fOldWidth != floorWidth) {
// We can use the results from SkShaper but have to do EVERYTHING ELSE again
- this->fClusters.reset();
- this->resetShifts();
fState = kShaped;
+ } else {
+ // Nothing changed case: we can reuse the data from the last layout
}
if (fState < kShaped) {
- fGraphemes.reset();
- this->markGraphemes();
-
+ this->fCodeUnitProperties.reset();
+ this->fCodeUnitProperties.push_back_n(fText.size() + 1, CodeUnitFlags::kNoCodeUnitFlag);
+ this->fWords.clear();
+ this->fBidiRegions.reset();
+ this->fUTF8IndexForUTF16Index.reset();
+ this->fUTF16IndexForUTF8Index.reset();
+ this->fRuns.reset();
if (!this->shapeTextIntoEndlessLine()) {
this->resetContext();
// TODO: merge the two next calls - they always come together
@@ -165,6 +165,7 @@ void ParagraphImpl::layout(SkScalar rawWidth) {
}
fAlphabeticBaseline = fEmptyMetrics.alphabeticBaseline();
fIdeographicBaseline = fEmptyMetrics.ideographicBaseline();
+ fLongestLine = FLT_MIN - FLT_MAX; // That is what flutter has
fMinIntrinsicWidth = 0;
fMaxIntrinsicWidth = 0;
this->fOldWidth = floorWidth;
@@ -172,27 +173,20 @@ void ParagraphImpl::layout(SkScalar rawWidth) {
return;
}
-
- this->fClusters.reset();
- this->resetShifts();
fState = kShaped;
}
if (fState < kMarked) {
+ this->fClusters.reset();
+ this->resetShifts();
+ this->fClustersIndexFromCodeUnit.reset();
+ this->fClustersIndexFromCodeUnit.push_back_n(fText.size() + 1, EMPTY_INDEX);
this->buildClusterTable();
fState = kClusterized;
-
- this->markLineBreaks();
this->spaceGlyphs();
fState = kMarked;
}
- if (fState >= kLineBroken) {
- if (fOldWidth != floorWidth || fOldHeight != fHeight) {
- fState = kMarked;
- }
- }
-
if (fState < kLineBroken) {
this->resetContext();
this->resolveStrut();
@@ -234,7 +228,7 @@ void ParagraphImpl::paint(SkCanvas* canvas, SkScalar x, SkScalar y) {
fState = kDrawn;
}
- SkMatrix matrix = SkMatrix::MakeTrans(x + fOrigin.fLeft, y + fOrigin.fTop);
+ SkMatrix matrix = SkMatrix::Translate(x + fOrigin.fLeft, y + fOrigin.fTop);
canvas->drawPicture(fPicture, &matrix, nullptr);
}
@@ -250,6 +244,246 @@ void ParagraphImpl::resetContext() {
fExceededMaxLines = false;
}
+class TextBreaker {
+public:
+ TextBreaker() : fInitialized(false), fPos(-1) {}
+
+ bool initialize(SkSpan<const char> text, UBreakIteratorType type) {
+
+ UErrorCode status = U_ZERO_ERROR;
+ fIterator = nullptr;
+ fSize = text.size();
+ UText sUtf8UText = UTEXT_INITIALIZER;
+ std::unique_ptr<UText, SkFunctionWrapper<decltype(utext_close), utext_close>> utf8UText(
+ utext_openUTF8(&sUtf8UText, text.begin(), text.size(), &status));
+ if (U_FAILURE(status)) {
+ SkDEBUGF("Could not create utf8UText: %s", u_errorName(status));
+ return false;
+ }
+ fIterator.reset(ubrk_open(type, "en", nullptr, 0, &status));
+ if (U_FAILURE(status)) {
+ SkDEBUGF("Could not create line break iterator: %s", u_errorName(status));
+ SK_ABORT("");
+ }
+
+ ubrk_setUText(fIterator.get(), utf8UText.get(), &status);
+ if (U_FAILURE(status)) {
+ SkDEBUGF("Could not setText on break iterator: %s", u_errorName(status));
+ return false;
+ }
+
+ fInitialized = true;
+ fPos = 0;
+ return true;
+ }
+
+ bool initialized() const { return fInitialized; }
+
+ size_t first() {
+ fPos = ubrk_first(fIterator.get());
+ return eof() ? fSize : fPos;
+ }
+
+ size_t next() {
+ fPos = ubrk_next(fIterator.get());
+ return eof() ? fSize : fPos;
+ }
+
+ size_t preceding(size_t offset) {
+ auto pos = ubrk_preceding(fIterator.get(), offset);
+ return pos == UBRK_DONE ? 0 : pos;
+ }
+
+ size_t following(size_t offset) {
+ auto pos = ubrk_following(fIterator.get(), offset);
+ return pos == UBRK_DONE ? fSize : pos;
+ }
+
+ int32_t status() { return ubrk_getRuleStatus(fIterator.get()); }
+
+ bool eof() { return fPos == UBRK_DONE; }
+
+private:
+ std::unique_ptr<UBreakIterator, SkFunctionWrapper<decltype(ubrk_close), ubrk_close>> fIterator;
+ bool fInitialized;
+ int32_t fPos;
+ size_t fSize;
+};
+
+// shapeTextIntoEndlessLine is the thing that calls this method
+// (that contains all ICU dependencies except for words)
+bool ParagraphImpl::computeCodeUnitProperties() {
+
+ #if defined(SK_USING_THIRD_PARTY_ICU)
+ if (!SkLoadICU()) {
+ return false;
+ }
+ #endif
+
+ {
+ const char* start = fText.c_str();
+ const char* end = start + fText.size();
+ const char* ch = start;
+ while (ch < end) {
+ auto index = ch - start;
+ auto unichar = utf8_next(&ch, end);
+ if (u_isWhitespace(unichar)) {
+ auto ending = ch - start;
+ for (auto k = index; k < ending; ++k) {
+ fCodeUnitProperties[k] |= CodeUnitFlags::kPartOfWhiteSpace;
+ }
+ }
+ }
+ }
+ {
+ TextBreaker breaker;
+ if (!breaker.initialize(this->text(), UBRK_LINE)) {
+ return false;
+ }
+ while (!breaker.eof()) {
+ size_t currentPos = breaker.next();
+ fCodeUnitProperties[currentPos] |=
+ breaker.status() == UBRK_LINE_HARD ? CodeUnitFlags::kHardLineBreakBefore : CodeUnitFlags::kSoftLineBreakBefore;
+ }
+ }
+ {
+ TextBreaker breaker;
+ if (!breaker.initialize(this->text(), UBRK_CHARACTER)) {
+ return false;
+ }
+
+ while (!breaker.eof()) {
+ auto currentPos = breaker.next();
+ fCodeUnitProperties[currentPos] |= CodeUnitFlags::kGraphemeStart;
+ }
+ }
+
+ return true;
+}
+
+// getWordBoundary is the thing that calls this method lazily
+bool ParagraphImpl::computeWords() {
+
+ if (!fWords.empty()) {
+ return true;
+ }
+
+ UErrorCode errorCode = U_ZERO_ERROR;
+
+ auto iter = ubrk_open(UBRK_WORD, uloc_getDefault(), nullptr, 0, &errorCode);
+ if (U_FAILURE(errorCode)) {
+ SkDEBUGF("Could not create line break iterator: %s", u_errorName(errorCode));
+ return false;
+ }
+
+ // Getting the length like this seems to always set U_BUFFER_OVERFLOW_ERROR
+ int32_t utf16Units;
+ u_strFromUTF8(nullptr, 0, &utf16Units, fText.c_str(), fText.size(), &errorCode);
+ errorCode = U_ZERO_ERROR;
+ std::unique_ptr<UChar[]> utf16(new UChar[utf16Units]);
+ u_strFromUTF8(utf16.get(), utf16Units, nullptr, fText.c_str(), fText.size(), &errorCode);
+ if (U_FAILURE(errorCode)) {
+ SkDEBUGF("Invalid utf8 input: %s", u_errorName(errorCode));
+ return false;
+ }
+
+ UText sUtf16UText = UTEXT_INITIALIZER;
+ ICUUText utf8UText(utext_openUChars(&sUtf16UText, utf16.get(), utf16Units, &errorCode));
+ if (U_FAILURE(errorCode)) {
+ SkDEBUGF("Could not create utf8UText: %s", u_errorName(errorCode));
+ return false;
+ }
+
+ ubrk_setUText(iter, utf8UText.get(), &errorCode);
+ if (U_FAILURE(errorCode)) {
+ SkDEBUGF("Could not setText on break iterator: %s", u_errorName(errorCode));
+ return false;
+ }
+
+ int32_t pos = ubrk_first(iter);
+ while (pos != UBRK_DONE) {
+ fWords.emplace_back(pos);
+ pos = ubrk_next(iter);
+ }
+
+ return true;
+}
+
+bool ParagraphImpl::getBidiRegions() {
+
+ if (!fBidiRegions.empty()) {
+ return true;
+ }
+
+ // ubidi only accepts utf16 (though internally it basically works on utf32 chars).
+ // We want an ubidi_setPara(UBiDi*, UText*, UBiDiLevel, UBiDiLevel*, UErrorCode*);
+ size_t utf8Bytes = fText.size();
+ const char* utf8 = fText.c_str();
+ uint8_t bidiLevel = fParagraphStyle.getTextDirection() == TextDirection::kLtr
+ ? UBIDI_LTR
+ : UBIDI_RTL;
+ if (!SkTFitsIn<int32_t>(utf8Bytes)) {
+ SkDEBUGF("Bidi error: text too long");
+ return false;
+ }
+
+ // Getting the length like this seems to always set U_BUFFER_OVERFLOW_ERROR
+ UErrorCode status = U_ZERO_ERROR;
+ int32_t utf16Units;
+ u_strFromUTF8(nullptr, 0, &utf16Units, utf8, utf8Bytes, &status);
+ status = U_ZERO_ERROR;
+ std::unique_ptr<UChar[]> utf16(new UChar[utf16Units]);
+ u_strFromUTF8(utf16.get(), utf16Units, nullptr, utf8, utf8Bytes, &status);
+ if (U_FAILURE(status)) {
+ SkDEBUGF("Invalid utf8 input: %s", u_errorName(status));
+ return false;
+ }
+
+ ICUBiDi bidi(ubidi_openSized(utf16Units, 0, &status));
+ if (U_FAILURE(status)) {
+ SkDEBUGF("Bidi error: %s", u_errorName(status));
+ return false;
+ }
+ SkASSERT(bidi);
+
+ // The required lifetime of utf16 isn't well documented.
+ // It appears it isn't used after ubidi_setPara except through ubidi_getText.
+ ubidi_setPara(bidi.get(), utf16.get(), utf16Units, bidiLevel, nullptr, &status);
+ if (U_FAILURE(status)) {
+ SkDEBUGF("Bidi error: %s", u_errorName(status));
+ return false;
+ }
+
+ SkTArray<BidiRegion> bidiRegions;
+ const char* start8 = utf8;
+ const char* end8 = utf8 + utf8Bytes;
+ TextRange textRange(0, 0);
+ UBiDiLevel currentLevel = 0;
+
+ int32_t pos16 = 0;
+ int32_t end16 = ubidi_getLength(bidi.get());
+ while (pos16 < end16) {
+ auto level = ubidi_getLevelAt(bidi.get(), pos16);
+ if (pos16 == 0) {
+ currentLevel = level;
+ } else if (level != currentLevel) {
+ textRange.end = start8 - utf8;
+ fBidiRegions.emplace_back(textRange.start, textRange.end, currentLevel);
+ currentLevel = level;
+ textRange = TextRange(textRange.end, textRange.end);
+ }
+ SkUnichar u = utf8_next(&start8, end8);
+ pos16 += SkUTF::ToUTF16(u);
+ }
+
+ textRange.end = start8 - utf8;
+ if (!textRange.empty()) {
+ fBidiRegions.emplace_back(textRange.start, textRange.end, currentLevel);
+ }
+
+ return true;
+}
+
// Clusters in the order of the input text
void ParagraphImpl::buildClusterTable() {
@@ -258,14 +492,14 @@ void ParagraphImpl::buildClusterTable() {
auto runIndex = run.index();
auto runStart = fClusters.size();
if (run.isPlaceholder()) {
- // There are no glyphs but we want to have one cluster
- SkSpan<const char> text = this->text(run.textRange());
- if (!fClusters.empty()) {
- fClusters.back().setBreakType(Cluster::SoftLineBreak);
+ // Add info to cluster indexes table (text -> cluster)
+ for (auto i = run.textRange().start; i < run.textRange().end; ++i) {
+ fClustersIndexFromCodeUnit[i] = fClusters.size();
}
- auto& cluster = fClusters.emplace_back(this, runIndex, 0ul, 1ul, text, run.advance().fX,
- run.advance().fY);
- cluster.setBreakType(Cluster::SoftLineBreak);
+ // There are no glyphs but we want to have one cluster
+ fClusters.emplace_back(this, runIndex, 0ul, 1ul, this->text(run.textRange()), run.advance().fX, run.advance().fY);
+ fCodeUnitProperties[run.textRange().start] |= CodeUnitFlags::kSoftLineBreakBefore;
+ fCodeUnitProperties[run.textRange().end] |= CodeUnitFlags::kSoftLineBreakBefore;
} else {
fClusters.reserve(fClusters.size() + run.size());
// Walk through the glyph in the direction of input text
@@ -276,20 +510,20 @@ void ParagraphImpl::buildClusterTable() {
SkScalar width,
SkScalar height) {
SkASSERT(charEnd >= charStart);
- SkSpan<const char> text(fText.c_str() + charStart, charEnd - charStart);
- auto& cluster = fClusters.emplace_back(this, runIndex, glyphStart, glyphEnd, text,
- width, height);
- cluster.setIsWhiteSpaces();
- if (fGraphemes.find(cluster.fTextRange.end) != nullptr) {
- cluster.setBreakType(Cluster::BreakType::GraphemeBreak);
+ // Add info to cluster indexes table (text -> cluster)
+ for (auto i = charStart; i < charEnd; ++i) {
+ fClustersIndexFromCodeUnit[i] = fClusters.size();
}
+ SkSpan<const char> text(fText.c_str() + charStart, charEnd - charStart);
+ fClusters.emplace_back(this, runIndex, glyphStart, glyphEnd, text, width, height);
});
}
run.setClusterRange(runStart, fClusters.size());
fMaxIntrinsicWidth += run.advance().fX;
}
- fClusters.emplace_back(this, EMPTY_RUN, 0, 0, SkSpan<const char>(), 0, 0);
+ fClustersIndexFromCodeUnit[fText.size()] = fClusters.size();
+ fClusters.emplace_back(this, EMPTY_RUN, 0, 0, this->text({fText.size(), fText.size()}), 0, 0);
}
void ParagraphImpl::spaceGlyphs() {
@@ -338,41 +572,6 @@ void ParagraphImpl::spaceGlyphs() {
}
}
-void ParagraphImpl::markLineBreaks() {
-
- // Find all possible (soft) line breaks
- // This iterator is used only once for a paragraph so we don't have to keep it
- TextBreaker breaker;
- if (!breaker.initialize(this->text(), UBRK_LINE)) {
- return;
- }
-
- // Mark all soft line breaks
- // Remove soft line breaks that are not on grapheme cluster edge
- Cluster* current = fClusters.begin();
- while (!breaker.eof() && current < fClusters.end()) {
- size_t currentPos = breaker.next();
- while (current < fClusters.end()) {
- if (current->textRange().end > currentPos) {
- break;
- } else if (current->textRange().end == currentPos) {
- if (breaker.status() == UBRK_LINE_HARD) {
- // Hard line break stronger than anything
- current->setBreakType(Cluster::BreakType::HardLineBreak);
- } else if (current->isGraphemeBreak()) {
- // Only allow soft line break if it's grapheme break
- current->setBreakType(Cluster::BreakType::SoftLineBreak);
- } else {
- // Leave it as is (either it's no break or a placeholder)
- }
- ++current;
- break;
- }
- ++current;
- }
- }
-}
-
bool ParagraphImpl::shapeTextIntoEndlessLine() {
if (fText.size() == 0) {
@@ -384,6 +583,10 @@ bool ParagraphImpl::shapeTextIntoEndlessLine() {
return true;
}
+ if (!computeCodeUnitProperties()) {
+ return false;
+ }
+
fFontSwitches.reset();
OneLineShaper oneLineShaper(this);
@@ -560,76 +763,6 @@ TextLine& ParagraphImpl::addLine(SkVector offset,
return fLines.emplace_back(this, offset, advance, blocks, text, textWithSpaces, clusters, clustersWithGhosts, widthWithSpaces, sizes);
}
-void ParagraphImpl::markGraphemes16() {
-
- if (!fGraphemes16.empty()) {
- return;
- }
-
- // This breaker gets called only once for a paragraph so we don't have to keep it
- TextBreaker breaker;
- if (!breaker.initialize(this->text(), UBRK_CHARACTER)) {
- return;
- }
-
- auto ptr = fText.c_str();
- auto end = fText.c_str() + fText.size();
- while (ptr < end) {
-
- size_t index = ptr - fText.c_str();
- SkUnichar u = SkUTF::NextUTF8(&ptr, end);
- uint16_t buffer[2];
- size_t count = SkUTF::ToUTF16(u, buffer);
- fCodePoints.emplace_back(EMPTY_INDEX, index, count > 1 ? 2 : 1);
- if (count > 1) {
- fCodePoints.emplace_back(EMPTY_INDEX, index, 1);
- }
- }
-
- CodepointRange codepoints(0ul, 0ul);
-
- size_t endPos = 0;
- while (!breaker.eof()) {
- auto startPos = endPos;
- endPos = breaker.next();
-
- // Collect all the codepoints that belong to the grapheme
- while (codepoints.end < fCodePoints.size() && fCodePoints[codepoints.end].fTextIndex < endPos) {
- ++codepoints.end;
- }
-
- if (startPos == endPos) {
- continue;
- }
-
- //SkDebugf("Grapheme #%d [%d:%d)\n", fGraphemes16.size(), startPos, endPos);
-
- // Update all the codepoints that belong to this grapheme
- for (auto i = codepoints.start; i < codepoints.end; ++i) {
- //SkDebugf(" [%d] = %d + %d\n", i, fCodePoints[i].fTextIndex, fCodePoints[i].fIndex);
- fCodePoints[i].fGrapheme = fGraphemes16.size();
- }
-
- fGraphemes16.emplace_back(codepoints, TextRange(startPos, endPos));
- codepoints.start = codepoints.end;
- }
-}
-
-void ParagraphImpl::markGraphemes() {
-
- // This breaker gets called only once for a paragraph so we don't have to keep it
- TextBreaker breaker;
- if (!breaker.initialize(this->text(), UBRK_CHARACTER)) {
- return;
- }
-
- auto endPos = breaker.first();
- while (!breaker.eof()) {
- fGraphemes.add(endPos);
- endPos = breaker.next();
- }
-}
-
// Returns a vector of bounding boxes that enclose all text between
// start and end glyph indexes, including start and excluding end
std::vector<TextBox> ParagraphImpl::getRectsForRange(unsigned start,
@@ -646,9 +779,9 @@ std::vector<TextBox> ParagraphImpl::getRectsForRange(unsigned start,
return results;
}
- markGraphemes16();
+ ensureUTF16Mapping();
- if (start >= end || start > fCodePoints.size() || end == 0) {
+ if (start >= end || start > fUTF8IndexForUTF16Index.size() || end == 0) {
return results;
}
@@ -661,16 +794,11 @@ std::vector<TextBox> ParagraphImpl::getRectsForRange(unsigned start,
// One flutter test fails because of it but the editing experience is correct
// (although you have to press the cursor many times before it moves to the next grapheme).
TextRange text(fText.size(), fText.size());
- if (start < fCodePoints.size()) {
- auto codepoint = fCodePoints[start];
- auto grapheme = fGraphemes16[codepoint.fGrapheme];
- text.start = grapheme.fTextRange.start;
+ if (start < fUTF8IndexForUTF16Index.size()) {
+ text.start = findGraphemeStart(fUTF8IndexForUTF16Index[start]);
}
-
- if (end < fCodePoints.size()) {
- auto codepoint = fCodePoints[end];
- auto grapheme = fGraphemes16[codepoint.fGrapheme];
- text.end = grapheme.fTextRange.start;
+ if (end < fUTF8IndexForUTF16Index.size()) {
+ text.end = findGraphemeStart(fUTF8IndexForUTF16Index[end]);
}
for (auto& line : fLines) {
@@ -728,7 +856,8 @@ PositionWithAffinity ParagraphImpl::getGlyphPositionAtCoordinate(SkScalar dx, Sk
return {0, Affinity::kDownstream};
}
- markGraphemes16();
+ ensureUTF16Mapping();
+
for (auto& line : fLines) {
// Let's figure out if we can stop looking
auto offsetY = line.offset().fY;
@@ -753,35 +882,9 @@ PositionWithAffinity ParagraphImpl::getGlyphPositionAtCoordinate(SkScalar dx, Sk
// the glyph at index offset.
// By "glyph" they mean a character index - indicated by Minikin's code
SkRange<size_t> ParagraphImpl::getWordBoundary(unsigned offset) {
- if (fWords.empty()) {
- auto unicode = icu::UnicodeString::fromUTF8(fText.c_str());
-
- UErrorCode errorCode = U_ZERO_ERROR;
- auto iter = ubrk_open(UBRK_WORD, icu::Locale().getName(), nullptr, 0, &errorCode);
- if (U_FAILURE(errorCode)) {
- SkDEBUGF("Could not create line break iterator: %s", u_errorName(errorCode));
- return {0, 0};
- }
-
- UText sUtf16UText = UTEXT_INITIALIZER;
- ICUUText utf16UText(utext_openUnicodeString(&sUtf16UText, &unicode, &errorCode));
- if (U_FAILURE(errorCode)) {
- SkDEBUGF("Could not create utf8UText: %s", u_errorName(errorCode));
- return {0, 0};
- }
-
- ubrk_setUText(iter, utf16UText.get(), &errorCode);
- if (U_FAILURE(errorCode)) {
- SkDEBUGF("Could not setText on break iterator: %s", u_errorName(errorCode));
- return {0, 0};
- }
-
- int32_t pos = ubrk_first(iter);
- while (pos != icu::BreakIterator::DONE) {
- fWords.emplace_back(pos);
- pos = ubrk_next(iter);
- }
+ if (!computeWords()) {
+ return {0, 0 };
}
int32_t start = 0;
@@ -796,10 +899,36 @@ SkRange<size_t> ParagraphImpl::getWordBoundary(unsigned offset) {
break;
}
}
+
//SkDebugf("getWordBoundary(%d): %d - %d\n", offset, start, end);
return { SkToU32(start), SkToU32(end) };
}
+void ParagraphImpl::forEachCodeUnitPropertyRange(CodeUnitFlags property, CodeUnitRangeVisitor visitor) {
+
+ size_t first = 0;
+ for (size_t i = 1; i < fText.size(); ++i) {
+ auto properties = fCodeUnitProperties[i];
+ if (properties & property) {
+ visitor({first, i});
+ first = i;
+ }
+
+ }
+ visitor({first, fText.size()});
+}
+
+size_t ParagraphImpl::getWhitespacesLength(TextRange textRange) {
+ size_t len = 0;
+ for (auto i = textRange.start; i < textRange.end; ++i) {
+ auto properties = fCodeUnitProperties[i];
+ if (properties & CodeUnitFlags::kPartOfWhiteSpace) {
+ ++len;
+ }
+ }
+ return len;
+}
+
void ParagraphImpl::getLineMetrics(std::vector<LineMetrics>& metrics) {
metrics.clear();
for (auto& line : fLines) {
@@ -823,11 +952,6 @@ Cluster& ParagraphImpl::cluster(ClusterIndex clusterIndex) {
return fClusters[clusterIndex];
}
-Run& ParagraphImpl::run(RunIndex runIndex) {
- SkASSERT(runIndex < fRuns.size());
- return fRuns[runIndex];
-}
-
Run& ParagraphImpl::runByCluster(ClusterIndex clusterIndex) {
auto start = cluster(clusterIndex);
return this->run(start.fRunIndex);
@@ -853,8 +977,18 @@ void ParagraphImpl::setState(InternalState state) {
switch (fState) {
case kUnknown:
fRuns.reset();
+ fCodeUnitProperties.reset();
+ fCodeUnitProperties.push_back_n(fText.size() + 1, kNoCodeUnitFlag);
+ fWords.clear();
+ fBidiRegions.reset();
+ fUTF8IndexForUTF16Index.reset();
+ fUTF16IndexForUTF8Index.reset();
+ [[fallthrough]];
+
case kShaped:
fClusters.reset();
+ [[fallthrough]];
+
case kClusterized:
case kMarked:
case kLineBroken:
@@ -863,12 +997,15 @@ void ParagraphImpl::setState(InternalState state) {
this->computeEmptyMetrics();
this->resetShifts();
fLines.reset();
+ [[fallthrough]];
+
case kFormatted:
fPicture = nullptr;
+ [[fallthrough]];
+
case kDrawn:
+ default:
break;
- default:
- break;
}
}
@@ -950,77 +1087,46 @@ void ParagraphImpl::updateBackgroundPaint(size_t from, size_t to, SkPaint paint)
}
}
-bool ParagraphImpl::calculateBidiRegions(SkTArray<BidiRegion>* regions) {
-
- regions->reset();
-
- // ubidi only accepts utf16 (though internally it basically works on utf32 chars).
- // We want an ubidi_setPara(UBiDi*, UText*, UBiDiLevel, UBiDiLevel*, UErrorCode*);
- size_t utf8Bytes = fText.size();
- const char* utf8 = fText.c_str();
- uint8_t bidiLevel = fParagraphStyle.getTextDirection() == TextDirection::kLtr
- ? UBIDI_LTR
- : UBIDI_RTL;
- if (!SkTFitsIn<int32_t>(utf8Bytes)) {
- SkDEBUGF("Bidi error: text too long");
- return false;
- }
-
- // Getting the length like this seems to always set U_BUFFER_OVERFLOW_ERROR
- UErrorCode status = U_ZERO_ERROR;
- int32_t utf16Units;
- u_strFromUTF8(nullptr, 0, &utf16Units, utf8, utf8Bytes, &status);
- status = U_ZERO_ERROR;
- std::unique_ptr<UChar[]> utf16(new UChar[utf16Units]);
- u_strFromUTF8(utf16.get(), utf16Units, nullptr, utf8, utf8Bytes, &status);
- if (U_FAILURE(status)) {
- SkDEBUGF("Invalid utf8 input: %s", u_errorName(status));
- return false;
+TextIndex ParagraphImpl::findGraphemeStart(TextIndex index) {
+ if (index == fText.size()) {
+ return index;
}
-
- ICUBiDi bidi(ubidi_openSized(utf16Units, 0, &status));
- if (U_FAILURE(status)) {
- SkDEBUGF("Bidi error: %s", u_errorName(status));
- return false;
+ while (index > 0 &&
+ (fCodeUnitProperties[index] & CodeUnitFlags::kGraphemeStart) == 0) {
+ --index;
}
- SkASSERT(bidi);
+ return index;
+}
- // The required lifetime of utf16 isn't well documented.
- // It appears it isn't used after ubidi_setPara except through ubidi_getText.
- ubidi_setPara(bidi.get(), utf16.get(), utf16Units, bidiLevel, nullptr, &status);
- if (U_FAILURE(status)) {
- SkDEBUGF("Bidi error: %s", u_errorName(status));
- return false;
+void ParagraphImpl::ensureUTF16Mapping() {
+ if (!fUTF16IndexForUTF8Index.empty()) {
+ return;
}
+ // Fill out code points 16
+ auto ptr = fText.c_str();
+ auto end = fText.c_str() + fText.size();
+ while (ptr < end) {
- SkTArray<BidiRegion> bidiRegions;
- const char* start8 = utf8;
- const char* end8 = utf8 + utf8Bytes;
- TextRange textRange(0, 0);
- UBiDiLevel currentLevel = 0;
+ size_t index = ptr - fText.c_str();
+ SkUnichar u = SkUTF::NextUTF8(&ptr, end);
- int32_t pos16 = 0;
- int32_t end16 = ubidi_getLength(bidi.get());
- while (pos16 < end16) {
- auto level = ubidi_getLevelAt(bidi.get(), pos16);
- if (pos16 == 0) {
- currentLevel = level;
- } else if (level != currentLevel) {
- textRange.end = start8 - utf8;
- regions->emplace_back(textRange.start, textRange.end, currentLevel);
- currentLevel = level;
- textRange = TextRange(textRange.end, textRange.end);
+ // All utf8 units refer to the same codepoint
+ size_t next = ptr - fText.c_str();
+ for (auto i = index; i < next; ++i) {
+ fUTF16IndexForUTF8Index.emplace_back(fUTF8IndexForUTF16Index.size());
}
- SkUnichar u = utf8_next(&start8, end8);
- pos16 += SkUTF::ToUTF16(u);
- }
+ SkASSERT(fUTF16IndexForUTF8Index.size() == next);
- textRange.end = start8 - utf8;
- if (!textRange.empty()) {
- regions->emplace_back(textRange.start, textRange.end, currentLevel);
+ // One or two codepoints refer to the same text index
+ uint16_t buffer[2];
+ size_t count = SkUTF::ToUTF16(u, buffer);
+ fUTF8IndexForUTF16Index.emplace_back(index);
+ if (count > 1) {
+ fUTF8IndexForUTF16Index.emplace_back(index);
+ }
}
-
- return true;
+ fUTF16IndexForUTF8Index.emplace_back(fUTF8IndexForUTF16Index.size());
+ fUTF8IndexForUTF16Index.emplace_back(fText.size());
}
} // namespace textlayout
diff --git a/chromium/third_party/skia/modules/skparagraph/src/ParagraphImpl.h b/chromium/third_party/skia/modules/skparagraph/src/ParagraphImpl.h
index 36b8abf53ad..bda3d1fed3b 100644
--- a/chromium/third_party/skia/modules/skparagraph/src/ParagraphImpl.h
+++ b/chromium/third_party/skia/modules/skparagraph/src/ParagraphImpl.h
@@ -2,24 +2,59 @@
#ifndef ParagraphImpl_DEFINED
#define ParagraphImpl_DEFINED
-#include <unicode/brkiter.h>
-#include <unicode/ubidi.h>
-#include <unicode/unistr.h>
-#include <unicode/urename.h>
+#include "include/core/SkFont.h"
+#include "include/core/SkPaint.h"
#include "include/core/SkPicture.h"
-#include "include/private/SkMutex.h"
+#include "include/core/SkPoint.h"
+#include "include/core/SkRect.h"
+#include "include/core/SkRefCnt.h"
+#include "include/core/SkScalar.h"
+#include "include/core/SkString.h"
+#include "include/core/SkTypes.h"
+#include "include/private/SkBitmaskEnum.h"
+#include "include/private/SkTArray.h"
#include "include/private/SkTHash.h"
+#include "include/private/SkTemplates.h"
+#include "modules/skparagraph/include/DartTypes.h"
+#include "modules/skparagraph/include/FontCollection.h"
#include "modules/skparagraph/include/Paragraph.h"
+#include "modules/skparagraph/include/ParagraphCache.h"
#include "modules/skparagraph/include/ParagraphStyle.h"
+#include "modules/skparagraph/include/TextShadow.h"
#include "modules/skparagraph/include/TextStyle.h"
#include "modules/skparagraph/src/Run.h"
-#include "modules/skparagraph/src/TextLine.h"
+#include "src/core/SkSpan.h"
+
+#include <unicode/ubrk.h>
+#include <memory>
+#include <string>
+#include <vector>
class SkCanvas;
namespace skia {
namespace textlayout {
+enum CodeUnitFlags {
+ kNoCodeUnitFlag = 0x0,
+ kPartOfWhiteSpace = 0x1,
+ kGraphemeStart = 0x2,
+ kSoftLineBreakBefore = 0x4,
+ kHardLineBreakBefore = 0x8,
+};
+}
+}
+
+namespace sknonstd {
+template <> struct is_bitmask_enum<skia::textlayout::CodeUnitFlags> : std::true_type {};
+}
+
+namespace skia {
+namespace textlayout {
+
+class LineMetrics;
+class TextLine;
+
template <typename T> bool operator==(const SkSpan<T>& a, const SkSpan<T>& b) {
return a.size() == b.size() && a.begin() == b.begin();
}
@@ -56,45 +91,6 @@ struct BidiRegion {
uint8_t direction;
};
-class TextBreaker {
-public:
- TextBreaker() : fInitialized(false), fPos(-1) {}
-
- bool initialize(SkSpan<const char> text, UBreakIteratorType type);
-
- bool initialized() const { return fInitialized; }
-
- size_t first() {
- fPos = ubrk_first(fIterator.get());
- return eof() ? fSize : fPos;
- }
-
- size_t next() {
- fPos = ubrk_next(fIterator.get());
- return eof() ? fSize : fPos;
- }
-
- size_t preceding(size_t offset) {
- auto pos = ubrk_preceding(fIterator.get(), offset);
- return pos == icu::BreakIterator::DONE ? 0 : pos;
- }
-
- size_t following(size_t offset) {
- auto pos = ubrk_following(fIterator.get(), offset);
- return pos == icu::BreakIterator::DONE ? fSize : pos;
- }
-
- int32_t status() { return ubrk_getRuleStatus(fIterator.get()); }
-
- bool eof() { return fPos == icu::BreakIterator::DONE; }
-
-private:
- std::unique_ptr<UBreakIterator, SkFunctionWrapper<decltype(ubrk_close), ubrk_close>> fIterator;
- bool fInitialized;
- int32_t fPos;
- size_t fSize;
-};
-
class ParagraphImpl final : public Paragraph {
public:
@@ -142,9 +138,12 @@ public:
const ParagraphStyle& paragraphStyle() const { return fParagraphStyle; }
SkSpan<Cluster> clusters() { return SkSpan<Cluster>(fClusters.begin(), fClusters.size()); }
sk_sp<FontCollection> fontCollection() const { return fFontCollection; }
- const SkTHashSet<size_t>& graphemes() const { return fGraphemes; }
- SkSpan<Codepoint> codepoints(){ return SkSpan<Codepoint>(fCodePoints.begin(), fCodePoints.size()); }
void formatLines(SkScalar maxWidth);
+ void ensureUTF16Mapping();
+ TextIndex findGraphemeStart(TextIndex index);
+ size_t getUTF16Index(TextIndex index) {
+ return fUTF16IndexForUTF8Index[index];
+ }
bool strutEnabled() const { return paragraphStyle().getStrutStyle().getStrutEnabled(); }
bool strutForceHeight() const {
@@ -158,7 +157,16 @@ public:
SkSpan<const char> text(TextRange textRange);
SkSpan<Cluster> clusters(ClusterRange clusterRange);
Cluster& cluster(ClusterIndex clusterIndex);
- Run& run(RunIndex runIndex);
+ ClusterIndex clusterIndex(TextIndex textIndex) {
+ auto clusterIndex = this->fClustersIndexFromCodeUnit[textIndex];
+ SkASSERT(clusterIndex != EMPTY_INDEX);
+ return clusterIndex;
+ }
+ Run& run(RunIndex runIndex) {
+ SkASSERT(runIndex < fRuns.size());
+ return fRuns[runIndex];
+ }
+
Run& runByCluster(ClusterIndex clusterIndex);
SkSpan<Block> blocks(BlockRange blockRange);
Block& block(BlockIndex blockIndex);
@@ -176,8 +184,12 @@ public:
void resetContext();
void resolveStrut();
+
+ bool computeCodeUnitProperties();
+ bool computeWords();
+ bool getBidiRegions();
+
void buildClusterTable();
- void markLineBreaks();
void spaceGlyphs();
bool shapeTextIntoEndlessLine();
void breakShapedTextIntoLines(SkScalar maxWidth);
@@ -201,6 +213,12 @@ public:
}
}
+ using CodeUnitRangeVisitor = std::function<bool(TextRange textRange)>;
+ void forEachCodeUnitPropertyRange(CodeUnitFlags property, CodeUnitRangeVisitor visitor);
+ size_t getWhitespacesLength(TextRange textRange);
+
+ bool codeUnitHasProperty(size_t index, CodeUnitFlags property) const { return (fCodeUnitProperties[index] & property) == property; }
+
private:
friend class ParagraphBuilder;
friend class ParagraphCacheKey;
@@ -212,13 +230,8 @@ private:
void calculateBoundaries();
- void markGraphemes16();
- void markGraphemes();
-
void computeEmptyMetrics();
- bool calculateBidiRegions(SkTArray<BidiRegion>* regions);
-
// Input
SkTArray<StyleBlock<SkScalar>> fLetterSpaceStyles;
SkTArray<StyleBlock<SkScalar>> fWordSpaceStyles;
@@ -234,12 +247,17 @@ private:
InternalState fState;
SkTArray<Run, false> fRuns; // kShaped
SkTArray<Cluster, true> fClusters; // kClusterized (cached: text, word spacing, letter spacing, resolved fonts)
- SkTArray<Grapheme, true> fGraphemes16;
- SkTArray<Codepoint, true> fCodePoints;
- SkTHashSet<size_t> fGraphemes;
+ SkTArray<CodeUnitFlags> fCodeUnitProperties;
+ SkTArray<size_t> fClustersIndexFromCodeUnit;
+ std::vector<size_t> fWords;
+ SkTArray<BidiRegion> fBidiRegions;
+ // These two arrays are used in measuring methods (getRectsForRange, getGlyphPositionAtCoordinate)
+ // They are filled lazily whenever they need and cached
+ SkTArray<TextIndex, true> fUTF8IndexForUTF16Index;
+ SkTArray<size_t, true> fUTF16IndexForUTF8Index;
size_t fUnresolvedGlyphs;
- SkTArray<TextLine, true> fLines; // kFormatted (cached: width, max lines, ellipsis, text align)
+ SkTArray<TextLine, false> fLines; // kFormatted (cached: width, max lines, ellipsis, text align)
sk_sp<SkPicture> fPicture; // kRecorded (cached: text styles)
SkTArray<ResolvedFontDescriptor> fFontSwitches;
@@ -251,9 +269,9 @@ private:
SkScalar fOldHeight;
SkScalar fMaxWidthWithTrailingSpaces;
SkRect fOrigin;
- std::vector<size_t> fWords;
};
} // namespace textlayout
} // namespace skia
+
#endif // ParagraphImpl_DEFINED
diff --git a/chromium/third_party/skia/modules/skparagraph/src/ParagraphStyle.cpp b/chromium/third_party/skia/modules/skparagraph/src/ParagraphStyle.cpp
index 8327790a854..c40372dce02 100644
--- a/chromium/third_party/skia/modules/skparagraph/src/ParagraphStyle.cpp
+++ b/chromium/third_party/skia/modules/skparagraph/src/ParagraphStyle.cpp
@@ -1,7 +1,8 @@
// Copyright 2019 Google LLC.
-#include <string>
+
+#include "modules/skparagraph/include/DartTypes.h"
#include "modules/skparagraph/include/ParagraphStyle.h"
-#include "unicode/unistr.h"
+#include "modules/skparagraph/src/ParagraphUtil.h"
namespace skia {
namespace textlayout {
@@ -36,11 +37,7 @@ TextAlign ParagraphStyle::effective_align() const {
}
void ParagraphStyle::setEllipsis(const std::u16string& ellipsis) {
- icu::UnicodeString unicode;
- unicode.setTo((UChar*)ellipsis.data());
- std::string str;
- unicode.toUTF8String(str);
- fEllipsis = SkString(str.c_str());
+ fEllipsis = SkStringFromU16String(ellipsis);
}
} // namespace textlayout
} // namespace skia
diff --git a/chromium/third_party/skia/modules/skparagraph/src/ParagraphUtil.cpp b/chromium/third_party/skia/modules/skparagraph/src/ParagraphUtil.cpp
new file mode 100644
index 00000000000..d78ad656878
--- /dev/null
+++ b/chromium/third_party/skia/modules/skparagraph/src/ParagraphUtil.cpp
@@ -0,0 +1,34 @@
+// Copyright 2019 Google LLC.
+
+#include "include/core/SkString.h"
+#include "include/core/SkTypes.h"
+#include "include/private/SkTo.h"
+#include "modules/skparagraph/src/ParagraphUtil.h"
+
+#include <unicode/umachine.h>
+#include <unicode/ustring.h>
+#include <unicode/utypes.h>
+#include <string>
+
+namespace skia {
+namespace textlayout {
+
+SkString SkStringFromU16String(const std::u16string& utf16text) {
+ SkString dst;
+ UErrorCode status = U_ZERO_ERROR;
+ int32_t dstSize;
+ // Getting the length like this seems to always set U_BUFFER_OVERFLOW_ERROR
+ u_strToUTF8(nullptr, 0, &dstSize, (UChar*)utf16text.data(), SkToS32(utf16text.size()), &status);
+ dst.resize(dstSize);
+ status = U_ZERO_ERROR;
+ u_strToUTF8(dst.writable_str(), dst.size(), nullptr,
+ (UChar*)utf16text.data(), SkToS32(utf16text.size()), &status);
+ if (U_FAILURE(status)) {
+ SkDEBUGF("Invalid UTF-16 input: %s", u_errorName(status));
+ return dst;
+ }
+ return dst;
+}
+
+}
+}
diff --git a/chromium/third_party/skia/modules/skparagraph/src/ParagraphUtil.h b/chromium/third_party/skia/modules/skparagraph/src/ParagraphUtil.h
new file mode 100644
index 00000000000..a32025ab805
--- /dev/null
+++ b/chromium/third_party/skia/modules/skparagraph/src/ParagraphUtil.h
@@ -0,0 +1,14 @@
+// Copyright 2020 Google LLC.
+#ifndef ParagraphUtil_DEFINED
+#define ParagraphUtil_DEFINED
+
+#include "include/core/SkString.h"
+#include <string>
+
+namespace skia {
+namespace textlayout {
+SkString SkStringFromU16String(const std::u16string& utf16text);
+}
+}
+
+#endif
diff --git a/chromium/third_party/skia/modules/skparagraph/src/Run.cpp b/chromium/third_party/skia/modules/skparagraph/src/Run.cpp
index e679be8648c..80c5222f05d 100644
--- a/chromium/third_party/skia/modules/skparagraph/src/Run.cpp
+++ b/chromium/third_party/skia/modules/skparagraph/src/Run.cpp
@@ -1,35 +1,32 @@
// Copyright 2019 Google LLC.
-#include "modules/skparagraph/src/Run.h"
-#include <unicode/brkiter.h>
#include "include/core/SkFontMetrics.h"
+#include "include/core/SkTextBlob.h"
+#include "include/private/SkFloatingPoint.h"
+#include "include/private/SkMalloc.h"
+#include "include/private/SkTo.h"
+#include "modules/skparagraph/include/DartTypes.h"
+#include "modules/skparagraph/include/TextStyle.h"
#include "modules/skparagraph/src/ParagraphImpl.h"
-#include <algorithm>
+#include "modules/skparagraph/src/Run.h"
+#include "modules/skshaper/include/SkShaper.h"
#include "src/utils/SkUTF.h"
-namespace {
-
-SkUnichar utf8_next(const char** ptr, const char* end) {
- SkUnichar val = SkUTF::NextUTF8(ptr, end);
- return val < 0 ? 0xFFFD : val;
-}
-
-}
-
namespace skia {
namespace textlayout {
Run::Run(ParagraphImpl* master,
const SkShaper::RunHandler::RunInfo& info,
size_t firstChar,
- SkScalar lineHeight,
+ SkScalar heightMultiplier,
size_t index,
SkScalar offsetX)
- : fMaster(master)
- , fTextRange(firstChar + info.utf8Range.begin(), firstChar + info.utf8Range.end())
- , fClusterRange(EMPTY_CLUSTERS)
- , fClusterStart(firstChar) {
- fFont = info.fFont;
- fHeightMultiplier = lineHeight;
+ : fMaster(master)
+ , fTextRange(firstChar + info.utf8Range.begin(), firstChar + info.utf8Range.end())
+ , fClusterRange(EMPTY_CLUSTERS)
+ , fFont(info.fFont)
+ , fClusterStart(firstChar)
+ , fHeightMultiplier(heightMultiplier)
+{
fBidiLevel = info.fBidiLevel;
fAdvance = info.fAdvance;
fIndex = index;
@@ -38,21 +35,34 @@ Run::Run(ParagraphImpl* master,
fGlyphs.push_back_n(info.glyphCount);
fBounds.push_back_n(info.glyphCount);
fPositions.push_back_n(info.glyphCount + 1);
- fOffsets.push_back_n(info.glyphCount + 1);
fClusterIndexes.push_back_n(info.glyphCount + 1);
fShifts.push_back_n(info.glyphCount + 1, 0.0);
info.fFont.getMetrics(&fFontMetrics);
+
+ this->calculateMetrics();
+
fSpaced = false;
// To make edge cases easier:
fPositions[info.glyphCount] = fOffset + fAdvance;
- fOffsets[info.glyphCount] = { 0, 0};
fClusterIndexes[info.glyphCount] = this->leftToRight() ? info.utf8Range.end() : info.utf8Range.begin();
fEllipsis = false;
fPlaceholderIndex = std::numeric_limits<size_t>::max();
}
+void Run::calculateMetrics() {
+ fCorrectAscent = fFontMetrics.fAscent - fFontMetrics.fLeading * 0.5;
+ fCorrectDescent = fFontMetrics.fDescent + fFontMetrics.fLeading * 0.5;
+ fCorrectLeading = 0;
+ if (!SkScalarNearlyZero(fHeightMultiplier)) {
+ auto multiplier = fHeightMultiplier * fFont.getSize() /
+ (fFontMetrics.fDescent - fFontMetrics.fAscent + fFontMetrics.fLeading);
+ fCorrectAscent *= multiplier;
+ fCorrectDescent *= multiplier;
+ }
+}
+
SkShaper::RunHandler::Buffer Run::newRunBuffer() {
- return {fGlyphs.data(), fPositions.data(), fOffsets.data(), fClusterIndexes.data(), fOffset};
+ return {fGlyphs.data(), fPositions.data(), nullptr, fClusterIndexes.data(), fOffset};
}
void Run::commit() {
@@ -74,81 +84,46 @@ SkScalar Run::calculateWidth(size_t start, size_t end, bool clip) const {
return posX(end) - posX(start) + shift + correction;
}
-void Run::copyTo(SkTextBlobBuilder& builder, size_t pos, size_t size, SkVector runOffset) const {
+void Run::copyTo(SkTextBlobBuilder& builder, size_t pos, size_t size) const {
SkASSERT(pos + size <= this->size());
const auto& blobBuffer = builder.allocRunPos(fFont, SkToInt(size));
sk_careful_memcpy(blobBuffer.glyphs, fGlyphs.data() + pos, size * sizeof(SkGlyphID));
- for (size_t i = 0; i < size; ++i) {
- auto point = fPositions[i + pos];
- auto offset = fOffsets[i + pos];
- point.offset(offset.fX, offset.fY);
- if (fSpaced) {
- point.fX += fShifts[i + pos];
- }
- if (!fJustificationShifts.empty()) {
- point.fX += fJustificationShifts[i + pos].fX;
- }
- blobBuffer.points()[i] = point + runOffset;
- }
-}
-
-std::tuple<bool, ClusterIndex, ClusterIndex> Run::findLimitingClusters(TextRange text, bool extendToClusters) const {
-
- if (text.width() == 0) {
- for (auto i = fClusterRange.start; i != fClusterRange.end; ++i) {
- auto& cluster = fMaster->cluster(i);
- if (cluster.textRange().end >= text.end && cluster.textRange().start <= text.start) {
- return std::make_tuple(true, i, i);
- }
- }
- return std::make_tuple(false, 0, 0);
- }
- Cluster* start = nullptr;
- Cluster* end = nullptr;
- if (extendToClusters) {
- for (auto i = fClusterRange.start; i != fClusterRange.end; ++i) {
- auto& cluster = fMaster->cluster(i);
- auto clusterRange = cluster.textRange();
- if (clusterRange.end <= text.start) {
- continue;
- } else if (clusterRange.start >= text.end) {
- break;
- }
- TextRange s = TextRange(std::max(clusterRange.start, text.start),
- std::min(clusterRange.end, text.end));
- if (s.width() > 0) {
- if (start == nullptr) {
- start = &cluster;
- }
- end = &cluster;
- }
- }
+ if (!fSpaced && fJustificationShifts.empty()) {
+ sk_careful_memcpy(blobBuffer.points(), fPositions.data() + pos, size * sizeof(SkPoint));
} else {
- // We need this branch when we draw styles for the part of a cluster
- for (auto i = fClusterRange.start; i != fClusterRange.end; ++i) {
- auto& cluster = fMaster->cluster(i);
- if (cluster.textRange().end > text.start && start == nullptr) {
- start = &cluster;
+ for (size_t i = 0; i < size; ++i) {
+ auto point = fPositions[i + pos];
+ if (fSpaced) {
+ point.fX += fShifts[i + pos];
}
- if (cluster.textRange().start < text.end) {
- end = &cluster;
- } else {
- break;
+ if (!fJustificationShifts.empty()) {
+ point.fX += fJustificationShifts[i + pos].fX;
}
+ blobBuffer.points()[i] = point;
}
}
+}
- if (start == nullptr || end == nullptr) {
- return std::make_tuple(false, 0, 0);
+// Find a cluster range from text range (within one run)
+// Cluster range is normalized ([start:end) start < end regardless of TextDirection
+// Boolean value in triple indicates whether the cluster range was found or not
+std::tuple<bool, ClusterIndex, ClusterIndex> Run::findLimitingClusters(TextRange text) const {
+ if (text.width() == 0) {
+ // Special Flutter case for "\n" and "...\n"
+ if (text.end > this->fTextRange.start) {
+ ClusterIndex index = fMaster->clusterIndex(text.end - 1);
+ return std::make_tuple(true, index, index);
+ } else {
+ return std::make_tuple(false, 0, 0);
+ }
}
+ ClusterIndex startIndex = fMaster->clusterIndex(text.start);
+ ClusterIndex endIndex = fMaster->clusterIndex(text.end - 1);
if (!leftToRight()) {
- std::swap(start, end);
+ std::swap(startIndex, endIndex);
}
-
- size_t startIndex = start - fMaster->clusters().begin();
- size_t endIndex = end - fMaster->clusters().begin();
return std::make_tuple(startIndex != fClusterRange.end && endIndex != fClusterRange.end, startIndex, endIndex);
}
@@ -307,25 +282,12 @@ void Run::updateMetrics(InternalLineMetrics* endlineMetrics) {
break;
}
+ this->calculateMetrics();
+
// Make sure the placeholder can fit the line
endlineMetrics->add(this);
}
-void Cluster::setIsWhiteSpaces() {
-
- fWhiteSpaces = false;
-
- auto span = fMaster->text(fTextRange);
- const char* ch = span.begin();
- while (ch < span.end()) {
- auto unichar = utf8_next(&ch, span.end());
- if (!u_isWhitespace(unichar)) {
- return;
- }
- }
- fWhiteSpaces = true;
-}
-
SkScalar Cluster::sizeToChar(TextIndex ch) const {
if (ch < fTextRange.start || ch >= fTextRange.end) {
return 0;
@@ -382,6 +344,18 @@ SkFont Cluster::font() const {
return fMaster->run(fRunIndex).font();
}
+bool Cluster::isHardBreak() const {
+ return fMaster->codeUnitHasProperty(fTextRange.end,CodeUnitFlags::kHardLineBreakBefore);
+}
+
+bool Cluster::isSoftBreak() const {
+ return fMaster->codeUnitHasProperty(fTextRange.end,CodeUnitFlags::kSoftLineBreakBefore);
+}
+
+bool Cluster::isGraphemeBreak() const {
+ return fMaster->codeUnitHasProperty(fTextRange.end,CodeUnitFlags::kGraphemeStart);
+}
+
Cluster::Cluster(ParagraphImpl* master,
RunIndex runIndex,
size_t start,
@@ -398,9 +372,9 @@ Cluster::Cluster(ParagraphImpl* master,
, fWidth(width)
, fSpacing(0)
, fHeight(height)
- , fHalfLetterSpacing(0.0)
- , fWhiteSpaces(false)
- , fBreakType(None) {
+ , fHalfLetterSpacing(0.0) {
+ size_t len = fMaster->getWhitespacesLength(fTextRange);
+ fIsWhiteSpaces = (len == this->fTextRange.width());
}
} // namespace textlayout
diff --git a/chromium/third_party/skia/modules/skparagraph/src/Run.h b/chromium/third_party/skia/modules/skparagraph/src/Run.h
index df61276e3ee..c50be46df55 100644
--- a/chromium/third_party/skia/modules/skparagraph/src/Run.h
+++ b/chromium/third_party/skia/modules/skparagraph/src/Run.h
@@ -2,21 +2,32 @@
#ifndef Run_DEFINED
#define Run_DEFINED
+#include "include/core/SkFont.h"
#include "include/core/SkFontMetrics.h"
#include "include/core/SkPoint.h"
-#include "include/core/SkTextBlob.h"
+#include "include/core/SkRect.h"
+#include "include/core/SkScalar.h"
+#include "include/core/SkTypes.h"
+#include "include/private/SkTArray.h"
#include "modules/skparagraph/include/DartTypes.h"
#include "modules/skparagraph/include/TextStyle.h"
#include "modules/skshaper/include/SkShaper.h"
#include "src/core/SkSpan.h"
-#include <functional> // std::function
+
+#include <math.h>
+#include <algorithm>
+#include <functional>
+#include <limits>
+#include <tuple>
+
+class SkTextBlobBuilder;
namespace skia {
namespace textlayout {
-class ParagraphImpl;
class Cluster;
-class Run;
+class InternalLineMetrics;
+class ParagraphImpl;
typedef size_t RunIndex;
const size_t EMPTY_RUN = EMPTY_INDEX;
@@ -29,9 +40,6 @@ const SkRange<size_t> EMPTY_CLUSTERS = EMPTY_RANGE;
typedef size_t GraphemeIndex;
typedef SkRange<GraphemeIndex> GraphemeRange;
-typedef size_t CodepointIndex;
-typedef SkRange<CodepointIndex> CodepointRange;
-
typedef size_t GlyphIndex;
typedef SkRange<GlyphIndex> GlyphRange;
@@ -44,31 +52,27 @@ class DirText {
size_t end;
};
-class InternalLineMetrics;
class Run {
public:
- Run() = default;
Run(ParagraphImpl* master,
const SkShaper::RunHandler::RunInfo& info,
size_t firstChar,
- SkScalar lineHeight,
+ SkScalar heightMultiplier,
size_t index,
SkScalar shiftX);
- ~Run() {}
+ Run(const Run&) = default;
+ Run& operator=(const Run&) = delete;
+ Run(Run&&) = default;
+ Run& operator=(Run&&) = delete;
+ ~Run() = default;
void setMaster(ParagraphImpl* master) { fMaster = master; }
SkShaper::RunHandler::Buffer newRunBuffer();
- SkScalar posX(size_t index) const {
- return fPositions[index].fX + fOffsets[index].fX;
- }
- void addX(size_t index, SkScalar shift) {
- fPositions[index].fX += shift;
- }
- SkScalar posY(size_t index) const {
- return fPositions[index].fY + fOffsets[index].fY;
- }
+ SkScalar posX(size_t index) const { return fPositions[index].fX; }
+ void addX(size_t index, SkScalar shift) { fPositions[index].fX += shift; }
+ SkScalar posY(size_t index) const { return fPositions[index].fY; }
size_t size() const { return fGlyphs.size(); }
void setWidth(SkScalar width) { fAdvance.fX = width; }
void setHeight(SkScalar height) { fAdvance.fY = height; }
@@ -77,36 +81,15 @@ public:
fOffset.fY += shiftY;
}
SkVector advance() const {
- return SkVector::Make(fAdvance.fX, fFontMetrics.fDescent - fFontMetrics.fAscent);
+ return SkVector::Make(fAdvance.fX, fFontMetrics.fDescent - fFontMetrics.fAscent + fFontMetrics.fLeading);
}
SkVector offset() const { return fOffset; }
SkScalar ascent() const { return fFontMetrics.fAscent; }
SkScalar descent() const { return fFontMetrics.fDescent; }
SkScalar leading() const { return fFontMetrics.fLeading; }
- SkScalar correctAscent() const {
-
- if (fHeightMultiplier == 0) {
- return fFontMetrics.fAscent - fFontMetrics.fLeading / 2;
- }
- return fFontMetrics.fAscent * fHeightMultiplier * fFont.getSize() /
- (fFontMetrics.fDescent - fFontMetrics.fAscent + fFontMetrics.fLeading / 2);
- }
- SkScalar correctDescent() const {
-
- if (fHeightMultiplier == 0) {
- return fFontMetrics.fDescent + fFontMetrics.fLeading / 2;
- }
- return fFontMetrics.fDescent * fHeightMultiplier * fFont.getSize() /
- (fFontMetrics.fDescent - fFontMetrics.fAscent + fFontMetrics.fLeading / 2);
- }
- SkScalar correctLeading() const {
-
- if (fHeightMultiplier == 0) {
- return fFontMetrics.fAscent;
- }
- return fFontMetrics.fLeading * fHeightMultiplier * fFont.getSize() /
- (fFontMetrics.fDescent - fFontMetrics.fAscent + fFontMetrics.fLeading);
- }
+ SkScalar correctAscent() const { return fCorrectAscent; }
+ SkScalar correctDescent() const { return fCorrectDescent; }
+ SkScalar correctLeading() const { return fCorrectLeading; }
const SkFont& font() const { return fFont; }
bool leftToRight() const { return fBidiLevel % 2 == 0; }
TextDirection getTextDirection() const { return leftToRight() ? TextDirection::kLtr : TextDirection::kRtl; }
@@ -125,6 +108,7 @@ public:
bool isEllipsis() const { return fEllipsis; }
+ void calculateMetrics();
void updateMetrics(InternalLineMetrics* endlineMetrics);
void setClusterRange(size_t from, size_t to) { fClusterRange = ClusterRange(from, to); }
@@ -145,7 +129,7 @@ public:
}
SkScalar calculateWidth(size_t start, size_t end, bool clip) const;
- void copyTo(SkTextBlobBuilder& builder, size_t pos, size_t size, SkVector offset) const;
+ void copyTo(SkTextBlobBuilder& builder, size_t pos, size_t size) const;
using ClusterTextVisitor = std::function<void(size_t glyphStart,
size_t glyphEnd,
@@ -158,16 +142,13 @@ public:
using ClusterVisitor = std::function<void(Cluster* cluster)>;
void iterateThroughClusters(const ClusterVisitor& visitor);
- std::tuple<bool, ClusterIndex, ClusterIndex> findLimitingClusters(TextRange text, bool onlyInnerClusters) const;
+ std::tuple<bool, ClusterIndex, ClusterIndex> findLimitingClusters(TextRange text) const;
SkSpan<const SkGlyphID> glyphs() const {
return SkSpan<const SkGlyphID>(fGlyphs.begin(), fGlyphs.size());
}
SkSpan<const SkPoint> positions() const {
return SkSpan<const SkPoint>(fPositions.begin(), fPositions.size());
}
- SkSpan<const SkPoint> offsets() const {
- return SkSpan<const SkPoint>(fOffsets.begin(), fOffsets.size());
- }
SkSpan<const uint32_t> clusterIndexes() const {
return SkSpan<const uint32_t>(fClusterIndexes.begin(), fClusterIndexes.size());
}
@@ -197,12 +178,8 @@ private:
ClusterRange fClusterRange;
SkFont fFont;
- SkFontMetrics fFontMetrics;
- SkScalar fHeightMultiplier;
size_t fPlaceholderIndex;
- bool fEllipsis;
size_t fIndex;
- uint8_t fBidiLevel;
SkVector fAdvance;
SkVector fOffset;
TextIndex fClusterStart;
@@ -210,29 +187,20 @@ private:
SkSTArray<128, SkGlyphID, true> fGlyphs;
SkSTArray<128, SkPoint, true> fPositions;
SkSTArray<128, SkPoint, true> fJustificationShifts; // For justification (current and prev shifts)
- SkSTArray<128, SkPoint, true> fOffsets;
SkSTArray<128, uint32_t, true> fClusterIndexes;
SkSTArray<128, SkRect, true> fBounds;
SkSTArray<128, SkScalar, true> fShifts; // For formatting (letter/word spacing)
- bool fSpaced;
-};
-
-struct Codepoint {
- Codepoint(GraphemeIndex graphemeIndex, TextIndex textIndex, size_t index)
- : fGrapheme(graphemeIndex), fTextIndex(textIndex), fIndex(index) { }
-
- GraphemeIndex fGrapheme;
- TextIndex fTextIndex; // Used for getGlyphPositionAtCoordinate
- size_t fIndex;
-};
+ SkFontMetrics fFontMetrics;
+ const SkScalar fHeightMultiplier;
+ SkScalar fCorrectAscent;
+ SkScalar fCorrectDescent;
+ SkScalar fCorrectLeading;
-struct Grapheme {
- Grapheme(CodepointRange codepoints, TextRange textRange)
- : fCodepointRange(codepoints), fTextRange(textRange) { }
- CodepointRange fCodepointRange;
- TextRange fTextRange; // Used for getRectsForRange
+ bool fSpaced;
+ bool fEllipsis;
+ uint8_t fBidiLevel;
};
class Cluster {
@@ -254,9 +222,7 @@ public:
, fWidth()
, fSpacing(0)
, fHeight()
- , fHalfLetterSpacing(0.0)
- , fWhiteSpaces(false)
- , fBreakType(None) {}
+ , fHalfLetterSpacing(0.0) {}
Cluster(ParagraphImpl* master,
RunIndex runIndex,
@@ -281,14 +247,11 @@ public:
fWidth += shift;
}
- void setBreakType(BreakType type) { fBreakType = type; }
- bool isWhitespaces() const { return fWhiteSpaces; }
- bool canBreakLineAfter() const {
- return fBreakType == SoftLineBreak || fBreakType == HardLineBreak;
- }
- bool isHardBreak() const { return fBreakType == HardLineBreak; }
- bool isSoftBreak() const { return fBreakType == SoftLineBreak; }
- bool isGraphemeBreak() const { return fBreakType == GraphemeBreak; }
+ bool isWhitespaces() const { return fIsWhiteSpaces; }
+ bool isHardBreak() const;
+ bool isSoftBreak() const;
+ bool isGraphemeBreak() const;
+ bool canBreakLineAfter() const { return isHardBreak() || isSoftBreak(); }
size_t startPos() const { return fStart; }
size_t endPos() const { return fEnd; }
SkScalar width() const { return fWidth; }
@@ -308,8 +271,6 @@ public:
SkScalar trimmedWidth(size_t pos) const;
- void setIsWhiteSpaces();
-
bool contains(TextIndex ch) const { return ch >= fTextRange.start && ch < fTextRange.end; }
bool belongs(TextRange text) const {
@@ -335,8 +296,7 @@ private:
SkScalar fSpacing;
SkScalar fHeight;
SkScalar fHalfLetterSpacing;
- bool fWhiteSpaces;
- BreakType fBreakType;
+ bool fIsWhiteSpaces;
};
class InternalLineMetrics {
diff --git a/chromium/third_party/skia/modules/skparagraph/src/TextLine.cpp b/chromium/third_party/skia/modules/skparagraph/src/TextLine.cpp
index ce9b1a97955..128310366c3 100644
--- a/chromium/third_party/skia/modules/skparagraph/src/TextLine.cpp
+++ b/chromium/third_party/skia/modules/skparagraph/src/TextLine.cpp
@@ -1,13 +1,34 @@
// Copyright 2019 Google LLC.
-#include "modules/skparagraph/src/TextLine.h"
-#include <unicode/brkiter.h>
-#include <unicode/ubidi.h>
+#include "include/core/SkBlurTypes.h"
+#include "include/core/SkCanvas.h"
+#include "include/core/SkFont.h"
+#include "include/core/SkFontMetrics.h"
+#include "include/core/SkMaskFilter.h"
+#include "include/core/SkPaint.h"
+#include "include/core/SkString.h"
+#include "include/core/SkTextBlob.h"
+#include "include/core/SkTypes.h"
+#include "include/private/SkTemplates.h"
+#include "include/private/SkTo.h"
+#include "modules/skparagraph/include/DartTypes.h"
+#include "modules/skparagraph/include/Metrics.h"
+#include "modules/skparagraph/include/ParagraphStyle.h"
+#include "modules/skparagraph/include/TextShadow.h"
+#include "modules/skparagraph/include/TextStyle.h"
#include "modules/skparagraph/src/Decorations.h"
#include "modules/skparagraph/src/ParagraphImpl.h"
+#include "modules/skparagraph/src/TextLine.h"
+#include "modules/skshaper/include/SkShaper.h"
+#include "src/core/SkSpan.h"
-#include "include/core/SkMaskFilter.h"
-#include "include/effects/SkDashPathEffect.h"
-#include "include/effects/SkDiscretePathEffect.h"
+#include <unicode/ubidi.h>
+#include <algorithm>
+#include <iterator>
+#include <limits>
+#include <map>
+#include <tuple>
+#include <type_traits>
+#include <utility>
namespace skia {
namespace textlayout {
@@ -107,16 +128,24 @@ TextLine::TextLine(ParagraphImpl* master,
}
// Get the logical order
- std::vector<UBiDiLevel> runLevels;
+
+ // This is just chosen to catch the common/fast cases. Feel free to tweak.
+ constexpr int kPreallocCount = 4;
+
+ SkAutoSTArray<kPreallocCount, UBiDiLevel> runLevels(numRuns);
+
+ size_t runLevelsIndex = 0;
for (auto runIndex = start.runIndex(); runIndex <= end.runIndex(); ++runIndex) {
auto& run = fMaster->run(runIndex);
- runLevels.emplace_back(run.fBidiLevel);
- fMaxRunMetrics.add(InternalLineMetrics(run.fFontMetrics.fAscent, run.fFontMetrics.fDescent, run.fFontMetrics.fLeading));
+ runLevels[runLevelsIndex++] = run.fBidiLevel;
+ fMaxRunMetrics.add(InternalLineMetrics(run.fFontMetrics.fAscent, run.fFontMetrics.fDescent,
+ run.fFontMetrics.fLeading));
}
+ SkASSERT(runLevelsIndex == numRuns);
- std::vector<int32_t> logicalOrder(numRuns);
- ubidi_reorderVisual(runLevels.data(), SkToU32(numRuns), logicalOrder.data());
+ SkAutoSTArray<kPreallocCount, int32_t> logicalOrder(numRuns);
+ ubidi_reorderVisual(runLevels.data(), SkToU32(numRuns), logicalOrder.data());
auto firstRunIndex = start.runIndex();
for (auto index : logicalOrder) {
fRunsInVisualOrder.push_back(firstRunIndex + index);
@@ -189,8 +218,7 @@ SkRect TextLine::calculateBoundaries() {
boundaries.fBottom += shadowRect.fBottom;
}
- boundaries.offset(this->fOffset); // Line offset from the beginning of the para
- boundaries.offset(this->fShift, 0); // Shift produced by formatting
+ boundaries.offset(this->offset()); // Line offset from the beginning of the para
boundaries.offset(0, this->baseline()); // Down by baseline
return boundaries;
@@ -201,9 +229,6 @@ void TextLine::paint(SkCanvas* textCanvas) {
return;
}
- textCanvas->save();
- textCanvas->translate(this->offset().fX, this->offset().fY);
-
if (fHasBackground) {
this->iterateThroughVisualRuns(false,
[textCanvas, this]
@@ -257,8 +282,6 @@ void TextLine::paint(SkCanvas* textCanvas) {
return true;
});
}
-
- textCanvas->restore();
}
void TextLine::format(TextAlign align, SkScalar maxWidth) {
@@ -345,22 +368,25 @@ void TextLine::paintText(SkCanvas* canvas, TextRange textRange, const TextStyle&
}
// TODO: This is the change for flutter, must be removed later
- SkScalar correctedBaseline = SkScalarFloorToScalar(this->baseline() + 0.5);
SkTextBlobBuilder builder;
- context.run->copyTo(builder, SkToU32(context.pos), context.size, SkVector::Make(0, correctedBaseline));
- canvas->save();
+ context.run->copyTo(builder, SkToU32(context.pos), context.size);
if (context.clippingNeeded) {
- canvas->clipRect(extendHeight(context));
+ canvas->save();
+ canvas->clipRect(extendHeight(context).makeOffset(this->offset()));
}
- canvas->translate(context.fTextShift, 0);
- canvas->drawTextBlob(builder.make(), 0, 0, paint);
- canvas->restore();
+ SkScalar correctedBaseline = SkScalarFloorToScalar(this->baseline() + 0.5);
+ canvas->drawTextBlob(builder.make(),
+ this->offset().fX + context.fTextShift, this->offset().fY + correctedBaseline, paint);
+
+ if (context.clippingNeeded) {
+ canvas->restore();
+ }
}
void TextLine::paintBackground(SkCanvas* canvas, TextRange textRange, const TextStyle& style, const ClipContext& context) const {
if (style.hasBackground()) {
- canvas->drawRect(context.clip, style.getBackground());
+ canvas->drawRect(context.clip.makeOffset(this->offset()), style.getBackground());
}
}
@@ -378,25 +404,32 @@ void TextLine::paintShadow(SkCanvas* canvas, TextRange textRange, const TextStyl
}
SkTextBlobBuilder builder;
- context.run->copyTo(builder, context.pos, context.size, SkVector::Make(0, shiftDown));
- canvas->save();
- SkRect clip = context.clip;
- clip.offset(shadow.fOffset);
+ context.run->copyTo(builder, context.pos, context.size);
+
if (context.clippingNeeded) {
- canvas->clipRect(extendHeight(context));
+ canvas->save();
+ SkRect clip = extendHeight(context);
+ clip.offset(this->offset());
+ canvas->clipRect(clip);
+ }
+ canvas->drawTextBlob(builder.make(),
+ this->offset().fX + shadow.fOffset.x() + context.fTextShift,
+ this->offset().fY + shadow.fOffset.y() + shiftDown,
+ paint);
+
+ if (context.clippingNeeded) {
+ canvas->restore();
}
- canvas->translate(context.fTextShift, 0);
- canvas->drawTextBlob(builder.make(), shadow.fOffset.x(), shadow.fOffset.y(), paint);
- canvas->restore();
}
}
void TextLine::paintDecorations(SkCanvas* canvas, TextRange textRange, const TextStyle& style, const ClipContext& context) const {
+ SkAutoCanvasRestore acr(canvas, true);
+ canvas->translate(this->offset().fX, this->offset().fY);
Decorations decorations;
SkScalar correctedBaseline = SkScalarFloorToScalar(this->baseline() + 0.5);
- decorations.paint(canvas, style, context, correctedBaseline, this->fShift);
-
+ decorations.paint(canvas, style, context, correctedBaseline, this->offset());
}
void TextLine::justify(SkScalar maxWidth) {
@@ -489,7 +522,7 @@ void TextLine::createEllipsis(SkScalar maxWidth, const SkString& ellipsis, bool)
auto attachEllipsis = [&](const Cluster* cluster){
// Shape the ellipsis
- Run* run = shapeEllipsis(ellipsis, cluster->run());
+ std::unique_ptr<Run> run = shapeEllipsis(ellipsis, cluster->run());
run->fClusterStart = cluster->textRange().start;
run->setMaster(fMaster);
@@ -500,7 +533,7 @@ void TextLine::createEllipsis(SkScalar maxWidth, const SkString& ellipsis, bool)
return false;
}
- fEllipsis = std::make_shared<Run>(*run);
+ fEllipsis = std::move(run);
fEllipsis->shift(width, 0);
fAdvance.fX = width;
return true;
@@ -517,13 +550,14 @@ void TextLine::createEllipsis(SkScalar maxWidth, const SkString& ellipsis, bool)
}
}
-Run* TextLine::shapeEllipsis(const SkString& ellipsis, Run* run) {
+std::unique_ptr<Run> TextLine::shapeEllipsis(const SkString& ellipsis, Run* run) {
class ShapeHandler final : public SkShaper::RunHandler {
public:
ShapeHandler(SkScalar lineHeight, const SkString& ellipsis)
: fRun(nullptr), fLineHeight(lineHeight), fEllipsis(ellipsis) {}
- Run* run() { return fRun; }
+ Run* run() & { return fRun.get(); }
+ std::unique_ptr<Run> run() && { return std::move(fRun); }
private:
void beginLine() override {}
@@ -533,7 +567,8 @@ Run* TextLine::shapeEllipsis(const SkString& ellipsis, Run* run) {
void commitRunInfo() override {}
Buffer runBuffer(const RunInfo& info) override {
- fRun = new Run(nullptr, info, 0, fLineHeight, 0, 0);
+ SkASSERT(!fRun);
+ fRun = std::unique_ptr<Run>(new Run(nullptr, info, 0, fLineHeight, 0, 0));
return fRun->newRunBuffer();
}
@@ -546,7 +581,7 @@ Run* TextLine::shapeEllipsis(const SkString& ellipsis, Run* run) {
void commitLine() override {}
- Run* fRun;
+ std::unique_ptr<Run> fRun;
SkScalar fLineHeight;
SkString fEllipsis;
};
@@ -558,7 +593,7 @@ Run* TextLine::shapeEllipsis(const SkString& ellipsis, Run* run) {
std::numeric_limits<SkScalar>::max(), &handler);
handler.run()->fTextRange = TextRange(0, ellipsis.size());
handler.run()->fMaster = fMaster;
- return handler.run();
+ return std::move(handler).run();
}
TextLine::ClipContext TextLine::measureTextInsideOneRun(TextRange textRange,
@@ -593,7 +628,7 @@ TextLine::ClipContext TextLine::measureTextInsideOneRun(TextRange textRange,
bool found;
ClusterIndex startIndex;
ClusterIndex endIndex;
- std::tie(found, startIndex, endIndex) = run->findLimitingClusters(textRange, limitToClusters);
+ std::tie(found, startIndex, endIndex) = run->findLimitingClusters(textRange);
if (!found) {
SkASSERT(textRange.empty() || limitToClusters);
return result;
@@ -647,6 +682,12 @@ TextLine::ClipContext TextLine::measureTextInsideOneRun(TextRange textRange,
result.clip.fRight = fAdvance.fX;
}
+ if (result.clip.width() < 0) {
+ // Weird situation when glyph offsets move the glyph to the left
+ // (happens with zalgo texts, for instance)
+ result.clip.fRight = result.clip.fLeft;
+ }
+
// The text must be aligned with the lineOffset
result.fTextShift = textStartInLine - textStartInRun;
@@ -658,7 +699,9 @@ void TextLine::iterateThroughClustersInGlyphsOrder(bool reversed,
const ClustersVisitor& visitor) const {
// Walk through the clusters in the logical order (or reverse)
SkSpan<const size_t> runs(fRunsInVisualOrder.data(), fRunsInVisualOrder.size());
- directional_for_each(runs, reversed, [&](decltype(runs[0]) r) {
+ bool ignore = false;
+ directional_for_each(runs, !reversed, [&](decltype(runs[0]) r) {
+ if (ignore) return;
auto run = this->fMaster->run(r);
auto trimmedRange = fClusterRange.intersection(run.clusterRange());
auto trailedRange = fGhostClusterRange.intersection(run.clusterRange());
@@ -666,12 +709,10 @@ void TextLine::iterateThroughClustersInGlyphsOrder(bool reversed,
auto trailed = fMaster->clusters(trailedRange);
auto trimmed = fMaster->clusters(trimmedRange);
- bool ignore = false;
directional_for_each(trailed, reversed != run.leftToRight(), [&](Cluster& cluster) {
if (ignore) return;
bool ghost = &cluster >= trimmed.end();
if (!includeGhosts && ghost) {
- ignore = true;
return;
}
if (!visitor(&cluster, ghost)) {
@@ -690,7 +731,8 @@ SkScalar TextLine::iterateThroughSingleRunByStyles(const Run* run,
if (run->fEllipsis) {
// Extra efforts to get the ellipsis text style
- ClipContext clipContext = this->measureTextInsideOneRun(run->textRange(), run, runOffset, 0, false, false);
+ ClipContext clipContext = this->measureTextInsideOneRun(run->textRange(), run, runOffset,
+ 0, false, false);
TextRange testRange(run->fClusterStart, run->fClusterStart + 1);
for (BlockIndex index = fBlockRange.start; index < fBlockRange.end; ++index) {
auto block = fMaster->styles().begin() + index;
@@ -704,7 +746,8 @@ SkScalar TextLine::iterateThroughSingleRunByStyles(const Run* run,
}
if (styleType == StyleType::kNone) {
- ClipContext clipContext = this->measureTextInsideOneRun(textRange, run, runOffset, 0, false, false);
+ ClipContext clipContext = this->measureTextInsideOneRun(textRange, run, runOffset,
+ 0, false, false);
if (clipContext.clip.height() > 0) {
visitor(textRange, TextStyle(), clipContext);
return clipContext.clip.width();
@@ -757,13 +800,14 @@ SkScalar TextLine::iterateThroughSingleRunByStyles(const Run* run,
}
// We have the style and the text
- auto textRange = TextRange(start, start + size);
+ auto runStyleTextRange = TextRange(start, start + size);
// Measure the text
- ClipContext clipContext = this->measureTextInsideOneRun(textRange, run, runOffset, textOffsetInRun, false, false);
+ ClipContext clipContext = this->measureTextInsideOneRun(runStyleTextRange, run, runOffset,
+ textOffsetInRun, false, false);
if (clipContext.clip.height() == 0) {
continue;
}
- visitor(textRange, *prevStyle, clipContext);
+ visitor(runStyleTextRange, *prevStyle, clipContext);
textOffsetInRun += clipContext.clip.width();
// Start all over again
@@ -843,7 +887,8 @@ LineMetrics TextLine::getMetrics() const {
result.fHeight = littleRound(fAdvance.fY);
result.fWidth = littleRound(fAdvance.fX);
result.fLeft = fOffset.fX;
- result.fBaseline = fMaxRunMetrics.baseline() + (this - fMaster->lines().begin()) * result.fHeight;
+ // This is Flutter definition of a baseline
+ result.fBaseline = this->offset().fY + this->height() - this->sizes().descent();
result.fLineNumber = this - fMaster->lines().begin();
// Fill out the style parts
@@ -1086,29 +1131,19 @@ PositionWithAffinity TextLine::getGlyphPositionAtCoordinate(SkScalar dx) {
[this, dx, &result, &lookingForHit]
(TextRange textRange, const TextStyle& style, const TextLine::ClipContext& context) {
- auto findCodepointByTextIndex = [this](ClusterIndex clusterIndex8) {
- auto codepoints = fMaster->codepoints();
- auto codepoint = std::lower_bound(
- codepoints.begin(), codepoints.end(),
- clusterIndex8,
- [](const Codepoint& lhs,size_t rhs) -> bool { return lhs.fTextIndex < rhs; });
-
- return codepoint - codepoints.begin();
- };
-
auto offsetX = this->offset().fX;
if (dx < context.clip.fLeft + offsetX) {
// All the other runs are placed right of this one
- auto codepointIndex = findCodepointByTextIndex(context.run->globalClusterIndex(context.pos));
- result = { SkToS32(codepointIndex), kDownstream };
+ auto utf16Index = fMaster->getUTF16Index(context.run->globalClusterIndex(context.pos));
+ result = { SkToS32(utf16Index), kDownstream };
lookingForHit = false;
return false;
}
if (dx >= context.clip.fRight + offsetX) {
// We have to keep looking ; just in case keep the last one as the closest
- auto codepointIndex = findCodepointByTextIndex(context.run->globalClusterIndex(context.pos + context.size));
- result = { SkToS32(codepointIndex), kUpstream };
+ auto utf16Index = fMaster->getUTF16Index(context.run->globalClusterIndex(context.pos + context.size));
+ result = { SkToS32(utf16Index), kUpstream };
return true;
}
@@ -1125,53 +1160,34 @@ PositionWithAffinity TextLine::getGlyphPositionAtCoordinate(SkScalar dx) {
found = index;
}
- auto glyphStart = context.run->positionX(found) + context.fTextShift + offsetX;
- auto glyphWidth = context.run->positionX(found + 1) - context.run->positionX(found);
+ auto glyphemeStart = context.run->positionX(found) + context.fTextShift + offsetX;
+ auto glyphemeWidth = context.run->positionX(found + 1) - context.run->positionX(found);
+
+ // Find the grapheme range that contains the point
auto clusterIndex8 = context.run->globalClusterIndex(found);
auto clusterEnd8 = context.run->globalClusterIndex(found + 1);
-
- // Find the grapheme positions in codepoints that contains the point
- auto codepointIndex = findCodepointByTextIndex(clusterIndex8);
- CodepointRange codepoints(codepointIndex, codepointIndex);
- auto masterCodepoints = fMaster->codepoints();
- if (context.run->leftToRight()) {
- for (codepoints.end = codepointIndex;
- codepoints.end < masterCodepoints.size(); ++codepoints.end) {
- auto& cp = masterCodepoints[codepoints.end];
- if (cp.fTextIndex >= clusterEnd8) {
- break;
- }
- }
- } else {
- for (codepoints.end = codepointIndex;
- codepoints.end > 0; --codepoints.end) {
- auto& cp = masterCodepoints[codepoints.end];
- if (cp.fTextIndex <= clusterEnd8) {
- break;
- }
- }
- std::swap(codepoints.start, codepoints.end);
- }
-
- auto graphemeSize = codepoints.width();
+ auto graphemeStart = fMaster->findGraphemeStart(clusterIndex8);
+ auto graphemeWidth =
+ fMaster->findGraphemeStart(clusterEnd8) - graphemeStart;
+ auto utf16Index = fMaster->getUTF16Index(clusterIndex8);
// We only need to inspect one glyph (maybe not even the entire glyph)
SkScalar center;
bool insideGlyph = false;
- if (graphemeSize > 1) {
- auto averageCodepointWidth = glyphWidth / graphemeSize;
- auto delta = dx - glyphStart;
- auto insideIndex = SkScalarFloorToInt(delta / averageCodepointWidth);
- insideGlyph = delta > averageCodepointWidth;
- center = glyphStart + averageCodepointWidth * insideIndex + averageCodepointWidth / 2;
- codepointIndex += insideIndex;
+ if (graphemeWidth > 1) {
+ auto averageGlyphWidth = glyphemeWidth / graphemeWidth;
+ auto delta = dx - glyphemeStart;
+ auto insideIndex = SkScalarFloorToInt(delta / averageGlyphWidth);
+ insideGlyph = delta > averageGlyphWidth;
+ center = glyphemeStart + averageGlyphWidth * insideIndex + averageGlyphWidth / 2;
+ utf16Index += insideIndex;
} else {
- center = glyphStart + glyphWidth / 2;
+ center = glyphemeStart + glyphemeWidth / 2;
}
if ((dx < center) == context.run->leftToRight() || insideGlyph) {
- result = { SkToS32(codepointIndex), kDownstream };
+ result = { SkToS32(utf16Index), kDownstream };
} else {
- result = { SkToS32(codepointIndex + 1), kUpstream };
+ result = { SkToS32(utf16Index + 1), kUpstream };
}
// No need to continue
lookingForHit = false;
diff --git a/chromium/third_party/skia/modules/skparagraph/src/TextLine.h b/chromium/third_party/skia/modules/skparagraph/src/TextLine.h
index da54147e60a..73033f130f8 100644
--- a/chromium/third_party/skia/modules/skparagraph/src/TextLine.h
+++ b/chromium/third_party/skia/modules/skparagraph/src/TextLine.h
@@ -2,18 +2,28 @@
#ifndef TextLine_DEFINED
#define TextLine_DEFINED
-#include "include/core/SkCanvas.h"
+#include "include/core/SkPoint.h"
+#include "include/core/SkRect.h"
+#include "include/core/SkScalar.h"
#include "include/private/SkTArray.h"
-#include "include/private/SkTHash.h"
#include "modules/skparagraph/include/DartTypes.h"
#include "modules/skparagraph/include/Metrics.h"
#include "modules/skparagraph/include/TextStyle.h"
#include "modules/skparagraph/src/Run.h"
-#include "src/core/SkSpan.h"
+
+#include <stddef.h>
+#include <functional>
+#include <memory>
+#include <vector>
+
+class SkCanvas;
+class SkString;
namespace skia {
namespace textlayout {
+class ParagraphImpl;
+
class TextLine {
public:
@@ -27,6 +37,10 @@ public:
};
TextLine() = default;
+ TextLine(const TextLine&) = delete;
+ TextLine& operator=(const TextLine&) = delete;
+ TextLine(TextLine&&) = default;
+ TextLine& operator=(TextLine&&) = default;
~TextLine() = default;
TextLine(ParagraphImpl* master,
@@ -108,7 +122,7 @@ public:
private:
- Run* shapeEllipsis(const SkString& ellipsis, Run* run);
+ std::unique_ptr<Run> shapeEllipsis(const SkString& ellipsis, Run* run);
void justify(SkScalar maxWidth);
void paintText(SkCanvas* canvas, TextRange textRange, const TextStyle& style, const ClipContext& context) const;
@@ -124,13 +138,13 @@ private:
TextRange fTextWithWhitespacesRange;
ClusterRange fClusterRange;
ClusterRange fGhostClusterRange;
-
- SkTArray<size_t, true> fRunsInVisualOrder;
+ // Avoid the malloc/free in the common case of one run per line
+ SkSTArray<1, size_t, true> fRunsInVisualOrder;
SkVector fAdvance; // Text size
SkVector fOffset; // Text position
SkScalar fShift; // Let right
SkScalar fWidthWithSpaces;
- std::shared_ptr<Run> fEllipsis; // In case the line ends with the ellipsis
+ std::unique_ptr<Run> fEllipsis; // In case the line ends with the ellipsis
InternalLineMetrics fSizes; // Line metrics as a max of all run metrics and struts
InternalLineMetrics fMaxRunMetrics; // No struts - need it for GetRectForRange(max height)
bool fHasBackground;
diff --git a/chromium/third_party/skia/modules/skparagraph/src/TextStyle.cpp b/chromium/third_party/skia/modules/skparagraph/src/TextStyle.cpp
index 4dfa070b365..9445f7920ed 100644
--- a/chromium/third_party/skia/modules/skparagraph/src/TextStyle.cpp
+++ b/chromium/third_party/skia/modules/skparagraph/src/TextStyle.cpp
@@ -15,7 +15,8 @@ TextStyle::TextStyle() : fFontStyle() {
// value to indicate no decoration color was set.
fDecoration.fColor = SK_ColorTRANSPARENT;
fDecoration.fStyle = TextDecorationStyle::kSolid;
- fDecoration.fMode = TextDecorationMode::kGaps;
+ // TODO: switch back to kGaps when (if) switching flutter to skparagraph
+ fDecoration.fMode = TextDecorationMode::kThrough;
// Thickness is applied as a multiplier to the default thickness of the font.
fDecoration.fThicknessMultiplier = 1.0;
fFontSize = 14.0;
diff --git a/chromium/third_party/skia/modules/skparagraph/src/TextWrapper.cpp b/chromium/third_party/skia/modules/skparagraph/src/TextWrapper.cpp
index 7c1fcf32595..bfee1b8d743 100644
--- a/chromium/third_party/skia/modules/skparagraph/src/TextWrapper.cpp
+++ b/chromium/third_party/skia/modules/skparagraph/src/TextWrapper.cpp
@@ -10,9 +10,9 @@ SkScalar littleRound(SkScalar a) {
// This rounding is done to match Flutter tests. Must be removed..
auto val = std::fabs(a);
if (val < 10000) {
- return SkScalarRoundToScalar(a * 100.0)/100.0;
+ return SkScalarRoundToScalar(a * 100) * (1.0f/100);
} else if (val < 100000) {
- return SkScalarRoundToScalar(a * 10.0)/10.0;
+ return SkScalarRoundToScalar(a * 10) * (1.0f/10);
} else {
return SkScalarFloorToScalar(a);
}
diff --git a/chromium/third_party/skia/modules/skparagraph/src/TextWrapper.h b/chromium/third_party/skia/modules/skparagraph/src/TextWrapper.h
index 7dfd1051863..95f4b004bfc 100644
--- a/chromium/third_party/skia/modules/skparagraph/src/TextWrapper.h
+++ b/chromium/third_party/skia/modules/skparagraph/src/TextWrapper.h
@@ -75,6 +75,7 @@ class TextWrapper {
fStart = ClusterPos(cluster, cluster->startPos());
}
fEnd = ClusterPos(cluster, cluster->endPos());
+ // TODO: Make sure all the checks are correct and there are no unnecessary checks
if (!cluster->run()->isPlaceholder()) {
fMetrics.add(cluster->run());
}
diff --git a/chromium/third_party/skia/modules/skparagraph/src/TypefaceFontProvider.cpp b/chromium/third_party/skia/modules/skparagraph/src/TypefaceFontProvider.cpp
index cb5c262da48..cd30a67d967 100644
--- a/chromium/third_party/skia/modules/skparagraph/src/TypefaceFontProvider.cpp
+++ b/chromium/third_party/skia/modules/skparagraph/src/TypefaceFontProvider.cpp
@@ -24,6 +24,10 @@ SkFontStyleSet* TypefaceFontProvider::onMatchFamily(const char familyName[]) con
return nullptr;
}
+sk_sp<SkTypeface> TypefaceFontProvider::onMakeFromFontData(std::unique_ptr<SkFontData>) const {
+ return nullptr;
+}
+
size_t TypefaceFontProvider::registerTypeface(sk_sp<SkTypeface> typeface) {
if (typeface == nullptr) {
return 0;
@@ -76,7 +80,9 @@ SkTypeface* TypefaceFontStyleSet::matchStyle(const SkFontStyle& pattern) {
}
void TypefaceFontStyleSet::appendTypeface(sk_sp<SkTypeface> typeface) {
- fStyles.emplace_back(std::move(typeface));
+ if (typeface.get() != nullptr) {
+ fStyles.emplace_back(std::move(typeface));
+ }
}
} // namespace textlayout
diff --git a/chromium/third_party/skia/modules/skplaintexteditor/BUILD.gn b/chromium/third_party/skia/modules/skplaintexteditor/BUILD.gn
index abf7d2aa68e..0b72bec1ecb 100644
--- a/chromium/third_party/skia/modules/skplaintexteditor/BUILD.gn
+++ b/chromium/third_party/skia/modules/skplaintexteditor/BUILD.gn
@@ -35,7 +35,8 @@ if (skia_use_icu && skia_use_harfbuzz) {
include_dirs = [ "../.." ]
public = [ "src/word_boundaries.h" ]
sources = [ "src/word_boundaries.cpp" ]
- deps = [ "../../third_party/icu" ]
+ configs += [ "../../third_party/icu/config:no_cxx" ]
+ deps = [ "//third_party/icu" ]
}
source_set("editor_app") {
diff --git a/chromium/third_party/skia/modules/skplaintexteditor/src/word_boundaries.cpp b/chromium/third_party/skia/modules/skplaintexteditor/src/word_boundaries.cpp
index 17c8cd35b2b..3b08f634a2f 100644
--- a/chromium/third_party/skia/modules/skplaintexteditor/src/word_boundaries.cpp
+++ b/chromium/third_party/skia/modules/skplaintexteditor/src/word_boundaries.cpp
@@ -1,50 +1,55 @@
// Copyright 2019 Google LLC.
// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
+#include "include/core/SkTypes.h"
+#include "include/private/SkTemplates.h"
#include "modules/skplaintexteditor/src/word_boundaries.h"
-#include <unicode/brkiter.h>
-#include <unicode/unistr.h>
-
+#include <unicode/ubrk.h>
+#include <unicode/utext.h>
+#include <unicode/utypes.h>
#include <memory>
-std::vector<bool> GetUtf8WordBoundaries(const char* begin, size_t byteCount, const char* locale) {
- static constexpr UBreakIteratorType kIteratorType = UBRK_WORD;
- struct UTextCloser {
- void operator()(UText* p) { (void)utext_close(p); }
- };
- struct UBreakCloser {
- void operator()(UBreakIterator* p) { (void)ubrk_close(p); }
- };
+namespace {
+template <typename T,typename P,P* p> using resource = std::unique_ptr<T, SkFunctionWrapper<P, p>>;
+using ICUBrk = resource<UBreakIterator, decltype(ubrk_close) , ubrk_close >;
+using ICUUText = resource<UText , decltype(utext_close) , utext_close >;
+}
+
+std::vector<bool> GetUtf8WordBoundaries(const char* begin, size_t byteCount, const char* locale) {
std::vector<bool> result;
if (0 == byteCount) {
return result;
}
result.resize(byteCount);
- UText utf8UText = UTEXT_INITIALIZER;
- UErrorCode errorCode = U_ZERO_ERROR;
- (void)utext_openUTF8(&utf8UText, begin, byteCount, &errorCode);
- std::unique_ptr<UText, UTextCloser> autoclose1(&utf8UText);
- if (U_FAILURE(errorCode)) {
+ UErrorCode status = U_ZERO_ERROR;
+ UText sUtf8UText = UTEXT_INITIALIZER;
+ ICUUText utf8UText(utext_openUTF8(&sUtf8UText, begin, byteCount, &status));
+ if (U_FAILURE(status)) {
+ SkDebugf("Could not create utf8UText: %s", u_errorName(status));
return result;
}
- UBreakIterator* iter = ubrk_open(kIteratorType, locale, nullptr, 0, &errorCode);
- std::unique_ptr<UBreakIterator, UBreakCloser> autoclose2(iter);
- if (U_FAILURE(errorCode)) {
+
+ ICUBrk wordBreakIterator(ubrk_open(UBRK_WORD, locale, nullptr, 0, &status));
+ if (!wordBreakIterator || U_FAILURE(status)) {
+ SkDEBUGF("Could not create line break iterator: %s", u_errorName(status));
return result;
}
- ubrk_setUText(iter, &utf8UText, &errorCode);
- if (U_FAILURE(errorCode)) {
+
+ ubrk_setUText(&*wordBreakIterator, utf8UText.get(), &status);
+ if (U_FAILURE(status)) {
+ SkDebugf("Could not setText on break iterator: %s", u_errorName(status));
return result;
}
- int pos = ubrk_first(iter);
- while (pos != icu::BreakIterator::DONE) {
- if ((unsigned)pos < (unsigned)byteCount) {
+
+ int32_t pos = ubrk_first(&*wordBreakIterator);
+ while (pos != UBRK_DONE) {
+ if ((size_t)pos < byteCount) {
result[pos] = true;
}
- pos = ubrk_next(iter);
+ pos = ubrk_next(&*wordBreakIterator);
}
return result;
}
diff --git a/chromium/third_party/skia/modules/skresources/include/SkResources.h b/chromium/third_party/skia/modules/skresources/include/SkResources.h
index 3a5769d900f..c5bdb81dfa7 100644
--- a/chromium/third_party/skia/modules/skresources/include/SkResources.h
+++ b/chromium/third_party/skia/modules/skresources/include/SkResources.h
@@ -11,6 +11,7 @@
#include "include/core/SkData.h"
#include "include/core/SkRefCnt.h"
#include "include/core/SkString.h"
+#include "include/core/SkTypeface.h"
#include "include/core/SkTypes.h"
#include "include/private/SkMutex.h"
#include "include/private/SkTHash.h"
@@ -96,6 +97,8 @@ public:
}
/**
+ * DEPRECATED: implement loadTypeface() instead.
+ *
* Load an external font and return as SkData.
*
* @param name font name ("fName" Lottie property)
@@ -112,6 +115,17 @@ public:
const char[] /* url */) const {
return nullptr;
}
+
+ /**
+ * Load an external font and return as SkTypeface.
+ *
+ * @param name font name
+ * @param url web font URL
+ */
+ virtual sk_sp<SkTypeface> loadTypeface(const char[] /* name */,
+ const char[] /* url */) const {
+ return nullptr;
+ }
};
class FileResourceProvider final : public ResourceProvider {
@@ -137,7 +151,7 @@ protected:
sk_sp<SkData> load(const char[], const char[]) const override;
sk_sp<ImageAsset> loadImageAsset(const char[], const char[], const char[]) const override;
- sk_sp<SkData> loadFont(const char[], const char[]) const override;
+ sk_sp<SkTypeface> loadTypeface(const char[], const char[]) const override;
private:
const sk_sp<ResourceProvider> fProxy;
diff --git a/chromium/third_party/skia/modules/skresources/src/SkResources.cpp b/chromium/third_party/skia/modules/skresources/src/SkResources.cpp
index 1d3e710c3f9..8934fd40e25 100644
--- a/chromium/third_party/skia/modules/skresources/src/SkResources.cpp
+++ b/chromium/third_party/skia/modules/skresources/src/SkResources.cpp
@@ -196,8 +196,9 @@ sk_sp<ImageAsset> ResourceProviderProxyBase::loadImageAsset(const char rpath[],
: nullptr;
}
-sk_sp<SkData> ResourceProviderProxyBase::loadFont(const char name[], const char url[]) const {
- return fProxy ? fProxy->loadFont(name, url)
+sk_sp<SkTypeface> ResourceProviderProxyBase::loadTypeface(const char name[],
+ const char url[]) const {
+ return fProxy ? fProxy->loadTypeface(name, url)
: nullptr;
}
diff --git a/chromium/third_party/skia/modules/sksg/include/SkSGGroup.h b/chromium/third_party/skia/modules/sksg/include/SkSGGroup.h
index 81dd797af5c..085f7a66abd 100644
--- a/chromium/third_party/skia/modules/sksg/include/SkSGGroup.h
+++ b/chromium/third_party/skia/modules/sksg/include/SkSGGroup.h
@@ -35,6 +35,7 @@ public:
void clear();
protected:
+ Group();
explicit Group(std::vector<sk_sp<RenderNode>>);
~Group() override;
diff --git a/chromium/third_party/skia/modules/sksg/include/SkSGTransform.h b/chromium/third_party/skia/modules/sksg/include/SkSGTransform.h
index 1c7c17ca8ef..3c1c013086f 100644
--- a/chromium/third_party/skia/modules/sksg/include/SkSGTransform.h
+++ b/chromium/third_party/skia/modules/sksg/include/SkSGTransform.h
@@ -49,7 +49,7 @@ private:
*
* auto m33 = Matrix<SkMatrix>::Make(SkMatrix::I());
* ...
- * m33->setMatrix(SkMatrix::MakeTrans(10, 10));
+ * m33->setMatrix(SkMatrix::Translate(10, 10));
*
*/
template <typename T>
diff --git a/chromium/third_party/skia/modules/sksg/src/SkSGGroup.cpp b/chromium/third_party/skia/modules/sksg/src/SkSGGroup.cpp
index 829c5ae50fc..f60c6ffbca9 100644
--- a/chromium/third_party/skia/modules/sksg/src/SkSGGroup.cpp
+++ b/chromium/third_party/skia/modules/sksg/src/SkSGGroup.cpp
@@ -13,6 +13,8 @@
namespace sksg {
+Group::Group() = default;
+
Group::Group(std::vector<sk_sp<RenderNode>> children)
: fChildren(std::move(children)) {
for (const auto& child : fChildren) {
diff --git a/chromium/third_party/skia/modules/skshaper/BUILD.gn b/chromium/third_party/skia/modules/skshaper/BUILD.gn
index 9673c374d1d..e595d859492 100644
--- a/chromium/third_party/skia/modules/skshaper/BUILD.gn
+++ b/chromium/third_party/skia/modules/skshaper/BUILD.gn
@@ -16,7 +16,10 @@ if (skia_enable_skshaper) {
if (is_component_build) {
defines += [ "SKSHAPER_DLL" ]
}
- if (skia_use_icu) {
+ if (skia_use_fonthost_mac) {
+ defines += [ "SK_SHAPER_CORETEXT_AVAILABLE" ]
+ }
+ if (skia_use_icu && skia_use_harfbuzz) {
defines += [ "SK_SHAPER_HARFBUZZ_AVAILABLE" ]
}
}
@@ -29,6 +32,9 @@ if (skia_enable_skshaper) {
deps = [ "../..:skia" ]
defines = [ "SKSHAPER_IMPLEMENTATION=1" ]
sources = skia_shaper_primitive_sources
+ if (skia_use_fonthost_mac) {
+ sources += skia_shaper_coretext_sources
+ }
if (skia_use_icu && skia_use_harfbuzz) {
sources += skia_shaper_harfbuzz_sources
deps += [
@@ -36,7 +42,10 @@ if (skia_enable_skshaper) {
"//third_party/icu",
]
}
- configs += [ "../../:skia_private" ]
+ configs += [
+ "../../:skia_private",
+ "../../third_party/icu/config:no_cxx",
+ ]
}
} else {
group("skshaper") {
diff --git a/chromium/third_party/skia/modules/skshaper/include/SkShaper.h b/chromium/third_party/skia/modules/skshaper/include/SkShaper.h
index 104872cd21c..c0864af5ab4 100644
--- a/chromium/third_party/skia/modules/skshaper/include/SkShaper.h
+++ b/chromium/third_party/skia/modules/skshaper/include/SkShaper.h
@@ -54,8 +54,9 @@ public:
static std::unique_ptr<SkShaper> MakeShapeThenWrap(sk_sp<SkFontMgr> = nullptr);
static std::unique_ptr<SkShaper> MakeShapeDontWrapOrReorder(sk_sp<SkFontMgr> = nullptr);
#endif
- // Returns nullptr if not supported
+ #ifdef SK_SHAPER_CORETEXT_AVAILABLE
static std::unique_ptr<SkShaper> MakeCoreText();
+ #endif
static std::unique_ptr<SkShaper> Make(sk_sp<SkFontMgr> = nullptr);
diff --git a/chromium/third_party/skia/modules/skshaper/skshaper.gni b/chromium/third_party/skia/modules/skshaper/skshaper.gni
index ba3f09dd36f..425cf3f4b96 100644
--- a/chromium/third_party/skia/modules/skshaper/skshaper.gni
+++ b/chromium/third_party/skia/modules/skshaper/skshaper.gni
@@ -11,7 +11,7 @@ skia_shaper_public = [ "$_include/SkShaper.h" ]
skia_shaper_primitive_sources = [
"$_src/SkShaper.cpp",
- "$_src/SkShaper_coretext.cpp",
"$_src/SkShaper_primitive.cpp",
]
skia_shaper_harfbuzz_sources = [ "$_src/SkShaper_harfbuzz.cpp" ]
+skia_shaper_coretext_sources = [ "$_src/SkShaper_coretext.cpp" ]
diff --git a/chromium/third_party/skia/modules/skshaper/src/SkShaper_coretext.cpp b/chromium/third_party/skia/modules/skshaper/src/SkShaper_coretext.cpp
index 6252795952f..50689ccc349 100644
--- a/chromium/third_party/skia/modules/skshaper/src/SkShaper_coretext.cpp
+++ b/chromium/third_party/skia/modules/skshaper/src/SkShaper_coretext.cpp
@@ -7,8 +7,6 @@
#include "modules/skshaper/include/SkShaper.h"
-#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
-
#ifdef SK_BUILD_FOR_MAC
#import <ApplicationServices/ApplicationServices.h>
#endif
@@ -22,6 +20,8 @@
#include "include/ports/SkTypeface_mac.h"
#include "src/core/SkArenaAlloc.h"
+#include "src/utils/mac/SkCGBase.h"
+#include "src/utils/mac/SkUniqueCFRef.h"
#include <vector>
@@ -96,15 +96,6 @@ void SkShaper_CoreText::shape(const char* utf8, size_t utf8Bytes,
width, handler);
}
-template <typename T> class AutoCF {
- T fObj;
-public:
- AutoCF(T obj) : fObj(obj) {}
- ~AutoCF() { CFRelease(fObj); }
-
- T get() const { return fObj; }
-};
-
// CTFramesetter/CTFrame can do this, but require version 10.14
class LineBreakIter {
CTTypesetterRef fTypesetter;
@@ -128,28 +119,16 @@ public:
};
static void dict_add_double(CFMutableDictionaryRef d, const void* name, double value) {
- AutoCF<CFNumberRef> number = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &value);
+ SkUniqueCFRef<CFNumberRef> number(
+ CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &value));
CFDictionaryAddValue(d, name, number.get());
}
-static void dump(CFDictionaryRef d) {
- CFIndex count = CFDictionaryGetCount(d);
- std::vector<const void*> keys(count);
- std::vector<const void*> vals(count);
-
- CFDictionaryGetKeysAndValues(d, keys.data(), vals.data());
-
- for (CFIndex i = 0; i < count; ++i) {
- CFStringRef kstr = (CFStringRef)keys[i];
- const char* ckstr = CFStringGetCStringPtr(kstr, kCFStringEncodingUTF8);
- SkDebugf("dict[%d] %s %p\n", i, ckstr, vals[i]);
- }
-}
-
-static CTFontRef create_ctfont_from_font(const SkFont& font) {
+static SkUniqueCFRef<CTFontRef> create_ctfont_from_font(const SkFont& font) {
auto typeface = font.getTypefaceOrDefault();
auto ctfont = SkTypeface_GetCTFontRef(typeface);
- return CTFontCreateCopyWithAttributes(ctfont, font.getSize(), nullptr, nullptr);
+ return SkUniqueCFRef<CTFontRef>(
+ CTFontCreateCopyWithAttributes(ctfont, font.getSize(), nullptr, nullptr));
}
static SkFont run_to_font(CTRunRef run, const SkFont& orig) {
@@ -157,7 +136,7 @@ static SkFont run_to_font(CTRunRef run, const SkFont& orig) {
CTFontRef ct = (CTFontRef)CFDictionaryGetValue(attr, kCTFontAttributeName);
if (!ct) {
SkDebugf("no ctfont in Run Attributes\n");
- dump(attr);
+ CFShow(attr);
return orig;
}
// Do I need to add a local cache, or allow the caller to manage this lookup?
@@ -174,25 +153,16 @@ void SkShaper_CoreText::shape(const char* utf8, size_t utf8Bytes,
bool /* leftToRight */,
SkScalar width,
RunHandler* handler) const {
- auto cgfloat_to_scalar = [](CGFloat x) {
- SkScalar s;
- if (sizeof(CGFloat) == sizeof(double)) {
- s = SkDoubleToScalar(x);
- } else {
- s = x;
- }
- return s;
- };
-
- AutoCF<CFStringRef> textString = CFStringCreateWithBytes(nullptr, (const uint8_t*)utf8, utf8Bytes,
- kCFStringEncodingUTF8, false);
+ SkUniqueCFRef<CFStringRef> textString(
+ CFStringCreateWithBytes(kCFAllocatorDefault, (const uint8_t*)utf8, utf8Bytes,
+ kCFStringEncodingUTF8, false));
- AutoCF<CTFontRef> ctfont = create_ctfont_from_font(font);
+ SkUniqueCFRef<CTFontRef> ctfont = create_ctfont_from_font(font);
- AutoCF<CFMutableDictionaryRef> attr =
+ SkUniqueCFRef<CFMutableDictionaryRef> attr(
CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks,
- &kCFTypeDictionaryValueCallBacks);
+ &kCFTypeDictionaryValueCallBacks));
CFDictionaryAddValue(attr.get(), kCTFontAttributeName, ctfont.get());
if (false) {
// trying to see what these affect
@@ -200,11 +170,11 @@ void SkShaper_CoreText::shape(const char* utf8, size_t utf8Bytes,
dict_add_double(attr.get(), kCTKernAttributeName, 0.0);
}
- AutoCF<CFAttributedStringRef> attrString =
- CFAttributedStringCreate(nullptr, textString.get(), attr.get());
+ SkUniqueCFRef<CFAttributedStringRef> attrString(
+ CFAttributedStringCreate(kCFAllocatorDefault, textString.get(), attr.get()));
- AutoCF<CTTypesetterRef> typesetter =
- CTTypesetterCreateWithAttributedStringAndOptions(attrString.get(), nullptr);
+ SkUniqueCFRef<CTTypesetterRef> typesetter(
+ CTTypesetterCreateWithAttributedString(attrString.get()));
SkSTArenaAlloc<4096> arena;
@@ -272,7 +242,7 @@ void SkShaper_CoreText::shape(const char* utf8, size_t utf8Bytes,
for (CFIndex k = 0; k < runGlyphs; ++k) {
buffer.positions[k] = {
- buffer.point.fX + cgfloat_to_scalar(positions[k].x),
+ buffer.point.fX + SkScalarFromCGFloat(positions[k].x),
buffer.point.fY,
};
if (buffer.offsets) {
@@ -288,9 +258,3 @@ void SkShaper_CoreText::shape(const char* utf8, size_t utf8Bytes,
handler->commitLine();
}
}
-
-#else
-std::unique_ptr<SkShaper> SkShaper::MakeCoreText() {
- return nullptr;
-}
-#endif
diff --git a/chromium/third_party/skia/modules/skshaper/src/SkShaper_harfbuzz.cpp b/chromium/third_party/skia/modules/skshaper/src/SkShaper_harfbuzz.cpp
index 29d9fe92974..c42575d6356 100644
--- a/chromium/third_party/skia/modules/skshaper/src/SkShaper_harfbuzz.cpp
+++ b/chromium/third_party/skia/modules/skshaper/src/SkShaper_harfbuzz.cpp
@@ -20,11 +20,13 @@
#include "include/core/SkTypes.h"
#include "include/private/SkBitmaskEnum.h"
#include "include/private/SkMalloc.h"
+#include "include/private/SkMutex.h"
#include "include/private/SkTArray.h"
#include "include/private/SkTFitsIn.h"
#include "include/private/SkTemplates.h"
#include "include/private/SkTo.h"
#include "modules/skshaper/include/SkShaper.h"
+#include "src/core/SkLRUCache.h"
#include "src/core/SkSpan.h"
#include "src/core/SkTDPQueue.h"
#include "src/utils/SkUTF.h"
@@ -73,25 +75,6 @@ using ICUBiDi = resource<UBiDi , decltype(ubidi_close) , ubidi_clo
using ICUBrk = resource<UBreakIterator, decltype(ubrk_close) , ubrk_close >;
using ICUUText = resource<UText , decltype(utext_close) , utext_close >;
-HBBlob stream_to_blob(std::unique_ptr<SkStreamAsset> asset) {
- size_t size = asset->getLength();
- HBBlob blob;
- if (const void* base = asset->getMemoryBase()) {
- blob.reset(hb_blob_create((char*)base, SkToUInt(size),
- HB_MEMORY_MODE_READONLY, asset.release(),
- [](void* p) { delete (SkStreamAsset*)p; }));
- } else {
- // SkDebugf("Extra SkStreamAsset copy\n");
- void* ptr = size ? sk_malloc_throw(size) : nullptr;
- asset->read(ptr, size);
- blob.reset(hb_blob_create((char*)ptr, SkToUInt(size),
- HB_MEMORY_MODE_READONLY, ptr, sk_free));
- }
- SkASSERT(blob);
- hb_blob_make_immutable(blob.get());
- return blob;
-}
-
hb_position_t skhb_position(SkScalar value) {
// Treat HarfBuzz hb_position_t as 16.16 fixed-point.
constexpr int kHbPosition1 = 1 << 16;
@@ -266,31 +249,65 @@ hb_blob_t* skhb_get_table(hb_face_t* face, hb_tag_t tag, void* user_data) {
}
SkData* rawData = data.release();
return hb_blob_create(reinterpret_cast<char*>(rawData->writable_data()), rawData->size(),
- HB_MEMORY_MODE_WRITABLE, rawData, [](void* ctx) {
+ HB_MEMORY_MODE_READONLY, rawData, [](void* ctx) {
SkSafeUnref(((SkData*)ctx));
});
}
-HBFont create_hb_font(const SkFont& font) {
- SkASSERT(font.getTypeface());
+HBBlob stream_to_blob(std::unique_ptr<SkStreamAsset> asset) {
+ size_t size = asset->getLength();
+ HBBlob blob;
+ if (const void* base = asset->getMemoryBase()) {
+ blob.reset(hb_blob_create((char*)base, SkToUInt(size),
+ HB_MEMORY_MODE_READONLY, asset.release(),
+ [](void* p) { delete (SkStreamAsset*)p; }));
+ } else {
+ // SkDebugf("Extra SkStreamAsset copy\n");
+ void* ptr = size ? sk_malloc_throw(size) : nullptr;
+ asset->read(ptr, size);
+ blob.reset(hb_blob_create((char*)ptr, SkToUInt(size),
+ HB_MEMORY_MODE_READONLY, ptr, sk_free));
+ }
+ SkASSERT(blob);
+ hb_blob_make_immutable(blob.get());
+ return blob;
+}
+
+SkDEBUGCODE(static hb_user_data_key_t gDataIdKey;)
+
+HBFace create_hb_face(const SkTypeface& typeface) {
int index;
- std::unique_ptr<SkStreamAsset> typefaceAsset = font.getTypeface()->openStream(&index);
+ std::unique_ptr<SkStreamAsset> typefaceAsset = typeface.openStream(&index);
HBFace face;
- if (!typefaceAsset) {
+ if (typefaceAsset && typefaceAsset->getMemoryBase()) {
+ HBBlob blob(stream_to_blob(std::move(typefaceAsset)));
+ face.reset(hb_face_create(blob.get(), (unsigned)index));
+ } else {
face.reset(hb_face_create_for_tables(
skhb_get_table,
- reinterpret_cast<void *>(font.refTypeface().release()),
+ const_cast<SkTypeface*>(SkRef(&typeface)),
[](void* user_data){ SkSafeUnref(reinterpret_cast<SkTypeface*>(user_data)); }));
- } else {
- HBBlob blob(stream_to_blob(std::move(typefaceAsset)));
- face.reset(hb_face_create(blob.get(), (unsigned)index));
}
SkASSERT(face);
if (!face) {
return nullptr;
}
hb_face_set_index(face.get(), (unsigned)index);
- hb_face_set_upem(face.get(), font.getTypeface()->getUnitsPerEm());
+ hb_face_set_upem(face.get(), typeface.getUnitsPerEm());
+
+ SkDEBUGCODE(
+ hb_face_set_user_data(face.get(), &gDataIdKey, const_cast<SkTypeface*>(&typeface),
+ nullptr, false);
+ )
+
+ return face;
+}
+
+HBFont create_hb_font(const SkFont& font, const HBFace& face) {
+ SkDEBUGCODE(
+ void* dataId = hb_face_get_user_data(face.get(), &gDataIdKey);
+ SkASSERT(dataId == font.getTypeface());
+ )
HBFont otFont(hb_font_create(face.get()));
SkASSERT(otFont);
@@ -652,6 +669,7 @@ protected:
private:
const sk_sp<SkFontMgr> fFontMgr;
HBBuffer fBuffer;
+ hb_language_t fUndefinedLanguage;
void shape(const char* utf8, size_t utf8Bytes,
const SkFont&,
@@ -777,6 +795,7 @@ ShaperHarfBuzz::ShaperHarfBuzz(HBBuffer buffer, ICUBrk line, ICUBrk grapheme,
, fGraphemeBreakIterator(std::move(grapheme))
, fFontMgr(std::move(fontmgr))
, fBuffer(std::move(buffer))
+ , fUndefinedLanguage(hb_language_from_string("und", -1))
{}
void ShaperHarfBuzz::shape(const char* utf8, size_t utf8Bytes,
@@ -1315,11 +1334,34 @@ ShapedRun ShaperHarfBuzz::shape(char const * const utf8,
hb_direction_t direction = is_LTR(bidi.currentLevel()) ? HB_DIRECTION_LTR:HB_DIRECTION_RTL;
hb_buffer_set_direction(buffer, direction);
hb_buffer_set_script(buffer, hb_script_from_iso15924_tag((hb_tag_t)script.currentScript()));
- hb_buffer_set_language(buffer, hb_language_from_string(language.currentLanguage(), -1));
+ // Buffers with HB_LANGUAGE_INVALID race since hb_language_get_default is not thread safe.
+ // The user must provide a language, but may provide data hb_language_from_string cannot use.
+ // Use "und" for the undefined language in this case (RFC5646 4.1 5).
+ hb_language_t hbLanguage = hb_language_from_string(language.currentLanguage(), -1);
+ if (hbLanguage == HB_LANGUAGE_INVALID) {
+ hbLanguage = fUndefinedLanguage;
+ }
+ hb_buffer_set_language(buffer, hbLanguage);
hb_buffer_guess_segment_properties(buffer);
- // TODO: how to cache hbface (typeface) / hbfont (font)
- HBFont hbFont(create_hb_font(font.currentFont()));
+ // TODO: better cache HBFace (data) / hbfont (typeface)
+ // An HBFace is expensive (it sanitizes the bits).
+ // An HBFont is fairly inexpensive.
+ // An HBFace is actually tied to the data, not the typeface.
+ // The size of 100 here is completely arbitrary and used to match libtxt.
+ static SkLRUCache<SkFontID, HBFace> gHBFaceCache(100);
+ static SkMutex gHBFaceCacheMutex;
+ HBFont hbFont;
+ {
+ SkAutoMutexExclusive lock(gHBFaceCacheMutex);
+ SkFontID dataId = font.currentFont().getTypeface()->uniqueID();
+ HBFace* hbFaceCached = gHBFaceCache.find(dataId);
+ if (!hbFaceCached) {
+ HBFace hbFace(create_hb_face(*font.currentFont().getTypeface()));
+ hbFaceCached = gHBFaceCache.insert(dataId, std::move(hbFace));
+ }
+ hbFont = create_hb_font(font.currentFont(), *hbFaceCached);
+ }
if (!hbFont) {
return run;
}