├── .github ├── README.md ├── example_canvas_ity.png ├── example_html5.png ├── icon.png └── workflows │ └── tests.yml ├── LICENSE.txt ├── demos └── tiger │ ├── CMakeLists.txt │ └── tiger.cpp ├── src └── canvas_ity.hpp └── test ├── CMakeLists.txt ├── test.cpp └── test.html /.github/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # canvas_ity 4 | 5 | [![Tests](../../../actions/workflows/tests.yml/badge.svg)](../../../actions/workflows/tests.yml) 6 | 7 | This is a tiny, [single-header C++ library](../src/canvas_ity.hpp) 8 | for rasterizing immediate-mode 2D vector graphics, closely 9 | modeled on the basic [W3C (not WHATWG) HTML5 2D canvas 10 | specification](https://www.w3.org/TR/2015/REC-2dcontext-20151119/). 11 | 12 | The priorities for this library are high-quality rendering, ease of use, and 13 | compact size. Speed is important too, but secondary to the other priorities. 14 | Notably, this library takes an opinionated approach and does not provide 15 | options for trading off quality for speed. 16 | 17 | Despite its small size, it supports nearly everything listed in the W3C 18 | HTML5 2D canvas specification, except for hit regions and getting certain 19 | properties. The main differences lie in the surface-level API to make this 20 | easier for C++ use, while the underlying implementation is carefully based 21 | on the specification. In particular, stroke, fill, gradient, pattern, 22 | image, and font styles are specified slightly differently (avoiding strings 23 | and auxiliary classes). Nonetheless, the goal is that this library could 24 | produce a conforming HTML5 2D canvas implementation if wrapped in a thin 25 | layer of JavaScript bindings. See the accompanying [C++ automated test 26 | suite](../test/test.cpp) and its [HTML5 port](../test/test.html) for a mapping 27 | between the APIs and a comparison of this library's rendering output against 28 | browser canvas implementations. 29 | 30 | ## :memo: Example 31 | 32 | The following complete example program writes out a TGA image file and 33 | demonstrates path building, fills, strokes, line dash patterns, line joins, 34 | line caps, linear gradients, drop shadows, and compositing operations. See 35 | the HTML5 equivalent of the example on the right (scroll the code horizontally 36 | if needed) and compare them line-by-line. Note that the minor differences 37 | in shading are due to the library's use of gamma-correct blending whereas 38 | browsers typically ignore this. 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 127 | 206 | 207 |
canvas_ityHTML5
51 | 52 | 53 | ```c++ 54 | #include 55 | #include 56 | // Include the library header and implementation. 57 | #define CANVAS_ITY_IMPLEMENTATION 58 | #include "canvas_ity.hpp" 59 | int main() 60 | { 61 | // Construct the canvas. 62 | static int const width = 256, height = 256; 63 | canvas_ity::canvas context( width, height ); 64 | 65 | // Build a star path. 66 | context.move_to( 128.0f, 28.0f ); context.line_to( 157.0f, 87.0f ); 67 | context.line_to( 223.0f, 97.0f ); context.line_to( 175.0f, 143.0f ); 68 | context.line_to( 186.0f, 208.0f ); context.line_to( 128.0f, 178.0f ); 69 | context.line_to( 69.0f, 208.0f ); context.line_to( 80.0f, 143.0f ); 70 | context.line_to( 32.0f, 97.0f ); context.line_to( 98.0f, 87.0f ); 71 | context.close_path(); 72 | 73 | // Set up the drop shadow. 74 | context.set_shadow_blur( 8.0f ); 75 | context.shadow_offset_y = 4.0f; 76 | context.set_shadow_color( 0.0f, 0.0f, 0.0f, 0.5f ); 77 | 78 | // Fill the star with yellow. 79 | context.set_color( canvas_ity::fill_style, 1.0f, 0.9f, 0.2f, 1.0f ); 80 | context.fill(); 81 | 82 | // Draw the star with a thick red stroke and rounded points. 83 | context.line_join = canvas_ity::rounded; 84 | context.set_line_width( 12.0f ); 85 | context.set_color( canvas_ity::stroke_style, 0.9f, 0.0f, 0.5f, 1.0f ); 86 | context.stroke(); 87 | 88 | // Draw the star again with a dashed thinner orange stroke. 89 | float segments[] = { 21.0f, 9.0f, 1.0f, 9.0f, 7.0f, 9.0f, 1.0f, 9.0f }; 90 | context.set_line_dash( segments, 8 ); 91 | context.line_dash_offset = 10.0f; 92 | context.line_cap = canvas_ity::circle; 93 | context.set_line_width( 6.0f ); 94 | context.set_color( canvas_ity::stroke_style, 0.95f, 0.65f, 0.15f, 1.0f ); 95 | context.stroke(); 96 | 97 | // Turn off the drop shadow. 98 | context.set_shadow_color( 0.0f, 0.0f, 0.0f, 0.0f ); 99 | 100 | // Add a shine layer over the star. 101 | context.set_linear_gradient( canvas_ity::fill_style, 64.0f, 0.0f, 192.0f, 256.0f ); 102 | context.add_color_stop( canvas_ity::fill_style, 0.30f, 1.0f, 1.0f, 1.0f, 0.0f ); 103 | context.add_color_stop( canvas_ity::fill_style, 0.35f, 1.0f, 1.0f, 1.0f, 0.8f ); 104 | context.add_color_stop( canvas_ity::fill_style, 0.45f, 1.0f, 1.0f, 1.0f, 0.8f ); 105 | context.add_color_stop( canvas_ity::fill_style, 0.50f, 1.0f, 1.0f, 1.0f, 0.0f ); 106 | 107 | context.global_composite_operation = canvas_ity::source_atop; 108 | context.fill_rectangle( 0.0f, 0.0f, 256.0f, 256.0f ); 109 | 110 | // Fetch the rendered RGBA pixels from the entire canvas. 111 | unsigned char *image = new unsigned char[ height * width * 4 ]; 112 | context.get_image_data( image, width, height, width * 4, 0, 0 ); 113 | // Write them out to a TGA image file (TGA uses BGRA order). 114 | unsigned char header[] = { 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 115 | width & 255, width >> 8, height & 255, height >> 8, 32, 40 }; 116 | for ( int pixel = 0; pixel < height * width; ++pixel ) 117 | std::swap( image[ pixel * 4 + 0 ], image[ pixel * 4 + 2 ] ); 118 | std::ofstream stream( "example.tga", std::ios::binary ); 119 | stream.write( reinterpret_cast< char * >( header ), sizeof( header ) ); 120 | stream.write( reinterpret_cast< char * >( image ), height * width * 4 ); 121 | delete[] image; 122 | } 123 | ``` 124 | 125 | 126 | 128 | 129 | 130 | ```html 131 | 132 | 133 | 134 | Example 135 | 136 | 137 | 138 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | ``` 203 | 204 | 205 |
208 | 209 | ## :sparkles: Features 210 | 211 | ### High-quality rendering 212 | 213 | - Trapezoidal area antialiasing provides very smooth antialiasing, even when 214 | lines are nearly horizontal or vertical. 215 | - Gamma-correct blending, interpolation, and resampling are used throughout. 216 | It linearizes all colors and premultiplies alpha on input and converts 217 | back to unpremultiplied sRGB on output. This reduces muddiness on many 218 | gradients (e.g., red to green), makes line thicknesses more perceptually 219 | uniform, and avoids dark fringes when interpolating opacity. 220 | - Bicubic convolution resampling is used whenever it needs to resample a 221 | pattern or image. This smoothly interpolates with less blockiness when 222 | magnifying, and antialiases well when minifying. It can simultaneously 223 | magnify and minify along different axes. 224 | - Ordered dithering is used on output. This reduces banding on subtle 225 | gradients while still being compression-friendly. 226 | - High curvature is handled carefully in line joins. Thick lines are drawn 227 | correctly as though tracing with a wide pen nib, even where the lines 228 | curve sharply. (Simpler curve offsetting approaches tend to show 229 | bite-like artifacts in these cases.) 230 | 231 | ### Ease of use 232 | 233 | - Provided as a single-header library with no dependencies beside the standard 234 | C++ library. There is nothing to link, and it even includes built-in 235 | binary parsing for TrueType font (TTF) files. It is pure CPU code and 236 | does not require a GPU or GPU context. 237 | - Has extensive Doxygen-style documentation comments for the public API. 238 | - Compiles cleanly at moderately high warning levels on most compilers. 239 | - Shares no internal pointers, nor holds any external pointers. Newcomers to 240 | C++ can have fun drawing with this library without worrying so much about 241 | resource lifetimes or mutability. 242 | - Uses no static or global variables. Threads may safely work with different 243 | canvas instances concurrently without locking. 244 | - Allocates no dynamic memory after reaching the high-water mark. Except for 245 | the pixel buffer, flat `std::vector` instances embedded in the canvas 246 | instance handle all dynamic memory. This reduces fragmentation and 247 | makes it easy to change the code to reserve memory up front or even to 248 | use statically allocated vectors. 249 | - Works with exceptions and RTTI disabled. 250 | - Intentionally uses a plain C++03 style to make it as widely portable as 251 | possible, easier to understand, and (with indexing preferred over pointer 252 | arithmetic) easier to port natively to other languages. The accompanying 253 | test suite may also help with porting. 254 | 255 | ### Compact size 256 | 257 | - The source code for the entire library consists of a bit over 2300 lines 258 | (not counting comments or blanks), each no longer than 78 columns. 259 | Alternately measured, it has fewer than 1300 semicolons. 260 | - The object code for the library can be less than 36 KiB on x86-64 with 261 | appropriate compiler settings for size. 262 | - Due to the library's small size, the accompanying automated test suite 263 | achieves 100% line coverage of the library in gcov and llvm-cov. 264 | 265 | ## :warning: Limitations 266 | 267 | - Trapezoidal antialiasing overestimates coverage where paths self-intersect 268 | within a single pixel. Where inner joins are visible, this can lead to 269 | a "grittier" appearance due to the extra windings used. 270 | - Clipping uses an antialiased sparse pixel mask rather than geometrically 271 | intersecting paths. Therefore, it is not subpixel-accurate. 272 | - Text rendering is extremely basic and mainly for convenience. It only 273 | supports left-to-right text, and does not do any hinting, kerning, 274 | ligatures, text shaping, or text layout. If you require any of those, 275 | consider using another library to provide those and feed the results to 276 | this library as either placed glyphs or raw paths. 277 | - _TRUETYPE FONT PARSING IS NOT SECURE!_ It does some basic validity checking, 278 | but should only be used with known-good or sanitized fonts. 279 | - Parameter checking does not test for non-finite floating-point values. 280 | - Rendering is single-threaded, not explicitly vectorized, and not GPU- 281 | accelerated. It also copies data to avoid ownership issues. If you 282 | need the speed, you are better off using a more fully-featured library. 283 | - The library does no input or output on its own. Instead, you must provide 284 | it with buffers to copy into or out of. 285 | 286 | ## :computer: Usage 287 | 288 | This is a [single-header library](../src/canvas_ity.hpp). You may freely 289 | include it in any of your source files to declare the `canvas_ity` namespace 290 | and its members. However, to get the implementation, you must 291 | ```c++ 292 | #define CANVAS_ITY_IMPLEMENTATION 293 | ``` 294 | in exactly one C++ file before including this header. 295 | 296 | Then, construct an instance of the `canvas_ity::canvas` class with the pixel 297 | dimensions that you want and draw into it using any of the various drawing 298 | functions. You can then use the `get_image_data()` function to retrieve the 299 | currently drawn image at any time. 300 | 301 | See each of the public member function and data member (i.e., method and 302 | field) declarations for the full API documentation. Also see the accompanying 303 | [C++ automated test suite](../test/test.cpp) for examples of the usage of each 304 | public member, and the [test suite's HTML5 port](../test/test.html) for how 305 | these map to the HTML5 canvas API. 306 | 307 | To build the test program, either just compile the one source file directly 308 | to an executable with a C++ compiler, e.g.: 309 | ``` 310 | g++ -O3 -o test test.cpp 311 | ``` 312 | or else use the accompanying [CMake file](../test/CMakeLists.txt). The CMake 313 | file enables extensive warnings and also offers targets for static analysis, 314 | dynamic analysis, measuring code size, and measuring test coverage. 315 | 316 | By default, the test harness simply runs each test once and reports the 317 | results. However, with command line arguments, it can write PNG images of 318 | the test results, run tests repeatedly to benchmark them, run just a subset 319 | of the test, or write out a new table of expected image hashes. Run the 320 | program with `--help` to see the usage guide for more on these. 321 | 322 | ## :copyright: License 323 | 324 | This software is distributed as open source under the terms of the permissive 325 | [ISC license](https://choosealicense.com/licenses/isc/). 326 | 327 | ## :mailbox: Contributing 328 | 329 | _Please do not send pull requests!_ They will be politely declined at 330 | this time. This library is open source, but not currently open to outside 331 | code contributions. It is also considered largely feature-complete. 332 | (Moreover, this GitHub repository is only a mirror for publishing releases 333 | from the author's local Mercurial repository.) 334 | 335 | Bug reports, discussions, kudos, and notices of nifty projects built using 336 | this library are most welcome, however. 337 | -------------------------------------------------------------------------------- /.github/example_canvas_ity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a-e-k/canvas_ity/8be29cb154d06392385687c721f822d8cce0dc05/.github/example_canvas_ity.png -------------------------------------------------------------------------------- /.github/example_html5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a-e-k/canvas_ity/8be29cb154d06392385687c721f822d8cce0dc05/.github/example_html5.png -------------------------------------------------------------------------------- /.github/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a-e-k/canvas_ity/8be29cb154d06392385687c721f822d8cce0dc05/.github/icon.png -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | push: 5 | 6 | jobs: 7 | tests: 8 | 9 | strategy: 10 | matrix: 11 | os: 12 | - ubuntu-latest 13 | - windows-latest 14 | - macos-latest 15 | include: 16 | - os: ubuntu-latest 17 | sanitize: On 18 | testexe: test 19 | - os: windows-latest 20 | sanitize: Off 21 | testexe: Debug/test.exe 22 | - os: macos-latest 23 | sanitize: On 24 | testexe: test 25 | runs-on: ${{matrix.os}} 26 | 27 | steps: 28 | - uses: actions/checkout@v3 29 | - name: CMake 30 | run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=Debug -DWITH_SANITIZERS=${{matrix.sanitize}} ${{github.workspace}}/test 31 | - name: Build 32 | run: cmake --build ${{github.workspace}}/build --config Debug 33 | - name: Test 34 | run: ${{github.workspace}}/build/${{matrix.testexe}} --plain 35 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | ISC license 2 | 3 | Copyright (c) 2022 Andrew Kensler 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | -------------------------------------------------------------------------------- /demos/tiger/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # CMake build file for PostScript tiger demo v1.00 -- ISC license 2 | # Copyright (c) 2024 Andrew Kensler 3 | # 4 | # Permission to use, copy, modify, and/or distribute this software for any 5 | # purpose with or without fee is hereby granted, provided that the above 6 | # copyright notice and this permission notice appear in all copies. 7 | # 8 | # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | 16 | # ======== ABOUT ======== 17 | # 18 | # This is the CMake build script for the PostScript tiger demo for the 19 | # canvas library. Note that this file is not strictly necessary to build 20 | # the demo program; instead, it can just be compiled directly to an 21 | # executable with a C++ compiler, e.g.: 22 | # g++ -Ofast -march=native -mtune=native -o tiger tiger.cpp 23 | # 24 | # However, building with CMake enables extensive warnings when building with 25 | # GCC, Clang, ICC, or MSVC. 26 | # 27 | # For the best results when benchmarking with GCC or Clang, build with: 28 | # -DCMAKE_BUILD_TYPE=Release 29 | # -DCMAKE_RELEASE_CXX_FLAGS="-Ofast -march=native -mtune=native" 30 | 31 | cmake_minimum_required( VERSION 3.15 ) 32 | project( tiger ) 33 | 34 | add_executable( tiger tiger.cpp ) 35 | target_compile_options( tiger PRIVATE 36 | $<$: -Wall -Wextra -pedantic -Wshadow -Wdisabled-optimization -Wformat=2 -Winit-self -Wmissing-include-dirs -Woverloaded-virtual -Wsign-promo -Wundef -fdiagnostics-show-option -Wconversion -Wsign-conversion> 37 | $<$: -Wdouble-promotion -Wcast-align -Wctor-dtor-privacy -Wredundant-decls -Wstrict-overflow=2 -Wold-style-cast -Wnull-dereference> 38 | $<$: -Wlogical-op -Wduplicated-branches -Wduplicated-cond -Wnoexcept -Wstrict-null-sentinel -Wuseless-cast> 39 | $<$: /permissive- /W4> ) 40 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # CMake build file for canvas_ity test suite and harness v1.00 -- ISC license 2 | # Copyright (c) 2022 Andrew Kensler 3 | # 4 | # Permission to use, copy, modify, and/or distribute this software for any 5 | # purpose with or without fee is hereby granted, provided that the above 6 | # copyright notice and this permission notice appear in all copies. 7 | # 8 | # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | 16 | # ======== ABOUT ======== 17 | # 18 | # This is the CMake build script for the canvas_ity test suite and harness. 19 | # Note that this file is not strictly necessary to build the test program; 20 | # instead, it can just be compiled directly to an executable with a C++ 21 | # compiler, e.g.: 22 | # g++ -O3 -o test test.cpp 23 | # 24 | # However, building with CMake enables extensive warnings when building with 25 | # GCC, Clang, ICC, or MSVC, and also offers targets for static analysis, 26 | # dynamic analysis, and measuring test coverage. Beyond that, it includes 27 | # targets for measuring the size of the library itself. 28 | # 29 | # These are the main custom options: 30 | # 31 | # - WITH_SANITIZERS: builds the test program with undefined behavior, address, 32 | # and/or integer sanitizers depending on the compiler 33 | # - WITH_COVERAGE: build the test program with instrumentation for measuring 34 | # test coverage with either gcov or llvm-cov. 35 | # 36 | # These are the main targets offered. Note that some of them may be 37 | # unavailable if the requisite tools are not found or options disabled: 38 | # 39 | # - all / canvas_test: build the test program, either test or test.exe. 40 | # - coverage: build the test program, execute it to measure coverage, and 41 | # then show how much of canvas_ity.hpp was tested while putting a full 42 | # report in canvas_ity.hpp.gcov. 43 | # - valgrind: build the test program, and then execute it under valgrind to 44 | # evaluate it for memory errors. 45 | # - analyze: check the test program and library using the Clang 46 | # static analyzer. 47 | # - cppcheck: check the test program and library using the CppCheck tool. 48 | # - size: compile the library implementation to an object file using custom 49 | # build flags for minimal size (ignoring the usual CMake build flags) and 50 | # then measure the size of the executable code with the size tool. 51 | # - lines: count the lines of source code in the library using the cloc tool. 52 | # - semis: count the number of semicolons in the library source code. 53 | # - test: drive the test program via CTest (for IDE test support). 54 | 55 | cmake_minimum_required( VERSION 3.15 ) 56 | project( test ) 57 | 58 | add_executable( canvas_test test.cpp ) 59 | set_target_properties( canvas_test PROPERTIES OUTPUT_NAME test ) 60 | target_compile_options( canvas_test PRIVATE 61 | $<$: -Wall -Wextra -std=c++98 -pedantic -Wshadow -Wdisabled-optimization -Wformat=2 -Winit-self -Wmissing-include-dirs -Woverloaded-virtual -Wsign-promo -Wundef -fdiagnostics-show-option -Wconversion -Wsign-conversion> 62 | $<$: -Wdouble-promotion -Wcast-align -Wctor-dtor-privacy -Wredundant-decls -Wstrict-overflow=2 -Wold-style-cast -Wnull-dereference> 63 | $<$: -Wlogical-op -Wduplicated-branches -Wduplicated-cond -Wnoexcept -Wstrict-null-sentinel -Wuseless-cast> 64 | $<$: /permissive- /W4> 65 | $<$,$>: -ffast-math -fno-math-errno -fno-exceptions -fno-asynchronous-unwind-tables -ffunction-sections -fdata-sections -fno-ident> 66 | $<$,$>: -flto> 67 | $<$,$>: -ipo> ) 68 | target_link_options( canvas_test PRIVATE 69 | $<$,$>: -s -Wl,--gc-sections -Wl,--hash-style=gnu -fdata-sections -fno-asynchronous-unwind-tables> 70 | $<$,$>: -Wl,-z,norelro> 71 | $<$,$>: -flto> ) 72 | 73 | option( WITH_SANITIZERS "Build with undefined behavior, address, and/or integer sanitizer" ) 74 | if( WITH_SANITIZERS ) 75 | target_compile_options( canvas_test PRIVATE 76 | $<$: -fsanitize=undefined -fsanitize=address> 77 | $<$: -fsanitize=leak> 78 | $<$: -fsanitize=integer> 79 | $<$: /fsanitize=address /MTd> ) 80 | target_link_options( canvas_test PRIVATE 81 | $<$: -fsanitize=undefined -fsanitize=address> 82 | $<$: -fsanitize=leak> 83 | $<$: -fsanitize=integer> 84 | $<$: /INCREMENTAL:NO> ) 85 | endif() 86 | 87 | option( WITH_COVERAGE "Build test with coverage profiling of the library" ) 88 | if( NOT TOOL_COVERAGE ) 89 | string( REPLACE "clang++" "llvm-cov" TOOL_COVERAGE ${CMAKE_CXX_COMPILER} ) 90 | string( REPLACE "g++" "gcov" TOOL_COVERAGE ${TOOL_COVERAGE} ) 91 | if( NOT EXISTS ${TOOL_COVERAGE} OR ${TOOL_COVERAGE} STREQUAL ${CMAKE_CXX_COMPILER} ) 92 | set( TOOL_COVERAGE "" ) 93 | endif() 94 | set( TOOL_COVERAGE ${TOOL_COVERAGE} CACHE PATH "Path to coverage tool for C++ compiler (gcov or llvm-cov)" FORCE ) 95 | endif() 96 | if( WITH_COVERAGE AND TOOL_COVERAGE ) 97 | target_compile_options( canvas_test PRIVATE --coverage -fprofile-filter-files=canvas_ity.hpp ) 98 | target_link_options( canvas_test PRIVATE --coverage ) 99 | set_source_files_properties( test.cpp PROPERTIES OBJECT_OUTPUTS ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/canvas_test.dir/test.cpp.gcno ) 100 | add_custom_command( 101 | TARGET canvas_test POST_BUILD 102 | COMMAND ${CMAKE_COMMAND} -E remove ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/canvas_test.dir/test.cpp.gcda ) 103 | add_custom_command( 104 | OUTPUT ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/canvas_test.dir/test.cpp.gcda 105 | COMMAND canvas_test 106 | USES_TERMINAL ) 107 | add_custom_command( 108 | OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/canvas_ity.hpp.gcov 109 | COMMAND ${TOOL_COVERAGE} $<$AppleClang>:gcov> ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/canvas_test.dir/test.cpp.gcno 110 | DEPENDS ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/canvas_test.dir/test.cpp.gcno ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/canvas_test.dir/test.cpp.gcda ) 111 | add_custom_target( coverage 112 | DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/canvas_ity.hpp.gcov ) 113 | endif() 114 | 115 | find_program( TOOL_VALGRIND valgrind ) 116 | if( TOOL_VALGRIND ) 117 | add_custom_target( valgrind 118 | COMMAND ${TOOL_VALGRIND} --tool=memcheck --error-limit=no --leak-resolution=high --show-reachable=yes --leak-check=full --trace-children=yes --undef-value-errors=yes --track-origins=yes $ 119 | USES_TERMINAL ) 120 | endif() 121 | 122 | if( NOT TOOL_ANALYZE ) 123 | if( ${CMAKE_CXX_COMPILER_ID} MATCHES "^(Clang|AppleClang)$" ) 124 | set( TOOL_ANALYZE ${CMAKE_CXX_COMPILER} CACHE PATH "Path to clang++ for static analysis" FORCE ) 125 | endif() 126 | find_program( TOOL_ANALYZE clang++ ) 127 | endif() 128 | if( TOOL_ANALYZE ) 129 | add_custom_target( analyze 130 | COMMAND ${TOOL_ANALYZE} --analyze -Xanalyzer -analyzer-output=text ${CMAKE_CURRENT_SOURCE_DIR}/test.cpp 131 | USES_TERMINAL ) 132 | endif() 133 | 134 | find_program( TOOL_CPPCHECK cppcheck ) 135 | if( TOOL_CPPCHECK ) 136 | add_custom_target( cppcheck 137 | COMMAND ${TOOL_CPPCHECK} --enable=all --std=c++03 --suppress=missingIncludeSystem --suppress=useInitializationList ${CMAKE_CURRENT_SOURCE_DIR}/test.cpp 138 | USES_TERMINAL ) 139 | endif() 140 | 141 | find_program( TOOL_SIZE size ) 142 | if( TOOL_SIZE ) 143 | add_custom_target( size 144 | COMMAND ${CMAKE_CXX_COMPILER} -Os $<$AppleClang>>:-ffast-math> -fno-math-errno -fno-exceptions -fno-asynchronous-unwind-tables -fmerge-all-constants -ffunction-sections -fdata-sections -c -o ${CMAKE_CURRENT_BINARY_DIR}/canvas_ity.o -x c++ -DCANVAS_ITY_IMPLEMENTATION ${CMAKE_CURRENT_SOURCE_DIR}/../src/canvas_ity.hpp 145 | COMMAND ${TOOL_SIZE} ${CMAKE_CURRENT_BINARY_DIR}/canvas_ity.o 146 | BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/canvas_ity.o ) 147 | endif() 148 | 149 | find_program( TOOL_CLOC cloc ) 150 | if( TOOL_CLOC ) 151 | add_custom_target( lines 152 | COMMAND ${TOOL_CLOC} ${CMAKE_CURRENT_SOURCE_DIR}/../src/canvas_ity.hpp 153 | USES_TERMINAL ) 154 | endif() 155 | 156 | find_program( TOOL_TR tr ) 157 | find_program( TOOL_WC wc ) 158 | if( TOOL_TR AND TOOL_WC ) 159 | add_custom_target( semis 160 | COMMAND ${TOOL_TR} -cd \; < ${CMAKE_CURRENT_SOURCE_DIR}/../src/canvas_ity.hpp | ${TOOL_WC} -c 161 | USES_TERMINAL 162 | VERBATIM ) 163 | endif() 164 | 165 | enable_testing() 166 | file( STRINGS test.cpp TEST_TABLE 167 | REGEX "^ { 0x[0-9a-f]+, [0-9]+, [0-9]+, [a-z_]+, \"[a-z_]+\" },$" ) 168 | foreach( TEST_ENTRY ${TEST_TABLE} ) 169 | string( REGEX MATCH "\"([a-z_]+)\"" TEST_NAME ${TEST_ENTRY} ) 170 | add_test( NAME ${CMAKE_MATCH_1} 171 | COMMAND canvas_test --subset ${CMAKE_MATCH_1} ) 172 | endforeach( TEST_ENTRY ) 173 | -------------------------------------------------------------------------------- /test/test.cpp: -------------------------------------------------------------------------------- 1 | // canvas_ity test suite and harness v1.00 -- ISC license 2 | // Copyright (c) 2022 Andrew Kensler 3 | // 4 | // Permission to use, copy, modify, and/or distribute this software for any 5 | // purpose with or without fee is hereby granted, provided that the above 6 | // copyright notice and this permission notice appear in all copies. 7 | // 8 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | 16 | // ======== ABOUT ======== 17 | // 18 | // This program contains both the test suite and a standalone test harness 19 | // for automated testing of the canvas_ity library. The harness calls each 20 | // test with a fresh instance of a canvas. The test then exercises the public 21 | // interface of the canvas. After it returns, the harness fetches the image 22 | // of the canvas and hashes the contents to compare against an expected hash 23 | // to determine if the test passed or fails. 24 | // 25 | // To build the test program, either just compile the one source file 26 | // directly to an executable with a C++ compiler, e.g.: 27 | // g++ -O3 -o test test.cpp 28 | // or else use the accompanying CMake file. The CMake file enables extensive 29 | // warnings and also offers targets for static analysis, dynamic analysis, 30 | // measuring code size, and measuring test coverage. 31 | // 32 | // By default, the test harness simply runs each test once and reports the 33 | // results. However, with command line arguments, it can write PNG images 34 | // of the test results, run tests repeatedly to benchmark them, run just a 35 | // subset of the test, or write out a new table of expected image hashes. 36 | // Run the program with --help to see the usage guide for more on these. 37 | // 38 | // Beware that while the hash checks are tuned to allow tests to pass even 39 | // with minor numerical differences due to aggressive compiler optimizations 40 | // (e.g., -Ofast on certain architectures), some tests may still report as 41 | // failing. This does not necessarily mean that there is a problem, but it 42 | // does warrant manual verification of the failing test's image against a 43 | // passing baseline test image produced with optimizations disabled. 44 | // 45 | // Also see test.html, the HTML5 2D canvas port of these tests. Compare 46 | // the code for the C++ and JavaScript tests line-by-line to see how this 47 | // library's API maps to the HTML5 API and vice-versa. Compare the images 48 | // produced by each (run with --pngs to get the images) to see how the 49 | // library's rendering relates to browser canvas implementations. 50 | 51 | #ifdef _WIN32 52 | #define WIN32_LEAN_AND_MEAN 53 | #define NOMINMAX 54 | #define _CRT_SECURE_NO_WARNINGS 55 | #endif 56 | 57 | #define CANVAS_ITY_IMPLEMENTATION 58 | #include "../src/canvas_ity.hpp" 59 | 60 | #if defined( __linux__ ) 61 | #include 62 | #include 63 | #elif defined( _WIN32 ) 64 | #include 65 | #elif defined( __MACH__ ) 66 | #include 67 | #include 68 | #else 69 | #include 70 | #include 71 | #endif 72 | 73 | #include 74 | #include 75 | #include 76 | #include 77 | #include 78 | #include 79 | #include 80 | 81 | using namespace canvas_ity; 82 | using namespace std; 83 | 84 | // ======== RESOURCES ======== 85 | // 86 | // The resources embedded here are mainly font files in TTF form. While 87 | // the data could be stored directly as an array of integer literals, the 88 | // Base64 encoding is much more compact in terms of source. It also means 89 | // that the HTML5 port of these tests can use these resources almost as-is. 90 | // 91 | // The valid fonts all have the following properties in common: 92 | // 93 | // - The asterisk is a composite of the acute mark with a mix of simple and 94 | // complex (i.e., 2x2) scaling transforms used to rotate them to the 95 | // eight principal directions at 45-degree angles. 96 | // - A glyph assigned to the high end of the private use area (at 10FFFD) 97 | // tests all combinations of four on-curve or off-curve points. 98 | // - Characters 'D' through 'H' are copies of a simple dot and assigned to 99 | // the last glyph indices; being copies and at the end means that they can 100 | // test the hmtx table having fewer horizontal metrics than glyphs, with 101 | // the last advance width being replicated to all the glyphs beyond it. 102 | // - Having assignments to 'C' through 'I', but in non-consecutive order also 103 | // allows for testing the range table in the format-4 cmap subtable. 104 | // - The 'a' glyph has a sequence of off-curve points going in the same 105 | // direction so that the points have a consecutive sequence of identical 106 | // flags that are compacted with a repeat flag. 107 | // - The 's' glyph is translated 1024 font units to the right. However, it 108 | // has a normal advance width and left side bearing so it should render 109 | // like a normal character despite this. 110 | // - The .notdef glyph has a couple of hinting instructions that just push a 111 | // few values on the stack but do nothing else. These instructions must 112 | // be skipped over to get to the point data in the glyph. 113 | 114 | // Valid TTF file, cmap table has types 12 and 4 subtables. 115 | vector< unsigned char > font_a; 116 | char const font_a_base64[] = 117 | "AAEAAAALAIAAAwAwT1MvMmisck8AAAE4AAAAYGNtYXAXewGCAAAB3AAAAUJjdnQgAEQFEQAA" 118 | "AyAAAAAEZ2x5ZjCUlAIAAANMAAAGhmhlYWQe1bIjAAAAvAAAADZoaGVhDf8FBAAAAPQAAAAk" 119 | "aG10eDmaBAMAAAGYAAAARGxvY2ERbxMOAAADJAAAAChtYXhwAHUAtwAAARgAAAAgbmFtZVZp" 120 | "NvsAAAnUAAAA23Bvc3T/aQBmAAAKsAAAACAAAQAAAAEAAEPW4v5fDzz1AB0IAAAAAADcB1gv" 121 | "AAAAANwUDpf/+f5tB5AH8wAAAAgAAgAAAAAAAAABAAAFu/+6ALgIAP/5/ToHkAABAAAAAAAA" 122 | "AAAAAAAAAAAADwABAAAAEwBAABAAcAAIAAIAAAABAAEAAABAAAMACAABAAQD/wGQAAUAAAUz" 123 | "BZkAAAEeBTMFmQAAA9cAZgISAAACAAUDAAAAAAAAAAAAQwIAAAAEAAAAAAAAAFBmRWQAgAAg" 124 | "//8GQP5AALgFuwBGAAAAAQAAAAADmwW3AAAAIAABAuwARAQAAAAFogAiBikAVwK0ABQDqAA8" 125 | "BGwANALYAE8CsQA8A8j/+QPI//kCtAAUAAABBQgAAAADhABkAGQAZABkAGQAAAACAAMAAQAA" 126 | "ABQAAwAKAAAAigAEAHYAAAAWABAAAwAGACAAKgBJAGEAbgB0AHYAeQDNAwH//wAAACAAKgBD" 127 | "AGEAbgBzAHYAeQDNAwH////h/9gAAP+k/5j/lP+T/5H/Pv0LAAEAAAAAABIAAAAAAAAAAAAA" 128 | "AAAAAAAAAAMAEgAOAA8AEAARAAQADAAAAAAAuAAAAAAAAAAOAAAAIAAAACAAAAABAAAAKgAA" 129 | "ACoAAAACAAAAQwAAAEMAAAADAAAARAAAAEQAAAASAAAARQAAAEgAAAAOAAAASQAAAEkAAAAE" 130 | "AAAAYQAAAGEAAAAFAAAAbgAAAG4AAAAGAAAAcwAAAHQAAAAHAAAAdgAAAHYAAAAJAAAAeQAA" 131 | "AHkAAAAKAAAAzQAAAM0AAAALAAADAQAAAwEAAAAMABD//QAQ//0AAAANAAAARAURAAAAFgAW" 132 | "AFQAkwDSAR8BbQGtAeoCIAJhAm8CjQMRAx0DJQMtAzUDQwACAEQAAAJkBVUAAwAHAAOxAQAz" 133 | "ESERJSERIUQCIP4kAZj+aAVV+qtEBM0A//8AIgBYBYEFpxCnAAwFogRQ0sAtPtLA0sAQpwAM" 134 | "AX4F2NLA0sAtPtLAEKcADAACAawtPtLALT4tPhCnAAwEJAAoLT4tPtLALT4QpwAM/+oEDQAA" 135 | "wABAAAAAEKcADAW+Ae4AAEAAwAAAABAvAAwD3gXswAAQBwAMAcIADAABAFf/4gW7BbsAIwAA" 136 | "ExA3NiEyBRYVFAcGJwIhIAMGFRQXFiEgEzYXFgcGBwQhIAEmV7jWAY6lATUPEhIGoP7k/t+6" 137 | "jZamAWkBM50JGBcCGBv+9/7M/rX+6pECxAEo1vmQB90JAwILATv++8XE9tvyAToSBQQSzRGf" 138 | "ARGOAAABABT/+gJ8BbQAIwAAMyInJjc2NxI3NgMmJyY3NjMkJRYXFgcGBwIXFhMWFxYXFgcG" 139 | "NxcBARefBA0BARUJoBUBARUBIgEIGwEBG7YEDAICDAO4HQIBH/8MCgg+aQFPvqYBXJUXAxUS" 140 | "BAYBFw0HNYX+u7y1/qh1HwUZDQEGAAACADz/7wN5A5EACAAuAAA3Fjc2JyYHDgI+AycmJyYH" 141 | "BhcWBwYnJjc2MzIDAhcWNzY3NgcGBwYnBicmJ+IDjJYDATJLpqRFkImHAgJAKE5zBAVyIhAJ" 142 | "HbLN6hcUBAVNQA4qDCqZZVKQbLYEw4UND9pgDxNUO2YoLC6NfjgiBAZBOCMKLhwcq/7J/vRg" 143 | "jxcTAwoifQUDdXUBAq4AAQA0//8ETgO2ADMAADMiNTQzMgMmNzYnNjMyBwYHJDc2ExIXFjcy" 144 | "FRQjMCEiNTQ3NicwAyYHBgcwAwI3NhcWJyBQHDBkDQYBAUueQDoSFQIBBovUBwkDAmcSFf6m" 145 | "JSFHAgUB2XpbCQ5qLQMDDv7SHhUBlbxgTCFlLzc1dAQH/ur+oo9oARoWIxgHEUQB2MoJBUP+" 146 | "cv7cBQIcIgEAAQRP/+4GiQObACUAACUmNzYzMhcWNzY3NicmNzY3NhcWBwYnJicmBwYHBhcW" 147 | "FxYHBiUmBFUGCAMVFAxWbJcLBqzgGiv3bWQPBgEXFA5lPGEpHJlKTFQFCf7c1zM6VBwcug4T" 148 | "o01ph5P6BAI4EogUBAQYoAIDkmRmMkNJg+UBAQABADz/7AKEBBEAIwAAEyYnJjc2NzYXFgcG" 149 | "FxY3FhUUBwYnJgcCFxYXFjcGJyYTEjU0aCIGBBxcQhUKIAMIVD+VMjKMTk8BCAgJoVVJOc3z" 150 | "ERQDLgUXEBZKQhUECyBQAgEHCi41AwcBAVH+u4mnAQEnlAQFAQEBNKpSAAH/+f+6A7QDjAAe" 151 | "AAAlJgEmJwUyFRQHBhUUEzYTNicmJzQ3NjcGBwAHBgciAbYX/tMRaAFkHh494U93Bz4sASik" 152 | "hV8Y/uEJDR4kDoACfSRdAhYSCxZAJv4/LwHaGRIMGhABAgU9Rv1/VHkBAAH/+f5tA7QDjAAm" 153 | "AAAlNAEmJwUyFRQHBhUUEzYTNicmJzQ3NjcGBwIHAgcGIyY1Njc2NzYBqv7IEWgBZB4ePeFF" 154 | "gQc+LAEopIVfGOJGngEYOFgBWSAGWixJApYkXQIWEgsWQCb+PygB4RoRDBoQAQIFPUb927D+" 155 | "cQM1AVAZGgkNy///ABT/+gLXB/MQZwAMABEC1T/4QAASBgAEAAAAAQEFAyMCxgUeAA0AAAE2" 156 | "EzY3NhcWBwYHBicmARAwqBoOWkoSHsKSFBwfA0prASUtAxQYBSf+ohcHBwAAEAAA/nAHkAYA" 157 | "AAMABwALAA8AEwAXABsAHwAjACcAKwAvADMANwA7AD8AABAQIBAAECARABAhEAAQIRESESAQ" 158 | "ABEgEQARIRAAESERExAgEAEQIBEBECEQARAhERMRIBABESARAREhEAERIREBkP5wAZD+cAGQ" 159 | "/nABkHABkP5wAZD+cAGQ/nABkHABkP5wAZD+cAGQ/nABkHABkP5wAZD+cAGQ/nABkP5wAZD+" 160 | "cAIAAZD+cAIAAZD+cAIAAZD+cPoAAZD+cAIAAZD+cAIAAZD+cAIAAZD+cPoAAZD+cAIAAZD+" 161 | "cAIAAZD+cAIAAZD+cPoAAZD+cAIAAZD+cAIAAZD+cAIAAZD+cAD//wBkADIDIAWqECcAEgAA" 162 | "/qIABAASAAP//wBkAZADIARMEAYAEgAA//8AZAGQAyAETBAGABIAAP//AGQBkAMgBEwQBgAS" 163 | "AAAAAQBkAZADIARMAAMAABIgECBkArz9RARM/UQAAAAAAAAMAJYAAQAAAAAAAQAFAAAAAQAA" 164 | "AAAAAgAHAAUAAQAAAAAAAwAFAAAAAQAAAAAABAAFAAAAAQAAAAAABQALAAwAAQAAAAAABgAF" 165 | "AAAAAwABBAkAAQAKABcAAwABBAkAAgAOACEAAwABBAkAAwAKABcAAwABBAkABAAKABcAAwAB" 166 | "BAkABQAWAC8AAwABBAkABgAKABdGb250QVJlZ3VsYXJWZXJzaW9uIDEuMABGAG8AbgB0AEEA" 167 | "UgBlAGcAdQBsAGEAcgBWAGUAcgBzAGkAbwBuACAAMQAuADAAAAMAAAAAAAD/ZgBmAAAAAAAA" 168 | "AAAAAAAAAAAAAAAAAAA="; 169 | 170 | // Valid TTF file, cmap table has type 4 subtable only and loca table is long. 171 | vector< unsigned char > font_b; 172 | char const font_b_base64[] = 173 | "AAEAAAALAIAAAwAwT1MvMmirdVEAAAE4AAAAYGNtYXAHhQC5AAAB3AAAAIJjdnQgAEQFEQAA" 174 | "AmAAAAAEZ2x5ZjCUlAIAAAK0AAAGhmhlYWQe1LMzAAAAvAAAADZoaGVhDf8FBAAAAPQAAAAk" 175 | "aG10eDmaBAMAAAGYAAAARGxvY2EAAEj6AAACZAAAAFBtYXhwAHUAtwAAARgAAAAgbmFtZVZp" 176 | "OPsAAAk8AAAA23Bvc3T/aQBmAAAKGAAAACAAAAEAAAEAAIakcHRfDzz1AB0IAAAAAADcB1gv" 177 | "AAAAANwUDqb/+f5tB5AH8wAAAAgAAgABAAAAAAABAAAFu/+6ALgIAP/5/ToHkAABAAAAAAAA" 178 | "AAAAAAAAAAAADwABAAAAEwBAABAAcAAIAAIAAAABAAEAAABAAAMACAABAAQD/wGQAAUAAAUz" 179 | "BZkAAAEeBTMFmQAAA9cAZgISAAACAAUDAAAAAAAAAAAAQwIAAAAEAAAAAAAAAFBmRWQAgAAg" 180 | "AwEGQP5AALgFuwBGAAAAAQAAAAADmwW3AAAAIAABAuwARAQAAAAFogAiBikAVwK0ABQDqAA8" 181 | "BGwANALYAE8CsQA8A8j/+QPI//kCtAAUAAABBQgAAAADhABkAGQAZABkAGQAAAABAAMAAQAA" 182 | "AAwABAB2AAAAFgAQAAMABgAgACoASQBhAG4AdAB2AHkAzQMB//8AAAAgACoAQwBhAG4AcwB2" 183 | "AHkAzQMB////4f/YAAD/pP+Y/5T/k/+R/z79CwABAAAAAAASAAAAAAAAAAAAAAAAAAAAAAAD" 184 | "ABIADgAPABAAEQAEAAAARAURAAAAAAAAACwAAAAsAAAAqAAAASYAAAGkAAACPgAAAtoAAANa" 185 | "AAAD1AAABEAAAATCAAAE3gAABRoAAAYiAAAGOgAABkoAAAZaAAAGagAABoYAAgBEAAACZAVV" 186 | "AAMABwADsQEAMxEhESUhESFEAiD+JAGY/mgFVfqrRATNAP//ACIAWAWBBacQpwAMBaIEUNLA" 187 | "LT7SwNLAEKcADAF+BdjSwNLALT7SwBCnAAwAAgGsLT7SwC0+LT4QpwAMBCQAKC0+LT7SwC0+" 188 | "EKcADP/qBA0AAMAAQAAAABCnAAwFvgHuAABAAMAAAAAQLwAMA94F7MAAEAcADAHCAAwAAQBX" 189 | "/+IFuwW7ACMAABMQNzYhMgUWFRQHBicCISADBhUUFxYhIBM2FxYHBgcEISABJle41gGOpQE1" 190 | "DxISBqD+5P7fuo2WpgFpATOdCRgXAhgb/vf+zP61/uqRAsQBKNb5kAfdCQMCCwE7/vvFxPbb" 191 | "8gE6EgUEEs0RnwERjgAAAQAU//oCfAW0ACMAADMiJyY3NjcSNzYDJicmNzYzJCUWFxYHBgcC" 192 | "FxYTFhcWFxYHBjcXAQEXnwQNAQEVCaAVAQEVASIBCBsBARu2BAwCAgwDuB0CAR//DAoIPmkB" 193 | "T76mAVyVFwMVEgQGARcNBzWF/ru8tf6odR8FGQ0BBgAAAgA8/+8DeQORAAgALgAANxY3Nicm" 194 | "Bw4CPgMnJicmBwYXFgcGJyY3NjMyAwIXFjc2NzYHBgcGJwYnJifiA4yWAwEyS6akRZCJhwIC" 195 | "QChOcwQFciIQCR2yzeoXFAQFTUAOKgwqmWVSkGy2BMOFDQ/aYA8TVDtmKCwujX44IgQGQTgj" 196 | "Ci4cHKv+yf70YI8XEwMKIn0FA3V1AQKuAAEANP//BE4DtgAzAAAzIjU0MzIDJjc2JzYzMgcG" 197 | "ByQ3NhMSFxY3MhUUIzAhIjU0NzYnMAMmBwYHMAMCNzYXFicgUBwwZA0GAQFLnkA6EhUCAQaL" 198 | "1AcJAwJnEhX+piUhRwIFAdl6WwkOai0DAw7+0h4VAZW8YEwhZS83NXQEB/7q/qKPaAEaFiMY" 199 | "BxFEAdjKCQVD/nL+3AUCHCIBAAEET//uBokDmwAlAAAlJjc2MzIXFjc2NzYnJjc2NzYXFgcG" 200 | "JyYnJgcGBwYXFhcWBwYlJgRVBggDFRQMVmyXCwas4Bor921kDwYBFxQOZTxhKRyZSkxUBQn+" 201 | "3NczOlQcHLoOE6NNaYeT+gQCOBKIFAQEGKACA5JkZjJDSYPlAQEAAQA8/+wChAQRACMAABMm" 202 | "JyY3Njc2FxYHBhcWNxYVFAcGJyYHAhcWFxY3BicmExI1NGgiBgQcXEIVCiADCFQ/lTIyjE5P" 203 | "AQgICaFVSTnN8xEUAy4FFxAWSkIVBAsgUAIBBwouNQMHAQFR/ruJpwEBJ5QEBQEBATSqUgAB" 204 | "//n/ugO0A4wAHgAAJSYBJicFMhUUBwYVFBM2EzYnJic0NzY3BgcABwYHIgG2F/7TEWgBZB4e" 205 | "PeFPdwc+LAEopIVfGP7hCQ0eJA6AAn0kXQIWEgsWQCb+Py8B2hkSDBoQAQIFPUb9f1R5AQAB" 206 | "//n+bQO0A4wAJgAAJTQBJicFMhUUBwYVFBM2EzYnJic0NzY3BgcCBwIHBiMmNTY3Njc2Aar+" 207 | "yBFoAWQeHj3hRYEHPiwBKKSFXxjiRp4BGDhYAVkgBlosSQKWJF0CFhILFkAm/j8oAeEaEQwa" 208 | "EAECBT1G/duw/nEDNQFQGRoJDcv//wAU//oC1wfzEGcADAARAtU/+EAAEgYABAAAAAEBBQMj" 209 | "AsYFHgANAAABNhM2NzYXFgcGBwYnJgEQMKgaDlpKEh7CkhQcHwNKawElLQMUGAUn/qIXBwcA" 210 | "ABAAAP5wB5AGAAADAAcACwAPABMAFwAbAB8AIwAnACsALwAzADcAOwA/AAAQECAQABAgEQAQ" 211 | "IRAAECEREhEgEAARIBEAESEQABEhERMQIBABECARARAhEAEQIRETESAQAREgEQERIRABESER" 212 | "AZD+cAGQ/nABkP5wAZBwAZD+cAGQ/nABkP5wAZBwAZD+cAGQ/nABkP5wAZBwAZD+cAGQ/nAB" 213 | "kP5wAZD+cAGQ/nACAAGQ/nACAAGQ/nACAAGQ/nD6AAGQ/nACAAGQ/nACAAGQ/nACAAGQ/nD6" 214 | "AAGQ/nACAAGQ/nACAAGQ/nACAAGQ/nD6AAGQ/nACAAGQ/nACAAGQ/nACAAGQ/nAA//8AZAAy" 215 | "AyAFqhAnABIAAP6iAAQAEgAD//8AZAGQAyAETBAGABIAAP//AGQBkAMgBEwQBgASAAD//wBk" 216 | "AZADIARMEAYAEgAAAAEAZAGQAyAETAADAAASIBAgZAK8/UQETP1EAAAAAAAADACWAAEAAAAA" 217 | "AAEABQAAAAEAAAAAAAIABwAFAAEAAAAAAAMABQAAAAEAAAAAAAQABQAAAAEAAAAAAAUACwAM" 218 | "AAEAAAAAAAYABQAAAAMAAQQJAAEACgAXAAMAAQQJAAIADgAhAAMAAQQJAAMACgAXAAMAAQQJ" 219 | "AAQACgAXAAMAAQQJAAUAFgAvAAMAAQQJAAYACgAXRm9udEJSZWd1bGFyVmVyc2lvbiAxLjAA" 220 | "RgBvAG4AdABCAFIAZQBnAHUAbABhAHIAVgBlAHIAcwBpAG8AbgAgADEALgAwAAADAAAAAAAA" 221 | "/2YAZgAAAAAAAAAAAAAAAAAAAAAAAAAA"; 222 | 223 | // Valid TTF file, cmap table has type 0 subtable only. 224 | vector< unsigned char > font_c; 225 | char const font_c_base64[] = 226 | "AAEAAAALAIAAAwAwT1MvMmisck8AAAE4AAAAYGNtYXAgGy9CAAAB3AAAARJjdnQgAEQFEQAA" 227 | "AvAAAAAEZ2x5ZjCUlAIAAAMcAAAGhmhlYWQe1bJmAAAAvAAAADZoaGVhDf8FBAAAAPQAAAAk" 228 | "aG10eDmaBAMAAAGYAAAARGxvY2ERbxMOAAAC9AAAAChtYXhwAHUAtwAAARgAAAAgbmFtZVZp" 229 | "OvsAAAmkAAAA23Bvc3T/aQBmAAAKgAAAACAAAQAAAAEAADKWgBhfDzz1AB0IAAAAAADcB1gv" 230 | "AAAAANwUDtr/+f5tB5AH8wAAAAgAAgAAAAAAAAABAAAFu/+6ALgIAP/5/ToHkAABAAAAAAAA" 231 | "AAAAAAAAAAAADwABAAAAEwBAABAAcAAIAAIAAAABAAEAAABAAAMACAABAAQD/wGQAAUAAAUz" 232 | "BZkAAAEeBTMFmQAAA9cAZgISAAACAAUDAAAAAAAAAAAAQwIAAAAEAAAAAAAAAFBmRWQAgAAg" 233 | "//8GQP5AALgFuwBGAAAAAQAAAAADmwW3AAAAIAABAuwARAQAAAAFogAiBikAVwK0ABQDqAA8" 234 | "BGwANALYAE8CsQA8A8j/+QPI//kCtAAUAAABBQgAAAADhABkAGQAZABkAGQAAAABAAEAAAAA" 235 | "AAwAAAEGAAABAAAAAAAAAAEBAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAEAAAAAAAAAAAACAAAA" 236 | "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAxIODxARBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQAA" 237 | "AAAAAAAAAAAAAAYAAAAABwgACQAACgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" 238 | "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAA" 239 | "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARAUR" 240 | "AAAAFgAWAFQAkwDSAR8BbQGtAeoCIAJhAm8CjQMRAx0DJQMtAzUDQwACAEQAAAJkBVUAAwAH" 241 | "AAOxAQAzESERJSERIUQCIP4kAZj+aAVV+qtEBM0A//8AIgBYBYEFpxCnAAwFogRQ0sAtPtLA" 242 | "0sAQpwAMAX4F2NLA0sAtPtLAEKcADAACAawtPtLALT4tPhCnAAwEJAAoLT4tPtLALT4QpwAM" 243 | "/+oEDQAAwABAAAAAEKcADAW+Ae4AAEAAwAAAABAvAAwD3gXswAAQBwAMAcIADAABAFf/4gW7" 244 | "BbsAIwAAExA3NiEyBRYVFAcGJwIhIAMGFRQXFiEgEzYXFgcGBwQhIAEmV7jWAY6lATUPEhIG" 245 | "oP7k/t+6jZamAWkBM50JGBcCGBv+9/7M/rX+6pECxAEo1vmQB90JAwILATv++8XE9tvyAToS" 246 | "BQQSzRGfARGOAAABABT/+gJ8BbQAIwAAMyInJjc2NxI3NgMmJyY3NjMkJRYXFgcGBwIXFhMW" 247 | "FxYXFgcGNxcBARefBA0BARUJoBUBARUBIgEIGwEBG7YEDAICDAO4HQIBH/8MCgg+aQFPvqYB" 248 | "XJUXAxUSBAYBFw0HNYX+u7y1/qh1HwUZDQEGAAACADz/7wN5A5EACAAuAAA3Fjc2JyYHDgI+" 249 | "AycmJyYHBhcWBwYnJjc2MzIDAhcWNzY3NgcGBwYnBicmJ+IDjJYDATJLpqRFkImHAgJAKE5z" 250 | "BAVyIhAJHbLN6hcUBAVNQA4qDCqZZVKQbLYEw4UND9pgDxNUO2YoLC6NfjgiBAZBOCMKLhwc" 251 | "q/7J/vRgjxcTAwoifQUDdXUBAq4AAQA0//8ETgO2ADMAADMiNTQzMgMmNzYnNjMyBwYHJDc2" 252 | "ExIXFjcyFRQjMCEiNTQ3NicwAyYHBgcwAwI3NhcWJyBQHDBkDQYBAUueQDoSFQIBBovUBwkD" 253 | "AmcSFf6mJSFHAgUB2XpbCQ5qLQMDDv7SHhUBlbxgTCFlLzc1dAQH/ur+oo9oARoWIxgHEUQB" 254 | "2MoJBUP+cv7cBQIcIgEAAQRP/+4GiQObACUAACUmNzYzMhcWNzY3NicmNzY3NhcWBwYnJicm" 255 | "BwYHBhcWFxYHBiUmBFUGCAMVFAxWbJcLBqzgGiv3bWQPBgEXFA5lPGEpHJlKTFQFCf7c1zM6" 256 | "VBwcug4To01ph5P6BAI4EogUBAQYoAIDkmRmMkNJg+UBAQABADz/7AKEBBEAIwAAEyYnJjc2" 257 | "NzYXFgcGFxY3FhUUBwYnJgcCFxYXFjcGJyYTEjU0aCIGBBxcQhUKIAMIVD+VMjKMTk8BCAgJ" 258 | "oVVJOc3zERQDLgUXEBZKQhUECyBQAgEHCi41AwcBAVH+u4mnAQEnlAQFAQEBNKpSAAH/+f+6" 259 | "A7QDjAAeAAAlJgEmJwUyFRQHBhUUEzYTNicmJzQ3NjcGBwAHBgciAbYX/tMRaAFkHh494U93" 260 | "Bz4sASikhV8Y/uEJDR4kDoACfSRdAhYSCxZAJv4/LwHaGRIMGhABAgU9Rv1/VHkBAAH/+f5t" 261 | "A7QDjAAmAAAlNAEmJwUyFRQHBhUUEzYTNicmJzQ3NjcGBwIHAgcGIyY1Njc2NzYBqv7IEWgB" 262 | "ZB4ePeFFgQc+LAEopIVfGOJGngEYOFgBWSAGWixJApYkXQIWEgsWQCb+PygB4RoRDBoQAQIF" 263 | "PUb927D+cQM1AVAZGgkNy///ABT/+gLXB/MQZwAMABEC1T/4QAASBgAEAAAAAQEFAyMCxgUe" 264 | "AA0AAAE2EzY3NhcWBwYHBicmARAwqBoOWkoSHsKSFBwfA0prASUtAxQYBSf+ohcHBwAAEAAA" 265 | "/nAHkAYAAAMABwALAA8AEwAXABsAHwAjACcAKwAvADMANwA7AD8AABAQIBAAECARABAhEAAQ" 266 | "IRESESAQABEgEQARIRAAESERExAgEAEQIBEBECEQARAhERMRIBABESARAREhEAERIREBkP5w" 267 | "AZD+cAGQ/nABkHABkP5wAZD+cAGQ/nABkHABkP5wAZD+cAGQ/nABkHABkP5wAZD+cAGQ/nAB" 268 | "kP5wAZD+cAIAAZD+cAIAAZD+cAIAAZD+cPoAAZD+cAIAAZD+cAIAAZD+cAIAAZD+cPoAAZD+" 269 | "cAIAAZD+cAIAAZD+cAIAAZD+cPoAAZD+cAIAAZD+cAIAAZD+cAIAAZD+cAD//wBkADIDIAWq" 270 | "ECcAEgAA/qIABAASAAP//wBkAZADIARMEAYAEgAA//8AZAGQAyAETBAGABIAAP//AGQBkAMg" 271 | "BEwQBgASAAAAAQBkAZADIARMAAMAABIgECBkArz9RARM/UQAAAAAAAAMAJYAAQAAAAAAAQAF" 272 | "AAAAAQAAAAAAAgAHAAUAAQAAAAAAAwAFAAAAAQAAAAAABAAFAAAAAQAAAAAABQALAAwAAQAA" 273 | "AAAABgAFAAAAAwABBAkAAQAKABcAAwABBAkAAgAOACEAAwABBAkAAwAKABcAAwABBAkABAAK" 274 | "ABcAAwABBAkABQAWAC8AAwABBAkABgAKABdGb250Q1JlZ3VsYXJWZXJzaW9uIDEuMABGAG8A" 275 | "bgB0AEMAUgBlAGcAdQBsAGEAcgBWAGUAcgBzAGkAbwBuACAAMQAuADAAAAMAAAAAAAD/ZgBm" 276 | "AAAAAAAAAAAAAAAAAAAAAAAAAAA="; 277 | 278 | // Invalid TTF file, valid magic number but only one byte after that. 279 | vector< unsigned char > font_d; 280 | char const font_d_base64[] = 281 | "AAEAAAA="; 282 | 283 | // Invalid TTF file, offset table cut short. 284 | vector< unsigned char > font_e; 285 | char const font_e_base64[] = 286 | "AAEAAAALAIAAAwAwT1MvMmisck8AAAE4AAAAYGNtYXAXewGCAAAB3AAAAUJjdnQgAEQFEQAA" 287 | "AyAAAAAEZ2x5ZjCUlAIAAANMAAAGhmhlYWQe1bIjAAAAvAAAADZoaGVhDf8FBAAAAPQAAAAk" 288 | "aG10eDmaBAMAAAGYAAAARGxvY2ERbxMOAAADJAAAAChtYXhwAHUAtwAAARgAAAAgbmFtZVZp" 289 | "NvsAAAnUAAAA23Bvc3T/aQBmAAAKsAAAAA=="; 290 | 291 | // Invalid TTF file, offset table is complete but points to missing tables. 292 | vector< unsigned char > font_f; 293 | char const font_f_base64[] = 294 | "AAEAAAALAIAAAwAwT1MvMmisck8AAAE4AAAAYGNtYXAXewGCAAAB3AAAAUJjdnQgAEQFEQAA" 295 | "AyAAAAAEZ2x5ZjCUlAIAAANMAAAGhmhlYWQe1bIjAAAAvAAAADZoaGVhDf8FBAAAAPQAAAAk" 296 | "aG10eDmaBAMAAAGYAAAARGxvY2ERbxMOAAADJAAAAChtYXhwAHUAtwAAARgAAAAgbmFtZVZp" 297 | "NvsAAAnUAAAA23Bvc3T/aQBmAAAKsAAAACAAAQAAAAEAAEPW4v5fDzz1AB0IAAAAAADcB1gv" 298 | "AAAAANwUDpf/+f5tB5AH8wAAAAgAAgAAAAAAAA=="; 299 | 300 | // Invalid TTF file, lacking most required TTF tables except for head. 301 | vector< unsigned char > font_g; 302 | char const font_g_base64[] = 303 | "AAEAAAABABAAAAAAaGVhZB7h+0cAAAAcAAAANgABAAAAAQAAC4VXZl8PPPUAHQgAAAAAANwH" 304 | "WC8AAAAA3CBXu//5/m0HkAfzAAAACAACAAAAAAAA"; 305 | 306 | // ======== TEST SUITE ======== 307 | // 308 | // Note that while individual tests only use the same public interface 309 | // as clients normally would, most tests are not good examples of the 310 | // ordinary use of the library. They tend to make superfluous calls, make 311 | // calls in unusual orders, and assume documented implicit behavior. They 312 | // typically test strict conformance to the W3C (not WHATWG) HTML5 2D canvas 313 | // specification (https://www.w3.org/TR/2015/REC-2dcontext-20151119/). 314 | // 315 | // For better examples of normal use of the library, see the tests prefixed 316 | // with "example_". These are written in more orthodox ways and intended to 317 | // demonstrate interesting things that the library can draw. 318 | // 319 | // To add a new test to the suite, write a function for it here with the same 320 | // function signature as the other tests, and then register it below in the 321 | // harness's table of tests to run. (Remember to also port it to test.html!) 322 | 323 | namespace 324 | { 325 | 326 | void scale_uniform( canvas &that, float width, float height ) 327 | { 328 | that.set_color( stroke_style, 0.0f, 0.0f, 0.0f, 1.0f ); 329 | float segments[] = { 1.0f }; 330 | that.set_line_dash( segments, 1 ); 331 | that.line_cap = circle; 332 | for ( float size = 8.0f; size < min( width, height ); size *= 2.0f ) 333 | { 334 | that.scale( 2.0f, 2.0f ); 335 | that.stroke_rectangle( 0.0f, 0.0f, 8.0f, 8.0f ); 336 | } 337 | } 338 | 339 | void scale_non_uniform( canvas &that, float width, float height ) 340 | { 341 | that.set_color( stroke_style, 0.0f, 0.0f, 0.0f, 1.0f ); 342 | float segments[] = { 4.0f }; 343 | that.set_line_dash( segments, 1 ); 344 | that.scale( 4.0f, 0.5f ); 345 | that.stroke_rectangle( width * 0.125f / 4.0f, height * 0.125f / 0.5f, 346 | width * 0.75f / 4.0f, height * 0.75f / 0.5f ); 347 | } 348 | 349 | void rotate( canvas &that, float width, float height ) 350 | { 351 | that.set_color( stroke_style, 0.0f, 0.0f, 0.0f, 1.0f ); 352 | for ( int step = 0; step < 64; ++step ) 353 | { 354 | that.rotate( 3.14159265f / 2.0f / 64.0f ); 355 | that.stroke_rectangle( 0.0f, 0.0f, width, height ); 356 | } 357 | } 358 | 359 | void translate( canvas &that, float width, float height ) 360 | { 361 | that.set_color( stroke_style, 0.0f, 0.0f, 0.0f, 1.0f ); 362 | for ( float step = 0.0f; step < 32.0f; step += 1.0f ) 363 | { 364 | that.translate( ( 0.5f - step / 32.0f ) * width * 0.2f, 365 | height / 32.0f ); 366 | that.begin_path(); 367 | that.arc( 0.0f, 0.0f, width * 0.125f, 0.0f, 6.28318531f ); 368 | that.close_path(); 369 | that.stroke(); 370 | } 371 | } 372 | 373 | void transform( canvas &that, float width, float height ) 374 | { 375 | that.set_color( stroke_style, 0.0f, 0.0f, 0.0f, 1.0f ); 376 | for ( int step = 0; step < 8; ++step ) 377 | { 378 | that.transform( 1.0f, 0.0f, 0.1f, 1.0f, width * -0.05f, 0.0f ); 379 | that.stroke_rectangle( width * 0.25f, height * 0.25f, 380 | width * 0.5f, height * 0.5f ); 381 | } 382 | } 383 | 384 | void transform_fill( canvas &that, float width, float height ) 385 | { 386 | unsigned char checker[ 1024 ]; 387 | for ( int index = 0; index < 1024; ++index ) 388 | checker[ index ] = static_cast< unsigned char >( 389 | ( ( index >> 5 & 1 ) ^ ( index >> 9 & 1 ) ^ 390 | ( ( index & 3 ) == 3 ) ) * 255 ); 391 | that.set_pattern( fill_style, checker, 16, 16, 64, repeat ); 392 | that.begin_path(); 393 | that.rectangle( width * 0.2f, height * 0.2f, 394 | width * 0.6f, height * 0.6f ); 395 | that.transform( 1.0f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f ); 396 | that.fill(); 397 | } 398 | 399 | void transform_stroke( canvas &that, float width, float height ) 400 | { 401 | that.set_color( stroke_style, 0.0f, 0.0f, 0.0f, 1.0f ); 402 | that.set_line_width( 8.0f ); 403 | float segments[] = { 22.0f, 8.0f, 10.0f, 8.0f }; 404 | that.set_line_dash( segments, 4 ); 405 | that.begin_path(); 406 | that.arc( width * 0.5f, height * 0.5f, min( width, height ) * 0.4f, 407 | 0.0f, 6.28318531f ); 408 | that.close_path(); 409 | that.transform( 1.0f, 1.0f, 0.0f, 2.0f, 0.0f, 0.0f ); 410 | that.stroke(); 411 | } 412 | 413 | void set_transform( canvas &that, float width, float height ) 414 | { 415 | that.set_color( stroke_style, 0.0f, 0.0f, 0.0f, 1.0f ); 416 | for ( int step = 0; step < 8; ++step ) 417 | that.set_transform( 1.0f, 0.0f, 0.1f, 1.0f, width * -0.05f, 0.0f ); 418 | that.stroke_rectangle( width * 0.25f, height * 0.25f, 419 | width * 0.5f, height * 0.5f ); 420 | } 421 | 422 | void global_alpha( canvas &that, float width, float height ) 423 | { 424 | that.set_color( stroke_style, 0.0f, 0.0f, 0.0f, 1.0f ); 425 | that.set_line_width( 3.0f ); 426 | for ( float y = 0.0f; y < 6.0f; y += 1.0f ) 427 | for ( float x = 0.0f; x < 6.0f; x += 1.0f ) 428 | { 429 | that.set_color( fill_style, x / 5.0f, 1.0f, y / 5.0f, x / 5.0f ); 430 | that.set_global_alpha( y / 4.0f - 0.25f ); 431 | that.begin_path(); 432 | that.rectangle( 433 | ( x + 0.1f ) / 6.0f * width, ( y + 0.1f ) / 6.0f * height, 434 | 0.8f / 6.0f * width, 0.8f / 6.0f * height ); 435 | that.fill(); 436 | that.stroke(); 437 | } 438 | } 439 | 440 | void global_composite_operation( canvas &that, float width, float height ) 441 | { 442 | composite_operation const operations[] = { 443 | source_in, source_copy, source_out, destination_in, 444 | destination_atop, lighter, destination_over, destination_out, 445 | source_atop, source_over, exclusive_or }; 446 | float box_width = 0.25f * width; 447 | float box_height = 0.25f * height; 448 | for ( int index = 0; index < 11; ++index ) 449 | { 450 | float column = static_cast< float >( operations[ index ] % 4 ); 451 | float row = static_cast< float >( operations[ index ] / 4 ); 452 | that.save(); 453 | that.begin_path(); 454 | that.rectangle( column * box_width, row * box_height, 455 | box_width, box_height ); 456 | that.clip(); 457 | that.set_color( fill_style, 0.0f, 0.0f, 1.0f, 1.0f ); 458 | that.fill_rectangle( ( column + 0.4f ) * box_width, 459 | ( row + 0.4f ) * box_height, 460 | 0.4f * box_width, 0.4f * box_height ); 461 | that.global_composite_operation = operations[ index ]; 462 | that.set_color( fill_style, 1.0f, 0.0f, 0.0f, 1.0f ); 463 | that.fill_rectangle( ( column + 0.2f ) * box_width, 464 | ( row + 0.2f ) * box_height, 465 | 0.4f * box_width, 0.4f * box_height ); 466 | that.restore(); 467 | } 468 | } 469 | 470 | void shadow_color( canvas &that, float width, float height ) 471 | { 472 | that.shadow_offset_x = 5.0f; 473 | that.shadow_offset_y = 5.0f; 474 | that.set_shadow_blur( 1.0f ); 475 | for ( float row = 0.0f; row < 5.0f; row += 1.0f ) 476 | { 477 | float y = ( row + 0.25f ) * 0.2f * height; 478 | that.set_color( fill_style, 0.0f, 0.0f, 0.0f, 0.25f * row ); 479 | that.set_shadow_color( 1.0f, -1.0f, 0.0f, 0.25f ); 480 | that.fill_rectangle( 0.05f * width, y, 481 | 0.15f * width, 0.1f * height ); 482 | that.set_shadow_color( 0.0f, 1.0f, 0.0f, 0.5f ); 483 | that.fill_rectangle( 0.30f * width, y, 484 | 0.15f * width, 0.1f * height ); 485 | that.set_shadow_color( 0.0f, 0.0f, 2.0f, 0.75f ); 486 | that.fill_rectangle( 0.55f * width, y, 487 | 0.15f * width, 0.1f * height ); 488 | that.set_shadow_color( 1.0f, 1.0f, 1.0f, 100.0f ); 489 | that.fill_rectangle( 0.80f * width, y, 490 | 0.15f * width, 0.1f * height ); 491 | } 492 | } 493 | 494 | void shadow_offset( canvas &that, float width, float height ) 495 | { 496 | that.set_shadow_blur( 2.0f ); 497 | that.set_color( fill_style, 1.0f, 1.0f, 1.0f, 1.0f ); 498 | that.set_shadow_color( 0.0f, 0.0f, 0.0f, 1.0f ); 499 | for ( float y = 0.0f; y < 5.0f; y += 1.0f ) 500 | for ( float x = 0.0f; x < 5.0f; x += 1.0f ) 501 | { 502 | that.shadow_offset_x = ( x - 2.0f ) * 4.0f; 503 | that.shadow_offset_y = ( y - 2.0f ) * 4.0f; 504 | that.fill_rectangle( ( x + 0.25f ) * 0.2f * width, 505 | ( y + 0.25f ) * 0.2f * height, 506 | 0.1f * width, 0.1f * height ); 507 | } 508 | } 509 | 510 | void shadow_offset_offscreen( canvas &that, float width, float height ) 511 | { 512 | that.shadow_offset_x = width; 513 | that.set_color( fill_style, 1.0f, 0.0f, 0.0f, 1.0f ); 514 | that.set_shadow_color( 0.0f, 0.0f, 0.0f, 0.5f ); 515 | that.fill_rectangle( width * -0.6875f, height * 0.0625f, 516 | width * 0.375f, height * 0.375f ); 517 | that.begin_path(); 518 | that.arc( width * 0.5f, height * 0.75f, min( width, height ) * 0.2f, 519 | 0.0f, 6.28318531f ); 520 | that.close_path(); 521 | that.fill(); 522 | } 523 | 524 | void shadow_blur( canvas &that, float width, float height ) 525 | { 526 | that.set_color( fill_style, 1.0f, 1.0f, 1.0f, 1.0f ); 527 | that.set_shadow_color( 0.0f, 0.0f, 0.0f, 1.0f ); 528 | that.shadow_offset_x = 5.0f; 529 | that.shadow_offset_y = 5.0f; 530 | for ( float x = 0.0f; x < 5.0f; x += 1.0f ) 531 | for ( float y = 4.0f; y >= 0.0f; y -= 1.0f ) 532 | { 533 | that.set_shadow_blur( ( y * 5.0f + x ) * 0.5f - 0.5f ); 534 | that.fill_rectangle( ( x + 0.25f ) * 0.2f * width, 535 | ( y + 0.25f ) * 0.2f * height, 536 | 0.1f * width, 0.1f * height ); 537 | } 538 | } 539 | 540 | void shadow_blur_offscreen( canvas &that, float width, float height ) 541 | { 542 | that.set_color( fill_style, 0.0f, 0.0f, 0.0f, 1.0f ); 543 | that.set_shadow_color( 0.0f, 0.0f, 0.0f, 1.0f ); 544 | that.set_shadow_blur( 5.0f ); 545 | that.fill_rectangle( 0.0f, height * 2.0f, width, height ); 546 | that.fill_rectangle( 0.0f, height * -2.0f, width, height ); 547 | that.fill_rectangle( width + 1.0f, 0.0f, width, height ); 548 | that.fill_rectangle( -width - 1.0f, 0.0f, width, height ); 549 | } 550 | 551 | void shadow_blur_composite( canvas &that, float width, float height ) 552 | { 553 | float radius = min( width, height ) * 0.5f; 554 | that.arc( 0.5f * width, 0.5f * height, radius, 0.0f, 6.28318531f ); 555 | that.clip(); 556 | that.set_color( fill_style, 0.0f, 0.0f, 1.0f, 1.0f ); 557 | that.set_shadow_color( 0.0f, 0.0f, 0.0f, 1.0f ); 558 | that.fill_rectangle( 0.4f * width, 0.0f, 0.2f * width, height ); 559 | that.global_composite_operation = destination_atop; 560 | that.set_color( stroke_style, 1.0f, 0.0f, 0.0f, 1.0f ); 561 | float dashing[] = { 16.0f, 4.0f }; 562 | that.set_line_dash( dashing, 2 ); 563 | that.set_line_width( 15.0f ); 564 | that.shadow_offset_x = 5.0f; 565 | that.shadow_offset_y = 5.0f; 566 | that.set_shadow_blur( 6.0f ); 567 | that.set_shadow_color( 0.0f, 0.0f, 0.0f, 1.0f ); 568 | that.begin_path(); 569 | that.arc( 0.45f * width, 0.85f * height, radius * 0.5f, 0.0f, 6.28318531f ); 570 | that.close_path(); 571 | that.stroke(); 572 | that.global_composite_operation = source_over; 573 | that.begin_path(); 574 | that.arc( 0.75f * width, 0.25f * height, radius, 0.0f, 6.28318531f ); 575 | that.close_path(); 576 | that.stroke(); 577 | } 578 | 579 | void line_width( canvas &that, float width, float height ) 580 | { 581 | that.set_color( stroke_style, 0.0f, 0.0f, 0.0f, 1.0f ); 582 | that.set_line_width( 4.0f ); 583 | for ( float step = 0.0f; step < 16.0f; step += 1.0f ) 584 | { 585 | float left = ( step + 0.25f ) / 16.0f * width; 586 | float right = ( step + 0.75f ) / 16.0f * width; 587 | that.begin_path(); 588 | that.move_to( left, 0.0f ); 589 | that.bezier_curve_to( left, 0.5f * height, 590 | right, 0.5f * height, 591 | right, height ); 592 | that.set_line_width( 0.5f * ( step - 1 ) ); 593 | that.stroke(); 594 | } 595 | that.set_color( fill_style, 1.0f, 1.0f, 1.0f, 1.0f ); 596 | that.global_composite_operation = source_atop; 597 | that.fill_rectangle( 0.0f, 0.5f * height, width, 0.5f * height ); 598 | that.global_composite_operation = destination_over; 599 | that.fill_rectangle( 0.0f, 0.25f * height, width, 0.25f * height ); 600 | that.set_color( fill_style, 0.0f, 0.0f, 0.0f, 1.0f ); 601 | that.fill_rectangle( 0.0f, 0.5f * height, width, 0.25f * height ); 602 | } 603 | 604 | void line_width_angular( canvas &that, float width, float height ) 605 | { 606 | for ( float step = 0.0f; step < 5.0f; step += 1.0f ) 607 | { 608 | float grey = ( step + 1.0f ) / 5.0f; 609 | that.set_color( stroke_style, grey, grey, grey, 1.0f ); 610 | that.begin_path(); 611 | that.move_to( 0.1f * width, 0.1f * height ); 612 | that.bezier_curve_to( 1.2f * width, 1.0f * height, 613 | 1.2f * width, -0.0f * height, 614 | 0.1f * width, 0.9f * height ); 615 | that.set_line_width( 30.0f - 7.0f * step ); 616 | that.stroke(); 617 | } 618 | } 619 | 620 | void line_cap( canvas &that, float width, float height ) 621 | { 622 | that.set_color( stroke_style, 0.0f, 0.0f, 0.0f, 1.0f ); 623 | that.set_line_width( 24.0f ); 624 | cap_style const caps[] = { butt, square, circle }; 625 | for ( int index = 0; index < 3; ++index ) 626 | { 627 | float right = static_cast< float >( index + 1 ) / 3.0f * width - 20.0f; 628 | that.begin_path(); 629 | that.move_to( right, 0.125f * height ); 630 | that.bezier_curve_to( right, 0.125f * height + 100.0f, 631 | right - 100.0f, 0.875f * height, 632 | right, 0.875f * height ); 633 | that.line_cap = caps[ index ]; 634 | that.stroke(); 635 | } 636 | } 637 | 638 | void line_cap_offscreen( canvas &that, float width, float height ) 639 | { 640 | that.set_color( stroke_style, 0.0f, 0.0f, 0.0f, 1.0f ); 641 | that.set_line_width( 36.0f ); 642 | cap_style const caps[] = { butt, square, circle }; 643 | for ( int index = 0; index < 3; ++index ) 644 | { 645 | float x = ( static_cast< float >( index ) + 0.5f ) / 3.0f * width; 646 | float y = ( static_cast< float >( index ) + 0.5f ) / 3.0f * height; 647 | that.begin_path(); 648 | that.move_to( x, -19.0f ); 649 | that.line_to( x, -9.0f ); 650 | that.move_to( x, height + 17.0f ); 651 | that.line_to( x, height + 27.0f ); 652 | that.move_to( -27.0f, y ); 653 | that.line_to( -17.0f, y ); 654 | that.move_to( width + 9.0f, y ); 655 | that.line_to( width + 19.0f, y ); 656 | that.line_cap = caps[ index ]; 657 | that.stroke(); 658 | } 659 | } 660 | 661 | void line_join( canvas &that, float width, float height ) 662 | { 663 | that.set_color( stroke_style, 0.0f, 0.0f, 0.0f, 1.0f ); 664 | that.set_line_width( 16.0f ); 665 | join_style const joins[] = { miter, bevel, rounded }; 666 | for ( int index = 0; index < 3; ++index ) 667 | { 668 | float left = ( static_cast< float >( index ) + 0.25f ) / 3.0f * width; 669 | float right = ( static_cast< float >( index ) + 0.75f ) / 3.0f * width; 670 | that.begin_path(); 671 | that.move_to( left, 0.2f * height ); 672 | that.line_to( left, 0.1f * height ); 673 | that.line_to( left, 0.2f * height ); 674 | that.line_to( right, 0.2f * height ); 675 | that.line_to( left, 0.2f * height ); 676 | that.line_to( left, 0.3f * height ); 677 | that.line_to( right, 0.3f * height ); 678 | that.line_to( right, 0.4f * height ); 679 | that.line_to( right, 0.5f * height ); 680 | that.line_to( left, 0.4f * height ); 681 | that.line_to( left, 0.5f * height ); 682 | that.line_to( right, 0.6f * height ); 683 | that.bezier_curve_to( right, height, 684 | left, 0.4f * height, 685 | left, 0.7f * height ); 686 | that.bezier_curve_to( left, 0.8f * height, 687 | right, 0.8f * height, 688 | right, 0.9f * height ); 689 | that.line_join = joins[ index ]; 690 | that.stroke(); 691 | } 692 | } 693 | 694 | void line_join_offscreen( canvas &that, float width, float height ) 695 | { 696 | that.set_color( stroke_style, 0.0f, 0.0f, 0.0f, 1.0f ); 697 | that.set_line_width( 36.0f ); 698 | join_style const joins[] = { miter, bevel, rounded }; 699 | for ( int index = 0; index < 3; ++index ) 700 | { 701 | float x = ( static_cast< float >( index ) + 0.5f ) / 3.0f * width; 702 | float y = ( static_cast< float >( index ) + 0.5f ) / 3.0f * height; 703 | that.begin_path(); 704 | that.move_to( x - 10.0f, -55.0f ); 705 | that.line_to( x - 10.0f, -5.0f ); 706 | that.line_to( x + 10.0f, -55.0f ); 707 | that.move_to( x - 10.0f, height + 130.0f ); 708 | that.line_to( x + 10.0f, height + 80.0f ); 709 | that.line_to( x + 10.0f, height + 130.0f ); 710 | that.move_to( -130.0f, y - 10.0f ); 711 | that.line_to( -80.0f, y - 10.0f ); 712 | that.line_to( -130.0f, y + 10.0f ); 713 | that.move_to( height + 55.0f, y - 10.0f ); 714 | that.line_to( height + 5.0f, y + 10.0f ); 715 | that.line_to( height + 55.0f, y + 10.0f ); 716 | that.line_join = joins[ index ]; 717 | that.stroke(); 718 | } 719 | } 720 | 721 | void miter_limit( canvas &that, float width, float height ) 722 | { 723 | that.set_color( stroke_style, 0.0f, 0.0f, 0.0f, 1.0f ); 724 | for ( float line = 0.0f; line < 4.0f; line += 1.0f ) 725 | { 726 | that.set_line_width( 1.5f * line + 1.0f ); 727 | that.set_miter_limit( 20.0f ); 728 | for ( float limit = 0.0f; limit < 8.0f; limit += 1.0f ) 729 | { 730 | float left = ( limit + 0.2f ) / 8.0f * width; 731 | float middle = ( limit + 0.5f ) / 8.0f * width; 732 | float right = ( limit + 0.7f ) / 8.0f * width; 733 | float top = ( line + 0.3f ) / 4.0f * height; 734 | float bottom = ( line + 0.7f ) / 4.0f * height; 735 | that.begin_path(); 736 | that.move_to( left, bottom ); 737 | that.line_to( left, top ); 738 | that.line_to( right, bottom ); 739 | that.line_to( middle, top ); 740 | that.set_miter_limit( 1.5f * limit ); 741 | that.stroke(); 742 | } 743 | } 744 | } 745 | 746 | void line_dash_offset( canvas &that, float width, float height ) 747 | { 748 | that.set_color( stroke_style, 0.0f, 0.0f, 0.0f, 1.0f ); 749 | that.set_line_width( 6.0f ); 750 | float segments[] = { 20.0f, 8.0f, 8.0f, 8.0f }; 751 | that.set_line_dash( segments, 4 ); 752 | for ( float step = 0.0f; step < 16.0f; step += 1.0f ) 753 | { 754 | float left = ( step + 0.125f ) / 16.0f * width; 755 | float right = ( step + 0.875f ) / 16.0f * width; 756 | that.begin_path(); 757 | that.move_to( left, 0.0f ); 758 | that.line_to( right, 0.125f * height ); 759 | that.line_to( left, 0.375f * height ); 760 | that.line_to( right, 0.625f * height ); 761 | that.line_to( left, 0.875f * height ); 762 | that.line_to( right, height ); 763 | that.line_dash_offset = ( step / 16.0f - 0.5f ) * 44.0f; 764 | that.stroke(); 765 | } 766 | } 767 | 768 | void line_dash( canvas &that, float width, float height ) 769 | { 770 | that.set_color( stroke_style, 0.0f, 0.0f, 0.0f, 1.0f ); 771 | that.set_line_width( 6.0f ); 772 | float segments_1[] = { 10.0f }; 773 | that.set_line_dash( segments_1, 1 ); 774 | that.stroke(); 775 | that.move_to( 0.0f, 0.0f ); 776 | that.stroke(); 777 | that.begin_path(); 778 | that.move_to( width * 0.25f, 0.0f ); 779 | that.line_to( width * 0.25f, height ); 780 | that.stroke(); 781 | float segments_2[] = { 20.0f, -8.0f }; 782 | that.set_line_dash( segments_2, 2 ); 783 | that.begin_path(); 784 | that.move_to( width * 0.375f, 0.0f ); 785 | that.line_to( width * 0.375f, height ); 786 | that.stroke(); 787 | float segments_3[] = { 20.0f, 8.0f, 8.0f, 8.0f }; 788 | that.set_line_dash( segments_3, 4 ); 789 | that.begin_path(); 790 | that.move_to( width * 0.5f, 0.0f ); 791 | that.line_to( width * 0.5f, height ); 792 | that.stroke(); 793 | float segments_4[] = { 0.0f, 8.0f, 2.0f, 8.0f }; 794 | that.set_line_dash( segments_4, 4 ); 795 | that.begin_path(); 796 | that.move_to( width * 0.625f, 0.0f ); 797 | that.line_to( width * 0.625f, height ); 798 | that.stroke(); 799 | that.set_line_dash( 0, 0 ); 800 | that.begin_path(); 801 | that.move_to( width * 0.75f, 0.0f ); 802 | that.line_to( width * 0.75f, height ); 803 | that.stroke(); 804 | } 805 | 806 | void line_dash_closed( canvas &that, float width, float height ) 807 | { 808 | that.set_color( stroke_style, 0.0f, 0.0f, 0.0f, 1.0f ); 809 | that.set_line_width( 32.0f ); 810 | float segments_1[] = { 96.0f, 32.0f }; 811 | that.set_line_dash( segments_1, 2 ); 812 | that.line_dash_offset = -80.0f; 813 | that.stroke_rectangle( 0.25f * width, 0.25f * height, 814 | 0.5f * width, 0.5f * height ); 815 | float segments_2[] = { 96.0f, 32.0f, 1024.0f, 16.0f }; 816 | that.set_line_dash( segments_2, 4 ); 817 | that.line_dash_offset = 128.0f; 818 | that.stroke_rectangle( 0.09375f * width, 0.09375f * height, 819 | 0.8125f * width, 0.8125f * height ); 820 | } 821 | 822 | void line_dash_overlap( canvas &that, float width, float height ) 823 | { 824 | that.set_color( stroke_style, 0.0f, 0.0f, 0.0f, 1.0f ); 825 | that.set_color( fill_style, 1.0f, 0.0f, 0.0f, 1.0f ); 826 | that.line_cap = circle; 827 | that.set_line_width( 16.0f ); 828 | float segments[] = { 14.0f, 12.0f }; 829 | that.set_line_dash( segments, 2 ); 830 | for ( int index = 0; index < 4; ++index ) 831 | { 832 | float flip = ( index == 3 ? -1.0f : 1.0f ); 833 | float top_y = ( index & 1 ? 0.25f : 0.1f ) * height; 834 | float bottom_y = ( index & 1 ? 0.9f : 0.75f ) * height; 835 | float mid_x = ( index & 2 ? 0.75f : 0.25f ) * width; 836 | float top_width = ( index & 1 ? 0.25f : 0.55f ) * flip * width; 837 | float bottom_width = ( index & 1 ? 0.55f : 0.25f ) * flip * width; 838 | that.move_to( mid_x, top_y ); 839 | that.bezier_curve_to( mid_x - top_width, top_y, 840 | mid_x + bottom_width, bottom_y, 841 | mid_x, bottom_y ); 842 | that.bezier_curve_to( mid_x - bottom_width, bottom_y, 843 | mid_x + top_width, top_y, 844 | mid_x, top_y ); 845 | that.close_path(); 846 | } 847 | that.fill(); 848 | that.stroke(); 849 | } 850 | 851 | void line_dash_offscreen( canvas &that, float width, float height ) 852 | { 853 | that.set_color( stroke_style, 0.0f, 0.0f, 0.0f, 1.0f ); 854 | that.set_line_width( 6.0f ); 855 | float segments[] = { 856 | 0.0f, width * 20.5f * 3.14159265f - height * 0.5f + 1.0f, 857 | height - 2.0f, 0.0f }; 858 | that.set_line_dash( segments, 4 ); 859 | for ( float step = -2.0f; step <= 2.0f; step += 1.0f ) 860 | { 861 | that.begin_path(); 862 | that.arc( width * -20.0f, height * 0.5f, 863 | width * ( 20.5f - step * 0.1f ), 864 | 3.14159265f, 1.5707963268f ); 865 | that.line_dash_offset = width * step * 0.1f * 3.14159265f; 866 | that.stroke(); 867 | } 868 | } 869 | 870 | void color( canvas &that, float width, float height ) 871 | { 872 | float radius = min( width, height ) * 0.4f; 873 | that.set_color( fill_style, 2.0f, -1.0f, 0.0f, 0.5f ); 874 | that.set_color( stroke_style, 0.0f, 0.0f, 1.0f, 1.5f ); 875 | that.set_line_width( 16.0f ); 876 | that.arc( 0.5f * width, 0.5f * height, radius, 0.0f, 6.28318531f ); 877 | that.close_path(); 878 | that.fill(); 879 | that.stroke(); 880 | } 881 | 882 | void linear_gradient( canvas &that, float width, float height ) 883 | { 884 | float radius = min( width, height ) * 0.4f; 885 | that.set_linear_gradient( fill_style, 886 | 0.3f * width, 0.3f * height, 887 | 0.7f * width, 0.7f * height ); 888 | that.add_color_stop( fill_style, 0.0f, 0.0f, 1.0f, 0.0f, 0.5f ); 889 | that.add_color_stop( fill_style, 1.0f, 1.0f, 0.0f, 1.0f, 100.0f ); 890 | that.set_linear_gradient( stroke_style, 891 | 0.3f * width, 0.7f * height, 892 | 0.7f * width, 0.3f * height ); 893 | that.add_color_stop( stroke_style, 0.0f, 0.0f, 0.0f, 1.0f, 0.5f ); 894 | that.add_color_stop( stroke_style, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f ); 895 | that.set_line_width( 16.0f ); 896 | that.arc( 0.5f * width, 0.5f * height, radius, 0.0f, 6.28318531f ); 897 | that.close_path(); 898 | that.fill(); 899 | that.stroke(); 900 | that.set_linear_gradient( stroke_style, 901 | 0.5f * width, 0.5f * height, 902 | 0.5f * width, 0.5f * height ); 903 | that.add_color_stop( stroke_style, 0.0f, 1.0f, 0.0f, 0.0f, 0.5f ); 904 | that.add_color_stop( stroke_style, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f ); 905 | that.stroke(); 906 | } 907 | 908 | void radial_gradient( canvas &that, float width, float height ) 909 | { 910 | float radius = min( width, height ) * 0.4f; 911 | that.set_radial_gradient( fill_style, 912 | 0.0f, 0.0f, radius, 913 | width, height, 0.5f * radius ); 914 | that.add_color_stop( fill_style, 0.0f, 0.0f, 1.0f, 0.0f, 0.5f ); 915 | that.add_color_stop( fill_style, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f ); 916 | that.set_radial_gradient( stroke_style, 917 | 0.0f, height, radius, 918 | width, 0.0f, 0.5f * radius ); 919 | that.add_color_stop( stroke_style, 0.0f, 0.0f, 0.0f, 1.0f, 0.5f ); 920 | that.add_color_stop( stroke_style, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f ); 921 | that.set_line_width( 16.0f ); 922 | that.arc( 0.5f * width, 0.5f * height, radius, 0.0f, 6.28318531f ); 923 | that.close_path(); 924 | that.fill(); 925 | that.stroke(); 926 | that.set_radial_gradient( stroke_style, 927 | 0.5f * width, 0.4f * height, 10.0f, 928 | 0.5f * width, 0.6f * height, 0.0f ); 929 | that.set_radial_gradient( stroke_style, 930 | 0.0f, 0.5f * height, -10.0f, 931 | width, 0.5f * height, 10.0f ); 932 | that.add_color_stop( stroke_style, 0.0f, 1.0f, 0.0f, 0.0f, 0.5f ); 933 | that.add_color_stop( stroke_style, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f ); 934 | that.stroke(); 935 | that.set_radial_gradient( fill_style, 936 | 0.5f * width, 0.5f * height, 0.0f, 937 | 0.5f * width, 0.5f * height, radius ); 938 | that.add_color_stop( fill_style, 0.15f, 0.0f, 0.0f, 0.0f, 1.0f ); 939 | that.add_color_stop( fill_style, 0.20f, 0.0f, 0.0f, 0.0f, 0.0f ); 940 | that.fill(); 941 | } 942 | 943 | void color_stop( canvas &that, float width, float height ) 944 | { 945 | that.add_color_stop( fill_style, 0.5f, 1.0f, 0.0f, 1.0f, 1.0f ); 946 | that.set_linear_gradient( fill_style, 947 | 0.1f * width, 0.0f, 948 | 0.9f * width, 0.0f ); 949 | that.fill_rectangle( 0.0f, 0.0f, width, 0.1f * height ); 950 | that.add_color_stop( fill_style, -1.0f, 0.0f, 1.0f, 0.0f, 1.0f ); 951 | that.add_color_stop( fill_style, 2.0f, 1.0f, 0.0f, 0.0f, 1.0f ); 952 | that.add_color_stop( fill_style, 0.3f, -1.0f, 0.0f, 2.0f, 2.0f ); 953 | that.add_color_stop( fill_style, 0.3f, 1.0f, 1.0f, 1.0f, 1.0f ); 954 | that.add_color_stop( fill_style, 0.3f, 0.0f, 0.0f, 0.0f, 1.0f ); 955 | that.add_color_stop( fill_style, 0.0f, 0.0f, 0.0f, 0.8f, 1.0f ); 956 | that.add_color_stop( fill_style, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f ); 957 | that.add_color_stop( fill_style, 0.7f, 0.9f, 0.9f, 0.9f, 1.0f ); 958 | that.add_color_stop( fill_style, 0.6f, 0.1f, 0.1f, 0.1f, 1.0f ); 959 | that.fill_rectangle( 0.0f, 0.1f * height, width, 0.4f * height ); 960 | that.fill_rectangle( 0.0f, 0.5f * height, width, 0.4f * height ); 961 | } 962 | 963 | void pattern( canvas &that, float width, float height ) 964 | { 965 | unsigned char checker[ 256 ]; 966 | for ( int index = 0; index < 256; ++index ) 967 | checker[ index ] = static_cast< unsigned char >( 968 | ( ( ( index >> 2 & 1 ) ^ ( index >> 5 & 1 ) ) | 969 | ( ( index & 3 ) == 3 ) ) * 255 ); 970 | that.arc( 0.5f * width, 0.5f * height, 32.0f, 0.0f, 6.28318531f ); 971 | that.close_path(); 972 | that.set_line_width( 20.0f ); 973 | that.set_color( stroke_style, 0.0f, 0.0f, 0.0f, 1.0f ); 974 | that.set_pattern( stroke_style, 0, 8, 8, 32, repeat ); 975 | that.stroke(); 976 | that.set_line_width( 16.0f ); 977 | that.set_pattern( stroke_style, checker, 8, 8, 32, repeat ); 978 | that.stroke(); 979 | for ( float scale = 8.0f; scale >= 1.0f; scale /= 2.0f ) 980 | { 981 | that.set_transform( 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f ); 982 | that.scale( scale, scale ); 983 | float size_x = 0.5f * width / scale; 984 | float size_y = 0.5f * height / scale; 985 | that.set_pattern( fill_style, checker, 8, 8, 32, no_repeat ); 986 | that.fill_rectangle( 0.0f, 0.0f, size_x, size_y ); 987 | that.set_pattern( fill_style, checker, 8, 8, 32, repeat_x ); 988 | that.fill_rectangle( size_x, 0.0f, size_x, size_y ); 989 | that.set_pattern( fill_style, checker, 8, 8, 32, repeat_y ); 990 | that.fill_rectangle( 0.0f, size_y, size_x, size_y ); 991 | that.set_pattern( fill_style, checker, 8, 8, 32, repeat ); 992 | that.fill_rectangle( size_x, size_y, size_x, size_y ); 993 | } 994 | } 995 | 996 | void begin_path( canvas &that, float width, float height ) 997 | { 998 | that.move_to( 0.0f, 0.0f ); 999 | that.line_to( width, height ); 1000 | that.stroke(); 1001 | that.set_color( stroke_style, 0.0f, 0.0f, 0.0f, 1.0f ); 1002 | that.begin_path(); 1003 | that.begin_path(); 1004 | that.move_to( width, 0.0f ); 1005 | that.line_to( 0.0f, height ); 1006 | that.stroke(); 1007 | that.begin_path(); 1008 | that.line_to( 0.5f * width, height ); 1009 | that.stroke(); 1010 | } 1011 | 1012 | void move_to( canvas &that, float width, float height ) 1013 | { 1014 | that.set_color( stroke_style, 0.0f, 0.0f, 0.0f, 1.0f ); 1015 | that.set_color( fill_style, 1.0f, 0.0f, 0.0f, 1.0f ); 1016 | that.set_line_width( 8.0f ); 1017 | that.move_to( 0.6f * width, height ); 1018 | that.move_to( 0.4f * width, 0.1f * height ); 1019 | that.line_to( 0.2f * width, 0.5f * height ); 1020 | that.line_to( 0.4f * width, 0.9f * height ); 1021 | that.move_to( 0.6f * width, 0.2f * height ); 1022 | that.line_to( 0.8f * width, 0.4f * height ); 1023 | that.move_to( 0.8f * width, 0.6f * height ); 1024 | that.line_to( 0.6f * width, 0.8f * height ); 1025 | that.move_to( 0.7f * width, 0.5f * height ); 1026 | that.line_to( 0.7f * width, 0.5f * height ); 1027 | that.fill(); 1028 | that.stroke(); 1029 | } 1030 | 1031 | void close_path( canvas &that, float width, float height ) 1032 | { 1033 | that.set_color( stroke_style, 0.0f, 0.0f, 0.0f, 1.0f ); 1034 | that.set_color( fill_style, 1.0f, 0.0f, 0.0f, 1.0f ); 1035 | that.set_line_width( 8.0f ); 1036 | that.close_path(); 1037 | that.line_to( 0.5f * width, 0.5f * height ); 1038 | that.line_to( 0.2f * width, 0.8f * height ); 1039 | that.line_to( 0.2f * width, 0.2f * height ); 1040 | that.close_path(); 1041 | that.line_to( 0.5f * width, 0.2f * height ); 1042 | that.line_to( 0.8f * width, 0.2f * height ); 1043 | that.close_path(); 1044 | that.close_path(); 1045 | that.move_to( 0.5f * width, 0.8f * height ); 1046 | that.line_to( 0.8f * width, 0.8f * height ); 1047 | that.line_to( 0.8f * width, 0.5f * height ); 1048 | that.close_path(); 1049 | that.fill(); 1050 | that.stroke(); 1051 | } 1052 | 1053 | void line_to( canvas &that, float width, float height ) 1054 | { 1055 | that.set_color( stroke_style, 0.0f, 0.0f, 0.0f, 1.0f ); 1056 | that.set_color( fill_style, 1.0f, 0.0f, 0.0f, 1.0f ); 1057 | that.set_line_width( 16.0f ); 1058 | that.line_to( 0.1f * width, 0.2f * height ); 1059 | that.line_to( 0.1f * width, 0.2f * height ); 1060 | that.line_to( 0.2f * width, 0.5f * height ); 1061 | that.line_to( 0.2f * width, 0.5f * height ); 1062 | that.line_to( 0.3f * width, 0.8f * height ); 1063 | that.line_to( 0.4f * width, 0.2f * height ); 1064 | that.line_to( 0.4f * width, 0.2f * height ); 1065 | that.line_to( 0.6f * width, 0.8f * height ); 1066 | that.line_to( 0.6f * width, 0.8f * height ); 1067 | that.move_to( 0.7f * width, 0.4f * height ); 1068 | that.line_to( 0.9f * width, 0.4f * height ); 1069 | that.line_to( 0.9f * width, 0.6f * height ); 1070 | that.line_to( 0.7f * width, 0.6f * height ); 1071 | that.line_to( 0.7f * width, 0.4f * height ); 1072 | that.fill(); 1073 | that.stroke(); 1074 | } 1075 | 1076 | void quadratic_curve_to( canvas &that, float width, float height ) 1077 | { 1078 | that.set_color( stroke_style, 0.0f, 0.0f, 0.0f, 1.0f ); 1079 | that.set_color( fill_style, 1.0f, 0.0f, 0.0f, 1.0f ); 1080 | that.set_line_width( 8.0f ); 1081 | that.quadratic_curve_to( 0.1f * width, 0.2f * height, 1082 | 0.1f * width, 0.2f * height ); 1083 | that.quadratic_curve_to( 0.2f * width, 0.5f * height, 1084 | 0.2f * width, 0.5f * height ); 1085 | that.quadratic_curve_to( 0.3f * width, 0.8f * height, 1086 | 0.4f * width, 0.2f * height ); 1087 | that.quadratic_curve_to( 0.6f * width, 0.8f * height, 1088 | 0.7f * width, 0.2f * height ); 1089 | that.move_to( 0.7f * width, 0.6f * height ); 1090 | that.quadratic_curve_to( 0.9f * width, 0.6f * height, 1091 | 0.9f * width, 0.8f * height ); 1092 | that.quadratic_curve_to( 0.9f * width, 0.9f * height, 1093 | 0.7f * width, 0.9f * height ); 1094 | that.close_path(); 1095 | that.move_to( 0.1f * width, 0.9f * height ); 1096 | that.quadratic_curve_to( 0.5f * width, 0.5f * height, 1097 | 0.1f * width, 0.9f * height ); 1098 | that.fill(); 1099 | that.stroke(); 1100 | } 1101 | 1102 | void bezier_curve_to( canvas &that, float width, float height ) 1103 | { 1104 | that.set_color( stroke_style, 0.0f, 0.0f, 0.0f, 1.0f ); 1105 | that.set_color( fill_style, 1.0f, 0.0f, 0.0f, 1.0f ); 1106 | that.set_line_width( 8.0f ); 1107 | that.bezier_curve_to( 0.9f * width, 0.9f * height, 1108 | 0.6f * width, 0.6f * height, 1109 | 0.6f * width, 0.9f * height ); 1110 | that.move_to( 0.1f * width, 0.1f * height ); 1111 | that.bezier_curve_to( 0.9f * width, 0.9f * height, 1112 | 0.9f * width, 0.1f * height, 1113 | 0.1f * width, 0.9f * height ); 1114 | that.move_to( 0.4f * width, 0.1f * height ); 1115 | that.bezier_curve_to( 0.1f * width, 0.3f * height, 1116 | 0.7f * width, 0.3f * height, 1117 | 0.4f * width, 0.1f * height ); 1118 | that.move_to( 0.9f * width, 0.1f * height ); 1119 | that.bezier_curve_to( 0.6f * width, 0.2f * height, 1120 | 0.9f * width, 0.1f * height, 1121 | 0.6f * width, 0.2f * height ); 1122 | that.move_to( 0.7f * width, 0.3f * height ); 1123 | that.bezier_curve_to( 0.9f * width, 0.3f * height, 1124 | 0.9f * width, 0.4f * height, 1125 | 0.8f * width, 0.5f * height ); 1126 | that.bezier_curve_to( 0.7f * width, 0.6f * height, 1127 | 0.7f * width, 0.7f * height, 1128 | 0.9f * width, 0.7f * height ); 1129 | that.fill(); 1130 | that.stroke(); 1131 | } 1132 | 1133 | void arc_to( canvas &that, float width, float height ) 1134 | { 1135 | float radius = min( width, height ) * 0.5f; 1136 | that.set_color( stroke_style, 0.0f, 0.0f, 0.0f, 1.0f ); 1137 | that.set_color( fill_style, 1.0f, 0.0f, 0.0f, 1.0f ); 1138 | that.set_line_width( 8.0f ); 1139 | that.arc_to( 0.3f * width, 0.3f * height, 1140 | 0.5f * width, 0.5f * height, 16.0f ); 1141 | that.move_to( 0.4f * width, 0.4f * height ); 1142 | that.arc_to( 0.7f * width, 0.1f * height, 1143 | 0.7f * width, 0.4f * height, 0.0f ); 1144 | that.arc_to( 0.9f * width, 0.5f * height, 1145 | 0.7f * width, 0.7f * height, 0.125f * radius ); 1146 | that.arc_to( 0.5f * width, 0.9f * height, 1147 | 0.3f * width, 0.8f * height, 0.25f * radius ); 1148 | that.arc_to( 0.1f * width, 0.7f * height, 1149 | 0.4f * width, 0.4f * height, 0.375f * radius ); 1150 | that.close_path(); 1151 | that.move_to( 0.1f * width, 0.6f * height ); 1152 | that.transform( 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.2f * height ); 1153 | that.arc_to( 0.1f * width, 0.9f * height, 1154 | 0.5f * width, 0.9f * height, 0.3f * radius ); 1155 | that.set_transform( 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f ); 1156 | that.close_path(); 1157 | that.move_to( 0.2f * width, 0.1f * height ); 1158 | that.arc_to( 0.1f * width, 0.1f * height, 1159 | 0.1f * width, 0.7f * height, 0.6f * radius ); 1160 | that.arc_to( 0.2f * width, 0.4f * height, 1161 | 0.2f * width, 0.4f * height, 0.5f * radius ); 1162 | that.arc_to( 0.4f * width, 0.2f * height, 1163 | 0.2f * width, 0.4f * height, 0.5f * radius ); 1164 | that.arc_to( 0.5f * width, 0.5f * height, 1165 | 0.9f * width, 0.1f * height, -1.0f ); 1166 | that.move_to( 0.6f * width, 0.9f * height ); 1167 | that.set_transform( 0.0f, 0.0f, 0.0f, 1.0f, 0.9f * width, 0.0f ); 1168 | that.arc_to( 0.9f * width, 0.9f * height, 1169 | 0.9f * width, 0.6f * height, 0.3f * radius ); 1170 | that.set_transform( 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f ); 1171 | that.arc_to( 0.9f * width, 0.6f * height, 1172 | 0.9f * width, 0.6f * height, 0.0f ); 1173 | that.fill(); 1174 | that.stroke(); 1175 | } 1176 | 1177 | void arc( canvas &that, float width, float height ) 1178 | { 1179 | that.set_color( stroke_style, 0.0f, 0.0f, 0.0f, 1.0f ); 1180 | that.set_color( fill_style, 1.0f, 0.0f, 0.0f, 1.0f ); 1181 | that.set_line_width( 8.0f ); 1182 | for ( int i = 0; i < 4; ++i ) 1183 | for ( int j = 0; j < 3; ++j ) 1184 | { 1185 | float x = ( static_cast< float >( j ) + 0.5f ) * width / 3.0f; 1186 | float y = ( static_cast< float >( i ) + 0.5f ) * height / 4.0f; 1187 | float radius = min( width, height ) * 0.1f; 1188 | float start = ( 3.14159265f + 1.0e-6f ) * 1189 | static_cast< float >( i % 2 ); 1190 | float end = ( 3.14159265f + 1.0e-6f ) * 1191 | ( 1.0f + 0.5f * static_cast< float >( j ) ); 1192 | bool counter = i / 2; 1193 | that.begin_path(); 1194 | that.arc( x, y, -radius, start, end, counter ); 1195 | that.arc( x, y, radius, start, end, counter ); 1196 | that.close_path(); 1197 | that.fill(); 1198 | that.stroke(); 1199 | } 1200 | } 1201 | 1202 | void rectangle( canvas &that, float width, float height ) 1203 | { 1204 | that.set_color( stroke_style, 0.0f, 0.0f, 0.0f, 1.0f ); 1205 | that.set_color( fill_style, 1.0f, 0.0f, 0.0f, 1.0f ); 1206 | that.set_line_width( 8.0f ); 1207 | that.move_to( 0.3f * width, 0.3f * height ); 1208 | that.line_to( 0.7f * width, 0.3f * height ); 1209 | that.line_to( 0.7f * width, 0.7f * height ); 1210 | that.line_to( 0.3f * width, 0.7f * height ); 1211 | that.close_path(); 1212 | that.move_to( 0.0f, 0.0f ); 1213 | for ( float y = -1.0f; y <= 1.0f; y += 1.0f ) 1214 | for ( float x = -1.0f; x <= 1.0f; x += 1.0f ) 1215 | that.rectangle( ( 0.5f + 0.1f * x ) * width, 1216 | ( 0.5f + 0.1f * y ) * height, 1217 | x * 0.3f * width, y * 0.3f * height ); 1218 | that.line_to( width, height ); 1219 | that.fill(); 1220 | that.stroke(); 1221 | } 1222 | 1223 | void fill( canvas &that, float width, float height ) 1224 | { 1225 | float radius = min( width, height ) * 0.45f; 1226 | that.set_color( fill_style, 0.0f, 0.0f, 0.0f, 1.0f ); 1227 | that.fill(); 1228 | that.begin_path(); 1229 | for ( float step = 0.0f; step < 128.0f; step += 1.0f ) 1230 | { 1231 | float angle = step * ( 59.0f / 128.0f * 6.28318531f ); 1232 | float x = cosf( angle ) * radius + width / 2.0f; 1233 | float y = sinf( angle ) * radius + height / 2.0f; 1234 | that.line_to( x, y ); 1235 | } 1236 | that.close_path(); 1237 | that.fill(); 1238 | that.set_color( fill_style, 1.0f, 0.0f, 0.0f, 1.0f ); 1239 | that.scale( 0.0f, 1.0f ); 1240 | that.fill(); 1241 | } 1242 | 1243 | void fill_rounding( canvas &that, float width, float height ) 1244 | { 1245 | static_cast< void >( width ); 1246 | static_cast< void >( height ); 1247 | that.set_color( fill_style, 0.0f, 0.0f, 0.0f, 1.0f ); 1248 | that.begin_path(); 1249 | that.move_to( 4.00000191f, 4.00000763f ); 1250 | that.line_to( 3.99999809f, 192.0f ); 1251 | that.line_to( 28.0000019f, 192.0f ); 1252 | that.close_path(); 1253 | that.move_to( -10390.0664f, 52.3311195f ); 1254 | that.line_to( -10389.9941f, 47.6248589f ); 1255 | that.line_to( -10395.9941f, 47.5328255f ); 1256 | that.line_to( -10396.0664f, 52.2478294f ); 1257 | that.close_path(); 1258 | that.move_to( 110.0f, 256.0f ); 1259 | that.line_to( 124.086205f, 255.998276f ); 1260 | that.line_to( 123.203453f, 0.0f ); 1261 | that.close_path(); 1262 | that.fill(); 1263 | } 1264 | 1265 | void fill_converging( canvas &that, float width, float height ) 1266 | { 1267 | float radius = min( width, height ) * 0.48f; 1268 | that.set_color( fill_style, 0.0f, 0.0f, 0.0f, 1.0f ); 1269 | for ( float step = 0.0f; step < 256.0f; step += 1.0f ) 1270 | { 1271 | float angle_1 = ( step + 0.0f ) / 256.0f * 6.28318531f; 1272 | float angle_2 = ( step + 0.5f ) / 256.0f * 6.28318531f; 1273 | that.move_to( width / 2.0f + 0.5f, height / 2.0f + 0.5f ); 1274 | that.line_to( cosf( angle_1 ) * radius + width / 2.0f + 0.5f, 1275 | sinf( angle_1 ) * radius + height / 2.0f + 0.5f ); 1276 | that.line_to( cosf( angle_2 ) * radius + width / 2.0f + 0.5f, 1277 | sinf( angle_2 ) * radius + height / 2.0f + 0.5f ); 1278 | that.close_path(); 1279 | } 1280 | that.fill(); 1281 | } 1282 | 1283 | void fill_zone_plate( canvas &that, float width, float height ) 1284 | { 1285 | float radius = floorf( min( width, height ) * 0.48f / 4.0f ) * 4.0f; 1286 | that.set_color( fill_style, 0.0f, 0.0f, 0.0f, 1.0f ); 1287 | for ( float step = 0.0f; step < radius; step += 2.0f ) 1288 | { 1289 | float inner = sqrtf( ( step + 0.0f ) / radius ) * radius; 1290 | float outer = sqrtf( ( step + 1.0f ) / radius ) * radius; 1291 | that.move_to( width / 2.0f + inner, height / 2.0f ); 1292 | that.arc( width / 2.0f, height / 2.0f, inner, 1293 | 0.0f, 6.28318531f ); 1294 | that.close_path(); 1295 | that.move_to( width / 2.0f + outer, height / 2.0f ); 1296 | that.arc( width / 2.0f, height / 2.0f, outer, 1297 | 6.28318531f, 0.0f, true ); 1298 | that.close_path(); 1299 | } 1300 | that.fill(); 1301 | } 1302 | 1303 | void stroke( canvas &that, float width, float height ) 1304 | { 1305 | float radius = min( width, height ) * 0.45f; 1306 | that.set_color( stroke_style, 0.0f, 0.0f, 0.0f, 1.0f ); 1307 | that.stroke(); 1308 | that.begin_path(); 1309 | for ( float step = 0.0f; step < 128.0f; step += 1.0f ) 1310 | { 1311 | float angle = step * ( 59.0f / 128.0f * 6.28318531f ); 1312 | float x = cosf( angle ) * radius + width / 2.0f; 1313 | float y = sinf( angle ) * radius + height / 2.0f; 1314 | that.line_to( x, y ); 1315 | } 1316 | that.close_path(); 1317 | that.stroke(); 1318 | that.set_color( stroke_style, 1.0f, 0.0f, 0.0f, 1.0f ); 1319 | that.scale( 0.0f, 1.0f ); 1320 | that.stroke(); 1321 | } 1322 | 1323 | void stroke_wide( canvas &that, float width, float height ) 1324 | { 1325 | that.scale( width / 256.0f, height / 256.0f ); 1326 | that.line_join = rounded; 1327 | that.move_to( 24.0f, 104.0f ); 1328 | that.bezier_curve_to( 112.0f, 24.0f, 16.0f, 24.0f, 104.0f, 104.0f ); 1329 | that.move_to( 152.0f, 104.0f ); 1330 | that.bezier_curve_to( 232.8f, 24.0f, 151.2f, 24.0f, 232.0f, 104.0f ); 1331 | that.move_to( 24.0f, 232.0f ); 1332 | that.bezier_curve_to( 104.0f, 152.0f, 24.0f, 152.0f, 104.0f, 232.0f ); 1333 | that.move_to( 188.0f, 232.0f ); 1334 | that.bezier_curve_to( 196.0f, 184.0f, 188.0f, 184.0f, 196.0f, 192.0f ); 1335 | that.set_line_width( 40.0f ); 1336 | that.stroke(); 1337 | that.set_color( stroke_style, 1.0f, 0.0f, 0.0f, 1.0f ); 1338 | that.set_line_width( 1.0f ); 1339 | that.stroke(); 1340 | } 1341 | 1342 | void stroke_inner_join( canvas &that, float width, float height ) 1343 | { 1344 | join_style const joins[] = { miter, bevel, rounded }; 1345 | for ( int index = 0; index < 3; ++index ) 1346 | { 1347 | float center = ( static_cast< float >( index ) + 0.5f ) / 3.0f * width; 1348 | that.begin_path(); 1349 | that.move_to( center - 0.05f * width, 0.275f * height ); 1350 | that.line_to( center, 0.225f * height ); 1351 | that.line_to( center + 0.025f * width, 0.25f * height ); 1352 | that.move_to( center - 0.05f * width, 0.775f * height ); 1353 | that.bezier_curve_to( center, 0.725f * height, 1354 | center, 0.725f * height, 1355 | center + 0.025f * width, 0.75f * height ); 1356 | that.line_join = joins[ index ]; 1357 | that.set_color( stroke_style, 0.0f, 0.0f, 0.0f, 1.0f ); 1358 | that.set_line_width( 0.3f * width ); 1359 | that.stroke(); 1360 | that.set_color( stroke_style, 1.0f, 0.0f, 0.0f, 1.0f ); 1361 | that.set_line_width( 1.0f ); 1362 | that.stroke(); 1363 | } 1364 | } 1365 | 1366 | void stroke_spiral( canvas &that, float width, float height ) 1367 | { 1368 | that.set_color( stroke_style, 0.0f, 0.0f, 0.0f, 1.0f ); 1369 | that.set_line_width( 2.0f ); 1370 | that.begin_path(); 1371 | float outside = min( width, height ) * 0.48f; 1372 | for ( float step = 0.0f; step <= 2048.0f; step += 1.0f ) 1373 | { 1374 | float parameter = ( step - 1024.0f ) / 1024.0f; 1375 | float angle = fabsf( parameter ) * 12.0f * 6.28318531f; 1376 | float radius = parameter * outside; 1377 | that.line_to( cosf( angle ) * radius + width * 0.5f, 1378 | sinf( angle ) * radius + height * 0.5f ); 1379 | } 1380 | that.stroke(); 1381 | } 1382 | 1383 | void stroke_long( canvas &that, float width, float height ) 1384 | { 1385 | that.set_color( stroke_style, 0.0f, 0.0f, 0.0f, 1.0f ); 1386 | for ( float step = 0.0f; step <= 29.0f; step += 1.0f ) 1387 | { 1388 | that.move_to( 0.4f * width, -23.0f * height ); 1389 | that.line_to( width * step / 29.0f, height ); 1390 | that.move_to( -23.0f * width, 0.4f * height ); 1391 | that.line_to( width, height * step / 29.0f ); 1392 | } 1393 | that.stroke(); 1394 | } 1395 | 1396 | void clip( canvas &that, float width, float height ) 1397 | { 1398 | float radius = min( width, height ) * 0.5f; 1399 | that.set_line_width( 8.0f ); 1400 | for ( int step = 0; step < 8; ++step ) 1401 | { 1402 | float fraction = static_cast< float >( step ) / 8.0f; 1403 | float angle = fraction * 6.28318531f; 1404 | that.set_color( stroke_style, 1405 | 0.0f, static_cast< float >( step & 1 ), 0.0f, 1.0f ); 1406 | that.begin_path(); 1407 | that.arc( 0.5f * width + 0.8f * radius * cosf( angle ), 1408 | 0.5f * height + 0.8f * radius * sinf( angle ), 1409 | radius, 0.0f, 6.28318531f ); 1410 | that.close_path(); 1411 | that.stroke(); 1412 | that.clip(); 1413 | } 1414 | that.begin_path(); 1415 | that.clip(); 1416 | that.set_color( fill_style, 1.0, 0.0f, 1.0f, 1.0f ); 1417 | that.fill_rectangle( 0.0f, 0.0f, width, height ); 1418 | } 1419 | 1420 | void clip_winding( canvas &that, float width, float height ) 1421 | { 1422 | that.move_to( 0.125f * width, 0.125f * height ); 1423 | that.line_to( 0.625f * width, 0.125f * height ); 1424 | that.line_to( 0.625f * width, 0.625f * height ); 1425 | that.line_to( 0.125f * width, 0.625f * height ); 1426 | that.move_to( 0.250f * width, 0.250f * height ); 1427 | that.line_to( 0.750f * width, 0.250f * height ); 1428 | that.line_to( 0.750f * width, 0.750f * height ); 1429 | that.line_to( 0.250f * width, 0.750f * height ); 1430 | that.move_to( 0.375f * width, 0.375f * height ); 1431 | that.line_to( 0.375f * width, 0.875f * height ); 1432 | that.line_to( 0.875f * width, 0.875f * height ); 1433 | that.line_to( 0.875f * width, 0.375f * height ); 1434 | that.set_color( fill_style, 1.0f, 0.0f, 0.0f, 1.0f ); 1435 | that.fill(); 1436 | that.clip(); 1437 | that.set_line_width( 4.0f ); 1438 | that.stroke(); 1439 | that.set_line_width( 6.0f ); 1440 | that.begin_path(); 1441 | for ( float step = 0.0f; step < 32.0f; step += 1.0f ) 1442 | { 1443 | that.move_to( step / 16.0f * width, 0.0f ); 1444 | that.line_to( step / 16.0f * width - width, height ); 1445 | } 1446 | that.stroke(); 1447 | } 1448 | 1449 | void is_point_in_path( canvas &that, float width, float height ) 1450 | { 1451 | that.set_color( fill_style, 0.0f, 0.0f, 1.0f, 1.0f ); 1452 | that.set_color( stroke_style, 1.0f, 1.0f, 1.0f, 1.0f ); 1453 | if ( that.is_point_in_path( 0.0f, 0.0f ) ) 1454 | that.fill_rectangle( 0.0f, 0.0f, 16.0f, 16.0f ); 1455 | that.scale( width / 256.0f, height / 256.0f ); 1456 | that.begin_path(); 1457 | that.move_to( 65.0f, 16.0f ); 1458 | that.line_to( 113.0f, 24.0f ); 1459 | that.bezier_curve_to( 113.0f, 24.0f, 93.0f, 126.0f, 119.0f, 160.0f ); 1460 | that.bezier_curve_to( 133.0f, 180.0f, 170.0f, 196.0f, 186.0f, 177.0f ); 1461 | that.bezier_curve_to( 198.0f, 162.0f, 182.0f, 130.0f, 166.0f, 118.0f ); 1462 | that.bezier_curve_to( 123.0f, 80.0f, 84.0f, 124.0f, 84.0f, 124.0f ); 1463 | that.line_to( 35.0f, 124.0f ); 1464 | that.line_to( 18.0f, 56.0f ); 1465 | that.line_to( 202.0f, 56.0f ); 1466 | that.line_to( 202.0f, 90.0f ); 1467 | that.bezier_curve_to( 202.0f, 90.0f, 240.0f, 168.0f, 209.0f, 202.0f ); 1468 | that.bezier_curve_to( 175.0f, 240.0f, 65.0f, 187.0f, 65.0f, 187.0f ); 1469 | that.close_path(); 1470 | that.translate( 40.0f, 160.0f ); 1471 | that.move_to( 110.0f, 0.0f ); 1472 | that.line_to( 0.0f, 0.0f ); 1473 | that.line_to( 0.0f, 0.0f ); 1474 | that.bezier_curve_to( 0.0f, 90.0f, 110.0f, 90.0f, 110.0f, 40.0f ); 1475 | that.close_path(); 1476 | that.fill(); 1477 | that.stroke(); 1478 | for ( int index = 0; index < 256; ++index ) 1479 | { 1480 | int bits = index; 1481 | bits = ( bits << 1 & 0xaa ) | ( bits >> 1 & 0x55 ); 1482 | bits = ( bits << 2 & 0xcc ) | ( bits >> 2 & 0x33 ); 1483 | bits = ( bits << 4 & 0xf0 ) | ( bits >> 4 & 0x0f ); 1484 | float x = static_cast< float >( bits ) / 256.0f * width; 1485 | float y = static_cast< float >( index ) / 256.0f * height; 1486 | that.rotate( 0.5f ); 1487 | bool inside = that.is_point_in_path( x, y ); 1488 | that.set_transform( 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f ); 1489 | that.set_color( stroke_style, 1.0f - inside, inside, 0.0f, 1.0f ); 1490 | that.stroke_rectangle( x - 1.5f, y - 1.5f, 3.0f, 3.0f ); 1491 | } 1492 | } 1493 | 1494 | void is_point_in_path_offscreen( canvas &that, float width, float height ) 1495 | { 1496 | that.set_color( fill_style, 0.0f, 0.0f, 1.0f, 1.0f ); 1497 | that.set_color( stroke_style, 1.0f, 1.0f, 1.0f, 1.0f ); 1498 | that.scale( width / 256.0f, height / 256.0f ); 1499 | that.begin_path(); 1500 | that.move_to( 321.0f, -240.0f ); 1501 | that.line_to( 369.0f, -232.0f ); 1502 | that.bezier_curve_to( 369.0f, -232.0f, 349.0f, -130.0f, 375.0f, -96.0f ); 1503 | that.bezier_curve_to( 389.0f, -76.0f, 426.0f, -60.0f, 442.0f, -79.0f ); 1504 | that.bezier_curve_to( 454.0f, -94.0f, 438.0f, -126.0f, 422.0f, -138.0f ); 1505 | that.bezier_curve_to( 379.0f, -176.0f, 340.0f, -132.0f, 340.0f, -132.0f ); 1506 | that.line_to( 291.0f, -132.0f ); 1507 | that.line_to( 274.0f, -200.0f ); 1508 | that.line_to( 458.0f, -200.0f ); 1509 | that.line_to( 458.0f, -166.0f ); 1510 | that.bezier_curve_to( 458.0f, -166.0f, 496.0f, -88.0f, 465.0f, -54.0f ); 1511 | that.bezier_curve_to( 431.0f, -16.0f, 321.0f, -69.0f, 321.0f, -69.0f ); 1512 | that.close_path(); 1513 | that.translate( 40.0f, 160.0f ); 1514 | that.move_to( 366.0f, -256.0f ); 1515 | that.line_to( 256.0f, -256.0f ); 1516 | that.line_to( 256.0f, -256.0f ); 1517 | that.bezier_curve_to( 256.0f, -166.0f, 366.0f, -166.0f, 366.0f, -216.0f ); 1518 | that.close_path(); 1519 | that.fill(); 1520 | that.stroke(); 1521 | for ( int index = 0; index < 256; ++index ) 1522 | { 1523 | int bits = index; 1524 | bits = ( bits << 1 & 0xaa ) | ( bits >> 1 & 0x55 ); 1525 | bits = ( bits << 2 & 0xcc ) | ( bits >> 2 & 0x33 ); 1526 | bits = ( bits << 4 & 0xf0 ) | ( bits >> 4 & 0x0f ); 1527 | float x = static_cast< float >( bits ) / 256.0f * width; 1528 | float y = static_cast< float >( index ) / 256.0f * height; 1529 | that.rotate( 0.5f ); 1530 | bool inside = that.is_point_in_path( x + width, y - height ); 1531 | that.set_transform( 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f ); 1532 | that.set_color( stroke_style, 1.0f - inside, inside, 0.0f, 1.0f ); 1533 | that.stroke_rectangle( x - 1.5f, y - 1.5f, 3.0f, 3.0f ); 1534 | } 1535 | } 1536 | 1537 | void clear_rectangle( canvas &that, float width, float height ) 1538 | { 1539 | that.set_color( stroke_style, 1.0f, 1.0f, 1.0f, 1.0f ); 1540 | that.set_color( fill_style, 0.4f, 0.05f, 0.2f, 1.0f ); 1541 | that.move_to( 0.0f, 0.0f ); 1542 | that.line_to( width, 0.0f ); 1543 | that.line_to( width, height ); 1544 | that.line_to( 0.0f, height ); 1545 | that.fill(); 1546 | that.rotate( 0.2f ); 1547 | that.begin_path(); 1548 | that.move_to( 0.2f * width, 0.2f * height ); 1549 | that.line_to( 0.8f * width, 0.2f * height ); 1550 | that.line_to( 0.8f * width, 0.8f * height ); 1551 | that.shadow_offset_x = 5.0f; 1552 | that.set_shadow_color( 0.0f, 0.0f, 0.0f, 1.0f ); 1553 | that.global_composite_operation = destination_atop; 1554 | that.set_global_alpha( 0.5f ); 1555 | for ( float y = -1.0f; y <= 1.0f; y += 1.0f ) 1556 | for ( float x = -1.0f; x <= 1.0f; x += 1.0f ) 1557 | that.clear_rectangle( ( 0.5f + 0.05f * x ) * width, 1558 | ( 0.5f + 0.05f * y ) * height, 1559 | x * 0.2f * width, y * 0.2f * height ); 1560 | that.set_global_alpha( 1.0f ); 1561 | that.global_composite_operation = source_over; 1562 | that.set_shadow_color( 0.0f, 0.0f, 0.0f, 0.0f ); 1563 | that.line_to( 0.2f * width, 0.8f * height ); 1564 | that.close_path(); 1565 | that.stroke(); 1566 | } 1567 | 1568 | void fill_rectangle( canvas &that, float width, float height ) 1569 | { 1570 | that.set_color( stroke_style, 0.0f, 0.0f, 0.0f, 1.0f ); 1571 | that.set_color( fill_style, 0.4f, 0.05f, 0.2f, 1.0f ); 1572 | that.rotate( 0.2f ); 1573 | that.move_to( 0.2f * width, 0.2f * height ); 1574 | that.line_to( 0.8f * width, 0.2f * height ); 1575 | that.line_to( 0.8f * width, 0.8f * height ); 1576 | for ( float y = -1.0f; y <= 1.0f; y += 1.0f ) 1577 | for ( float x = -1.0f; x <= 1.0f; x += 1.0f ) 1578 | that.fill_rectangle( ( 0.5f + 0.05f * x ) * width, 1579 | ( 0.5f + 0.05f * y ) * height, 1580 | x * 0.2f * width, y * 0.2f * height ); 1581 | that.line_to( 0.2f * width, 0.8f * height ); 1582 | that.close_path(); 1583 | that.stroke(); 1584 | } 1585 | 1586 | void stroke_rectangle( canvas &that, float width, float height ) 1587 | { 1588 | that.set_color( stroke_style, 0.0f, 0.0f, 0.0f, 1.0f ); 1589 | that.rotate( 0.2f ); 1590 | that.begin_path(); 1591 | that.move_to( 0.2f * width, 0.2f * height ); 1592 | that.line_to( 0.8f * width, 0.2f * height ); 1593 | that.line_to( 0.8f * width, 0.8f * height ); 1594 | for ( float y = -1.0f; y <= 1.0f; y += 1.0f ) 1595 | for ( float x = -1.0f; x <= 1.0f; x += 1.0f ) 1596 | that.stroke_rectangle( ( 0.5f + 0.05f * x ) * width, 1597 | ( 0.5f + 0.05f * y ) * height, 1598 | x * 0.2f * width, y * 0.2f * height ); 1599 | that.line_to( 0.2f * width, 0.8f * height ); 1600 | that.close_path(); 1601 | that.stroke(); 1602 | } 1603 | 1604 | void text_align( canvas &that, float width, float height ) 1605 | { 1606 | that.set_font( &font_a[ 0 ], static_cast< int >( font_a.size() ), 0.2f * height ); 1607 | that.rotate( 0.2f ); 1608 | that.set_color( fill_style, 0.0f, 0.0f, 0.0f, 1.0f ); 1609 | align_style const alignments[] = { leftward, center, rightward, start, ending }; 1610 | for ( int index = 0; index < 5; ++index ) 1611 | { 1612 | float base = ( 0.1f + 0.2f * static_cast< float >( index ) ) * height; 1613 | that.text_align = alignments[ index ]; 1614 | that.fill_text( "HIty", 0.5f * width, base ); 1615 | } 1616 | that.set_color( stroke_style, 1.0f, 0.0f, 0.0f, 0.5f ); 1617 | that.set_line_width( 1.0f ); 1618 | that.move_to( 0.0f, 0.5f * height ); 1619 | that.line_to( width, 0.5f * height ); 1620 | that.move_to( 0.5f * width, 0.0f ); 1621 | that.line_to( 0.5f * width, height ); 1622 | that.stroke(); 1623 | } 1624 | 1625 | void text_baseline( canvas &that, float width, float height ) 1626 | { 1627 | that.set_font( &font_a[ 0 ], static_cast< int >( font_a.size() ), 0.2f * height ); 1628 | that.rotate( 0.2f ); 1629 | that.set_color( fill_style, 0.0f, 0.0f, 0.0f, 1.0f ); 1630 | baseline_style const baselines[] = { 1631 | alphabetic, top, middle, bottom, hanging, ideographic }; 1632 | for ( int index = 0; index < 6; ++index ) 1633 | { 1634 | float left = ( 0.1f + 0.15f * static_cast< float >( index ) ) * width; 1635 | that.text_baseline = baselines[ index ]; 1636 | that.fill_text( "Iy", left, 0.5f * height ); 1637 | } 1638 | that.set_color( stroke_style, 1.0f, 0.0f, 0.0f, 0.5f ); 1639 | that.set_line_width( 1.0f ); 1640 | that.move_to( 0.0f, 0.5f * height ); 1641 | that.line_to( width, 0.5f * height ); 1642 | that.move_to( 0.5f * width, 0.0f ); 1643 | that.line_to( 0.5f * width, height ); 1644 | that.stroke(); 1645 | } 1646 | 1647 | void font( canvas &that, float width, float height ) 1648 | { 1649 | that.set_color( stroke_style, 1.0f, 0.0f, 0.0f, 1.0f ); 1650 | that.stroke_text( "D", 0.8f * width, 0.95f * height ); 1651 | that.set_color( fill_style, 1.0f, 0.0f, 0.0f, 1.0f ); 1652 | that.fill_text( "D", 0.9f * width, 0.95f * height ); 1653 | that.set_font( 0, 0, 0.1f * height ); 1654 | that.set_color( fill_style, 0.0f, 0.0f, 0.0f, 1.0f ); 1655 | that.set_font( &font_a[ 0 ], static_cast< int >( font_a.size() ), 0.2f * height ); 1656 | that.fill_text( "CE\xc3\x8d\xf4\x8f\xbf\xbd\xf0I", 0.0f, 0.20f * height ); 1657 | that.set_font( 0, 0, 0.1f * height ); 1658 | that.fill_text( "CE\xc3\x8d\xf4\x8f\xbf\xbd\xf0I", 0.65f * width, 0.20f * height ); 1659 | that.set_font( &font_b[ 0 ], static_cast< int >( font_b.size() ), 0.2f * height ); 1660 | that.fill_text( "CE\xc3\x8d\xf4\x8f\xbf\xbd\xf0I", 0.0f, 0.45f * height ); 1661 | that.set_font( &font_c[ 0 ], static_cast< int >( font_c.size() ), 0.2f * height ); 1662 | that.fill_text( "CE\xc3\x8d\xf4\x8f\xbf\xbd\xf0I", 0.0, 0.70f * height ); 1663 | that.set_color( fill_style, 1.0f, 0.0f, 0.0f, 1.0f ); 1664 | that.set_font( &font_d[ 0 ], static_cast< int >( font_d.size() ), 0.2f * height ); 1665 | that.fill_text( "D", 0.1f * width, 0.95f * height ); 1666 | that.set_font( &font_e[ 0 ], static_cast< int >( font_e.size() ), 0.2f * height ); 1667 | that.fill_text( "D", 0.2f * width, 0.95f * height ); 1668 | that.set_font( &font_f[ 0 ], static_cast< int >( font_f.size() ), 0.2f * height ); 1669 | that.fill_text( "D", 0.3f * width, 0.95f * height ); 1670 | that.set_font( &font_g[ 0 ], static_cast< int >( font_g.size() ), 0.2f * height ); 1671 | that.fill_text( "D", 0.4f * width, 0.95f * height ); 1672 | } 1673 | 1674 | void fill_text( canvas &that, float width, float height ) 1675 | { 1676 | that.set_linear_gradient( fill_style, 0.4f * width, 0.0f, 0.6f * width, 0.0f ); 1677 | that.add_color_stop( fill_style, 0.00f, 0.0f, 0.00f, 1.0f, 1.0f ); 1678 | that.add_color_stop( fill_style, 0.45f, 0.0f, 0.25f, 0.5f, 1.0f ); 1679 | that.add_color_stop( fill_style, 0.50f, 1.0f, 0.00f, 0.0f, 1.0f ); 1680 | that.add_color_stop( fill_style, 0.55f, 0.0f, 0.25f, 0.5f, 1.0f ); 1681 | that.add_color_stop( fill_style, 1.00f, 0.0f, 0.50f, 0.0f, 1.0f ); 1682 | that.set_font( &font_a[ 0 ], static_cast< int >( font_a.size() ), 0.3f * height ); 1683 | that.rotate( 0.2f ); 1684 | that.shadow_offset_x = 2.0f; 1685 | that.shadow_offset_y = 2.0f; 1686 | that.set_shadow_blur( 4.0f ); 1687 | that.set_shadow_color( 0.0f, 0.0f, 0.0f, 0.75f ); 1688 | that.move_to( 0.0f, 0.2f * height ); 1689 | that.fill_text( "Canvas", 0.1f * width, 0.2f * height ); 1690 | that.line_to( width, 0.2f * height ); 1691 | that.fill_text( "Ity\n*", 0.2f * width, 0.5f * height, width ); 1692 | that.move_to( 0.0f, 0.5f * height ); 1693 | that.fill_text( "*Canvas\fIty*", 0.2f * width, 0.8f * height, 0.7f * width ); 1694 | that.set_color( fill_style, 1.0f, 0.0f, 0.0f, 1.0f ); 1695 | that.fill_text( "****", 0.1f * width, 0.35f * height, 0.0f ); 1696 | that.line_to( width, 0.5f * height ); 1697 | that.set_shadow_color( 0.0f, 0.0f, 0.0f, 0.0f ); 1698 | that.set_color( stroke_style, 1.0f, 0.0f, 0.0f, 1.0f ); 1699 | that.set_line_width( 2.0f ); 1700 | that.stroke(); 1701 | } 1702 | 1703 | void stroke_text( canvas &that, float width, float height ) 1704 | { 1705 | that.set_color( stroke_style, 0.0f, 0.0f, 0.0f, 1.0f ); 1706 | that.set_font( &font_a[ 0 ], static_cast< int >( font_a.size() ), 0.3f * height ); 1707 | that.rotate( 0.2f ); 1708 | that.set_line_width( 2.0f ); 1709 | float segments[] = { 8.0f, 2.0f }; 1710 | that.set_line_dash( segments, 2 ); 1711 | that.move_to( 0.0f, 0.2f * height ); 1712 | that.stroke_text( "Canvas", 0.1f * width, 0.2f * height ); 1713 | that.line_to( width, 0.2f * height ); 1714 | that.stroke_text( "Ity\n*", 0.2f * width, 0.5f * height, width ); 1715 | that.move_to( 0.0f, 0.5f * height ); 1716 | that.stroke_text( "*Canvas\fIty*", 0.2f * width, 0.8f * height, 0.7f * width ); 1717 | that.set_color( stroke_style, 1.0f, 0.0f, 0.0f, 1.0f ); 1718 | that.stroke_text( "****", 0.1f * width, 0.35f * height, 0.0f ); 1719 | that.line_to( width, 0.5f * height ); 1720 | that.set_line_dash( 0, 0 ); 1721 | that.stroke(); 1722 | } 1723 | 1724 | void measure_text( canvas &that, float width, float height ) 1725 | { 1726 | that.set_color( fill_style, 0.0f, 0.0f, 0.0f, 1.0f ); 1727 | float place = 0.1f * width; 1728 | place += that.measure_text( "C" ); 1729 | that.set_font( &font_a[ 0 ], static_cast< int >( font_a.size() ), 0.3f * height ); 1730 | that.rotate( 0.5f ); 1731 | that.scale( 1.15f, 1.0f ); 1732 | that.fill_text( "C", place, 0.2f * height ); 1733 | place += that.measure_text( "C" ); 1734 | that.fill_text( "a", place, 0.25f * height ); 1735 | place += that.measure_text( "a" ); 1736 | that.fill_text( "nv", place, 0.2f * height ); 1737 | place += that.measure_text( "nv" ); 1738 | that.fill_text( "a", place, 0.15f * height ); 1739 | place += that.measure_text( "a" ); 1740 | that.fill_text( "s", place, 0.2f * height ); 1741 | } 1742 | 1743 | void draw_image( canvas &that, float width, float height ) 1744 | { 1745 | unsigned char checker[ 1024 ]; 1746 | for ( int index = 0; index < 1024; ++index ) 1747 | checker[ index ] = static_cast< unsigned char >( 1748 | ( ( ( index >> 2 & 1 ) ^ ( index >> 6 & 1 ) ) | 1749 | ( ( index & 3 ) == 3 ) ) * 255 ); 1750 | that.draw_image( checker, 16, 16, 64, 1751 | 0.0f, 0.0f, width * 0.75f, height * 0.75f ); 1752 | for ( float row = 0.0f; row < 4.0f; row += 1.0f ) 1753 | for ( float column = 0.0f; column < 4.0f; column += 1.0f ) 1754 | that.draw_image( checker, 16, 16, 64, 1755 | column * 17.25f, row * 17.25f, 16.0f, 16.0f ); 1756 | that.draw_image( checker, 16, 16, 64, 128.0f, 0.0f, 32.0f, 8.0f ); 1757 | that.draw_image( checker, 16, 16, 64, 128.0f, 48.0f, 32.0f, -32.0f ); 1758 | that.draw_image( checker, 16, 16, 64, 200.0f, 16.0f, -32.0f, 32.0f ); 1759 | that.draw_image( checker, 16, 16, 64, 128.0f, 64.0f, 32.0f, 0.0f ); 1760 | that.draw_image( 0, 16, 16, 64, 200.0f, 64.0f, 32.0f, 32.0f ); 1761 | unsigned char pixel[] = { 0, 255, 0, 255 }; 1762 | that.draw_image( pixel, 1, 1, 4, 1763 | width * 0.875f, height * 0.25f, 1.0f, 1.0f ); 1764 | that.draw_image( pixel, 1, 1, 4, 1765 | width * 0.875f, height * 0.5f, 16.0f, 16.0f ); 1766 | that.rotate( 0.2f ); 1767 | that.global_composite_operation = lighter; 1768 | that.set_global_alpha( 1.0f ); 1769 | that.draw_image( checker, 16, 16, 64, 1770 | 0.25f * width, 0.25f * height, 1771 | 0.5f * width, 0.5f * height ); 1772 | } 1773 | 1774 | void draw_image_matted( canvas &that, float width, float height ) 1775 | { 1776 | that.set_color( fill_style, 0.0f, 1.0f, 0.0f, 0.0f ); 1777 | that.fill_rectangle( 0.0f, 0.0f, width, height ); 1778 | unsigned char checker[ 36 ] = { 1779 | 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 1780 | 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 1781 | 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 1782 | }; 1783 | float y = 0.5f; 1784 | float size_y = 3.0f; 1785 | for ( int step_y = 0; step_y < 20 && y < height; ++step_y ) 1786 | { 1787 | float x = 0.5f; 1788 | float size_x = 3.0f; 1789 | for ( int step_x = 0; step_x < 20 && x < width; ++step_x ) 1790 | { 1791 | that.draw_image( checker, 3, 3, 12, x, y, size_x, size_y ); 1792 | x += size_x + 5.0f; 1793 | size_x *= 1.5f; 1794 | } 1795 | y += size_y + 5.0f; 1796 | size_y *= 1.5f; 1797 | } 1798 | } 1799 | 1800 | void get_image_data( canvas &that, float width, float height ) 1801 | { 1802 | for ( int index = 0; index < 100; ++index ) 1803 | { 1804 | that.set_color( fill_style, 1805 | static_cast< float >( index / 2 % 2 ), 1806 | static_cast< float >( index / 4 % 2 ), 1807 | static_cast< float >( index / 8 % 2 ), 1808 | static_cast< float >( index / 16 % 2 ) ); 1809 | that.fill_rectangle( 3.0f * static_cast< float >( index % 10 ), 1810 | 3.0f * static_cast< float >( index / 10 ), 1811 | 3.0f, 3.0f ); 1812 | } 1813 | unsigned char data[ 4939 ]; 1814 | data[ 0 ] = 150; 1815 | for ( int index = 1; index < 4939; ++index ) 1816 | data[ index ] = static_cast< unsigned char >( 1817 | ( data[ index - 1 ] * 137 + 53 ) & 255 ); 1818 | that.get_image_data( data + 2, 35, 35, 141, -10, -10 ); 1819 | unsigned hash = 0; 1820 | for ( int index = 0; index < 4939; ++index ) 1821 | hash = ( ( ( hash & 0x1ffff ) << 15 ) | ( hash >> 17 ) ) ^ data[ index ]; 1822 | unsigned const expected = 0xf53f9792; 1823 | that.set_color( fill_style, hash != expected, hash == expected, 0.0f, 1.0f ); 1824 | that.fill_rectangle( 30.0f, 0.0f, width, 30.0f ); 1825 | that.set_linear_gradient( fill_style, 0.0f, 0.0f, width, 0.0f ); 1826 | that.add_color_stop( fill_style, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f ); 1827 | that.add_color_stop( fill_style, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f ); 1828 | that.fill_rectangle( 0.0f, 0.45f * height, width, 0.1f * height ); 1829 | that.get_image_data( 0, 32, 32, 128, 0, 0 ); 1830 | } 1831 | 1832 | void put_image_data( canvas &that, float width, float height ) 1833 | { 1834 | unsigned char checker[ 2052 ]; 1835 | for ( int index = 0; index < 2048; ++index ) 1836 | checker[ index + 2 ] = static_cast< unsigned char >( 1837 | ( ( ( ( index >> 2 & 1 ) ^ ( index >> 7 & 1 ) ) | 1838 | ( ( index & 3 ) == 3 ) ) & 1839 | ( index >> 10 & 1 ) ) * 255 ); 1840 | checker[ 0 ] = 157; 1841 | checker[ 1 ] = 157; 1842 | checker[ 2050 ] = 157; 1843 | checker[ 2051 ] = 157; 1844 | that.set_color( fill_style, 0.4f, 0.05f, 0.2f, 1.0f ); 1845 | that.fill_rectangle( 0.0f, 0.0f, 0.25f * width, 0.25f * height ); 1846 | that.set_global_alpha( 0.5f ); 1847 | that.global_composite_operation = lighter; 1848 | that.rotate( 0.2f ); 1849 | for ( int y = -10; y < static_cast< int >( height ); y += 29 ) 1850 | for ( int x = -10; x < static_cast< int >( width ); x += 29 ) 1851 | that.put_image_data( checker + 6, 16, 16, 128, x, y ); 1852 | that.put_image_data( 0, 32, 32, 128, 0, 0 ); 1853 | } 1854 | 1855 | void save_restore( canvas &that, float width, float height ) 1856 | { 1857 | that.rectangle( width * 0.25f, height * 0.25f, 1858 | width * 0.25f, height * 0.25f ); 1859 | that.set_color( stroke_style, 0.0f, 0.0f, 1.0f, 1.0f ); 1860 | that.set_line_width( 8.0f ); 1861 | that.save(); 1862 | that.clip(); 1863 | that.begin_path(); 1864 | that.rectangle( width * 0.25f, height * 0.25f, 1865 | width * 0.5f, height * 0.5f ); 1866 | that.set_color( stroke_style, 1.0f, 0.0f, 0.0f, 1.0f ); 1867 | that.set_line_width( 1.0f ); 1868 | that.restore(); 1869 | that.restore(); 1870 | that.stroke(); 1871 | that.save(); 1872 | that.save(); 1873 | } 1874 | 1875 | void example_button( canvas &that, float width, float height ) 1876 | { 1877 | float left = roundf( 0.25f * width ); 1878 | float right = roundf( 0.75f * width ); 1879 | float top = roundf( 0.375f * height ); 1880 | float bottom = roundf( 0.625f * height ); 1881 | float mid_x = ( left + right ) * 0.5f; 1882 | float mid_y = ( top + bottom ) * 0.5f; 1883 | that.shadow_offset_x = 3.0f; 1884 | that.shadow_offset_y = 3.0f; 1885 | that.set_shadow_blur( 3.0f ); 1886 | that.set_shadow_color( 0.0f, 0.0f, 0.0f, 0.5f ); 1887 | that.set_linear_gradient( fill_style, 0.0f, top, 0.0f, bottom ); 1888 | that.add_color_stop( fill_style, 0.0f, 0.3f, 0.3f, 0.3f, 1.0f ); 1889 | that.add_color_stop( fill_style, 1.0f, 0.2f, 0.2f, 0.2f, 1.0f ); 1890 | that.move_to( left + 0.5f, mid_y ); 1891 | that.arc_to( left + 0.5f, top + 0.5f, mid_x, top + 0.5f, 4.0f ); 1892 | that.arc_to( right - 0.5f, top + 0.5f, right - 0.5f, mid_y, 4.0f ); 1893 | that.arc_to( right - 0.5f, bottom - 0.5f, mid_x, bottom - 0.5f, 4.0f ); 1894 | that.arc_to( left + 0.5f, bottom - 0.5f, left + 0.5f, mid_y, 4.0f ); 1895 | that.close_path(); 1896 | that.fill(); 1897 | that.set_shadow_color( 0.0f, 0.0f, 0.0f, 0.0f ); 1898 | that.set_font( &font_a[ 0 ], static_cast< int >( font_a.size() ), 0.075f * height ); 1899 | that.text_align = center; 1900 | that.text_baseline = middle; 1901 | that.set_color( fill_style, 0.8f, 0.8f, 0.8f, 1.0f ); 1902 | that.fill_text( "* Cats", 0.5f * width, 0.5f * height ); 1903 | that.set_color( fill_style, 0.4f, 0.4f, 0.4f, 1.0f ); 1904 | that.fill_rectangle( left + 4.0f, top + 1.0f, right - left - 8.0f, 1.0f ); 1905 | that.set_color( stroke_style, 0.1f, 0.1f, 0.1f, 1.0f ); 1906 | that.stroke(); 1907 | } 1908 | 1909 | void example_smiley( canvas &that, float width, float height ) 1910 | { 1911 | float center_x = 0.5f * width; 1912 | float center_y = 0.5f * height; 1913 | float radius = min( width, height ) * 0.4f; 1914 | that.set_radial_gradient( fill_style, 1915 | center_x, center_y, 0.0f, 1916 | center_x, center_y, radius ); 1917 | that.add_color_stop( fill_style, 0.0f, 1.0f, 0.9f, 0.2f, 1.0f ); 1918 | that.add_color_stop( fill_style, 0.95f, 0.95f, 0.65f, 0.15f, 1.0f ); 1919 | that.add_color_stop( fill_style, 1.0f, 0.9f, 0.55f, 0.0f, 1.0f ); 1920 | that.arc( center_x, center_y, radius, 0.0f, 6.28318531f ); 1921 | that.fill(); 1922 | that.set_linear_gradient( fill_style, 1923 | center_x, center_y - 0.95f * radius, 1924 | center_x, center_y ); 1925 | that.add_color_stop( fill_style, 0.0f, 1.0f, 1.0f, 1.0f, 0.5f ); 1926 | that.add_color_stop( fill_style, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f ); 1927 | that.begin_path(); 1928 | that.arc( center_x, center_y - 0.15f * radius, 0.8f * radius, 1929 | 0.0f, 6.28318531f ); 1930 | that.fill(); 1931 | that.set_color( stroke_style, 0.0f, 0.0f, 0.0f, 0.95f ); 1932 | that.set_line_width( 0.2f * radius ); 1933 | that.line_cap = circle; 1934 | that.begin_path(); 1935 | that.move_to( center_x - 0.2f * radius, center_y - 0.5f * radius ); 1936 | that.line_to( center_x - 0.2f * radius, center_y - 0.2f * radius ); 1937 | that.move_to( center_x + 0.2f * radius, center_y - 0.5f * radius ); 1938 | that.line_to( center_x + 0.2f * radius, center_y - 0.2f * radius ); 1939 | that.stroke(); 1940 | that.set_color( fill_style, 0.0f, 0.0f, 0.0f, 0.95f ); 1941 | that.begin_path(); 1942 | that.move_to( center_x - 0.6f * radius, center_y + 0.1f * radius ); 1943 | that.bezier_curve_to( center_x - 0.3f * radius, center_y + 0.8f * radius, 1944 | center_x + 0.3f * radius, center_y + 0.8f * radius, 1945 | center_x + 0.6f * radius, center_y + 0.1f * radius); 1946 | that.bezier_curve_to( center_x + 0.3f * radius, center_y + 0.3f * radius, 1947 | center_x - 0.3f * radius, center_y + 0.3f * radius, 1948 | center_x - 0.6f * radius, center_y + 0.1f * radius); 1949 | that.fill(); 1950 | } 1951 | 1952 | void example_knot( canvas &that, float width, float height ) 1953 | { 1954 | float points[ 6 ][ 8 ] = { 1955 | { 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f }, 1956 | { -1.0f, -1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f }, 1957 | { 2.0f, 1.0f, 2.0f, -2.0f, -1.0f, -2.0f, -1.0f, -1.0f }, 1958 | { -2.0f, -1.0f, -2.0f, 2.0f, 1.0f, 2.0f, 1.0f, 1.0f }, 1959 | { -2.0f, -1.0f, -2.0f, -3.0f, 0.0f, -3.0f, 0.0f, -1.0f }, 1960 | { 2.0f, 1.0f, 2.0f, 3.0f, 0.0f, 3.0f, 0.0f, 1.0f }, 1961 | }; 1962 | that.translate( width * 0.5f, height * 0.5f ); 1963 | that.scale( width * 0.17f, height * 0.17f ); 1964 | that.rotate( -15.0f * 3.14159265f / 180.0f ); 1965 | for ( int index = 0; index < 6; ++index ) 1966 | { 1967 | that.begin_path(); 1968 | that.move_to( 1969 | 1.01f * points[ index ][ 0 ] - 0.01f * points[ index ][ 2 ], 1970 | 1.01f * points[ index ][ 1 ] - 0.01f * points[ index ][ 3 ] ); 1971 | that.line_to( points[ index ][ 0 ], points[ index ][ 1 ] ); 1972 | that.bezier_curve_to( points[ index ][ 2 ], points[ index ][ 3 ], 1973 | points[ index ][ 4 ], points[ index ][ 5 ], 1974 | points[ index ][ 6 ], points[ index ][ 7 ] ); 1975 | that.line_to( 1976 | -0.01f * points[ index ][ 4 ] + 1.01f * points[ index ][ 6 ], 1977 | -0.01f * points[ index ][ 5 ] + 1.01f * points[ index ][ 7 ] ); 1978 | that.set_color( stroke_style, 0.0f, 0.0f, 0.0f, 1.0f ); 1979 | that.set_line_width( 0.75f ); 1980 | that.line_cap = butt; 1981 | that.stroke(); 1982 | that.set_radial_gradient( stroke_style, 1983 | 0.0f, 0.0f, 0.0f, 1984 | 0.0f, 0.0f, 3.0f ); 1985 | that.add_color_stop( stroke_style, 0.0f, 0.8f, 1.0f, 0.6f, 1.0f ); 1986 | that.add_color_stop( stroke_style, 1.0f, 0.1f, 0.5f, 0.1f, 1.0f ); 1987 | that.set_line_width( 0.5f ); 1988 | that.line_cap = circle; 1989 | that.stroke(); 1990 | } 1991 | } 1992 | 1993 | void example_icon( canvas &that, float width, float height ) 1994 | { 1995 | that.set_shadow_color( 0.0f, 0.0f, 0.0f, 1.0f ); 1996 | that.shadow_offset_x = width / 64.0f; 1997 | that.shadow_offset_y = height / 64.0f; 1998 | that.set_shadow_blur( std::min( width, height ) / 32.0f ); 1999 | that.scale( width / 32.0f, height / 32.0f ); 2000 | that.set_color( fill_style, 0.4f, 0.05f, 0.2f, 1.0f ); 2001 | that.move_to( 15.5f, 1.0f ); 2002 | that.arc_to( 30.0f, 1.0f, 30.0f, 15.5f, 6.0f ); 2003 | that.arc_to( 30.0f, 30.0f, 15.5f, 30.0f, 6.0f ); 2004 | that.arc_to( 1.0f, 30.0f, 1.0f, 15.5f, 6.0f ); 2005 | that.arc_to( 1.0f, 1.0f, 15.5f, 1.0f, 6.0f ); 2006 | that.fill(); 2007 | that.set_color( stroke_style, 0.5f, 0.5f, 0.5f, 1.0f ); 2008 | that.begin_path(); 2009 | that.move_to( 11.0f, 16.0f ); 2010 | that.line_to( 27.0f, 16.0f ); 2011 | that.move_to( 2.0f, 23.0f ); 2012 | that.line_to( 29.0f, 23.0f ); 2013 | that.stroke(); 2014 | that.set_color( stroke_style, 0.75f, 0.75f, 0.75f, 1.0f ); 2015 | that.begin_path(); 2016 | that.arc( 25.0f, 22.0f, 0.5f, 0.0f, 6.28318531f ); 2017 | that.move_to( 19.0f, 6.0f ); 2018 | that.line_to( 18.5f, 8.0f ); 2019 | that.move_to( 20.0f, 6.0f ); 2020 | that.line_to( 20.0f, 8.0f ); 2021 | that.move_to( 21.0f, 6.0f ); 2022 | that.line_to( 21.5f, 8.0f ); 2023 | that.move_to( 17.0f, 14.0f ); 2024 | that.line_to( 16.0f, 18.0f ); 2025 | that.move_to( 20.0f, 14.0f ); 2026 | that.line_to( 20.0f, 18.0f ); 2027 | that.move_to( 23.0f, 14.0f ); 2028 | that.line_to( 24.0f, 18.0f ); 2029 | that.move_to( 18.0f, 9.0f ); 2030 | that.line_to( 22.0f, 9.0f ); 2031 | that.move_to( 18.0f, 13.0f ); 2032 | that.line_to( 22.0f, 13.0f ); 2033 | that.rectangle( 16.0f, 8.0f, 8.0f, 6.0f ); 2034 | that.stroke(); 2035 | that.set_color( stroke_style, 1.0f, 1.0f, 1.0f, 1.0f ); 2036 | that.begin_path(); 2037 | that.arc( 19.0f, 12.0f, 9.0f, 0.0f, 6.28318531f ); 2038 | that.move_to( 12.3f, 17.3f ); 2039 | that.line_to( 3.3f, 26.3f ); 2040 | that.move_to( 13.0f, 18.0f ); 2041 | that.line_to( 4.0f, 27.0f ); 2042 | that.move_to( 13.7f, 18.7f ); 2043 | that.line_to( 4.7f, 27.7f ); 2044 | that.stroke(); 2045 | } 2046 | 2047 | void example_illusion( canvas &that, float width, float height ) 2048 | { 2049 | that.set_color( fill_style, 0.0f, 0.4f, 1.0f, 1.0f ); 2050 | that.fill_rectangle( 0.0f, 0.0f, width, height ); 2051 | that.set_color( fill_style, 0.8f, 0.8f, 0.0f, 1.0f ); 2052 | that.set_line_width( 0.4f ); 2053 | for ( float spot = 0.0f; spot < 240.0f; spot += 1.0f ) 2054 | { 2055 | float angle = fmodf( spot * 0.61803398875f, 1.0f ) * 6.28318531f; 2056 | float radius = spot / 240.0f * 0.5f * hypotf( width, height ); 2057 | float size = min( width, height ) * sqrtf( spot ) / 240.0f; 2058 | that.set_transform( 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f ); 2059 | that.translate( 0.5f * width + radius * cosf( angle ), 2060 | 0.5f * height + radius * sinf( angle ) ); 2061 | that.rotate( angle - 1.3f ); 2062 | that.scale( 0.8f * size, 0.6f * size ); 2063 | that.rotate( 1.3f ); 2064 | that.begin_path(); 2065 | that.arc( 0.0f, 0.0f, 1.0f, 0.0f, 6.28318531f ); 2066 | that.fill(); 2067 | that.begin_path(); 2068 | that.arc( 0.0f, 0.0f, 1.0f, 0.0f, 3.14159265f ); 2069 | that.set_color( stroke_style, 1.0f, 1.0f, 1.0f, 1.0f ); 2070 | that.stroke(); 2071 | that.begin_path(); 2072 | that.arc( 0.0f, 0.0f, 1.0f, 3.14159265f, 6.28318531f ); 2073 | that.set_color( stroke_style, 0.0f, 0.0f, 0.0f, 1.0f ); 2074 | that.stroke(); 2075 | } 2076 | } 2077 | 2078 | void example_star( canvas &that, float width, float height ) 2079 | { 2080 | that.scale( width / 256.0f, height / 256.0f ); 2081 | that.move_to( 128.0f, 28.0f ); 2082 | that.line_to( 157.0f, 87.0f ); 2083 | that.line_to( 223.0f, 97.0f ); 2084 | that.line_to( 175.0f, 143.0f ); 2085 | that.line_to( 186.0f, 208.0f ); 2086 | that.line_to( 128.0f, 178.0f ); 2087 | that.line_to( 69.0f, 208.0f ); 2088 | that.line_to( 80.0f, 143.0f ); 2089 | that.line_to( 32.0f, 97.0f ); 2090 | that.line_to( 98.0f, 87.0f ); 2091 | that.close_path(); 2092 | that.set_shadow_blur( 8.0f ); 2093 | that.shadow_offset_y = 4.0f; 2094 | that.set_shadow_color( 0.0f, 0.0f, 0.0f, 0.5f ); 2095 | that.set_color( fill_style, 1.0f, 0.9f, 0.2f, 1.0f ); 2096 | that.fill(); 2097 | that.line_join = rounded; 2098 | that.set_line_width( 12.0f ); 2099 | that.set_color( stroke_style, 0.9f, 0.0f, 0.5f, 1.0f ); 2100 | that.stroke(); 2101 | float segments[] = { 21.0f, 9.0f, 1.0f, 9.0f, 7.0f, 9.0f, 1.0f, 9.0f }; 2102 | that.set_line_dash( segments, 8 ); 2103 | that.line_dash_offset = 10.0f; 2104 | that.line_cap = circle; 2105 | that.set_line_width( 6.0f ); 2106 | that.set_color( stroke_style, 0.95f, 0.65f, 0.15f, 1.0f ); 2107 | that.stroke(); 2108 | that.set_shadow_color( 0.0f, 0.0f, 0.0f, 0.0f ); 2109 | that.set_linear_gradient( fill_style, 64.0f, 0.0f, 192.0f, 256.0f ); 2110 | that.add_color_stop( fill_style, 0.30f, 1.0f, 1.0f, 1.0f, 0.0f ); 2111 | that.add_color_stop( fill_style, 0.35f, 1.0f, 1.0f, 1.0f, 0.8f ); 2112 | that.add_color_stop( fill_style, 0.45f, 1.0f, 1.0f, 1.0f, 0.8f ); 2113 | that.add_color_stop( fill_style, 0.50f, 1.0f, 1.0f, 1.0f, 0.0f ); 2114 | that.global_composite_operation = source_atop; 2115 | that.fill_rectangle( 0.0f, 0.0f, 256.0f, 256.0f ); 2116 | } 2117 | 2118 | void example_neon( canvas &that, float width, float height ) 2119 | { 2120 | that.scale( width / 256.0f, height / 256.0f ); 2121 | that.set_color( fill_style, 0.0f, 0.0625f, 0.125f, 1.0f ); 2122 | that.fill_rectangle( 0.0f, 0.0f, 256.0f, 256.0f ); 2123 | that.move_to( 45.5f, 96.2f ); 2124 | that.bezier_curve_to( 45.5f, 96.2f, 31.3f, 106.2f, 31.5f, 113.1f ); 2125 | that.bezier_curve_to( 31.7f, 119.5f, 50.6f, 104.8f, 50.6f, 93.9f ); 2126 | that.bezier_curve_to( 50.6f, 91.1f, 46.6f, 89.1f, 43.3f, 89.4f ); 2127 | that.bezier_curve_to( 27.5f, 90.6f, 8.5f, 108.2f, 8.8f, 121.8f ); 2128 | that.bezier_curve_to( 9.1f, 133.1f, 21.3f, 136.6f, 29.8f, 136.3f ); 2129 | that.bezier_curve_to( 52.4f, 135.5f, 62.3f, 115.6f, 62.3f, 115.6f ); 2130 | that.move_to( 81.0f, 120.2f ); 2131 | that.bezier_curve_to( 81.0f, 120.2f, 60.2f, 123.0f, 59.7f, 130.8f ); 2132 | that.bezier_curve_to( 59.2f, 140.6f, 73.8f, 136.4f, 78.3f, 125.3f ); 2133 | that.move_to( 80.7f, 130.5f ); 2134 | that.bezier_curve_to( 79.5f, 132.4f, 80.9f, 135.0f, 83.4f, 135.0f ); 2135 | that.bezier_curve_to( 95.8f, 135.6f, 99.3f, 122.5f, 111.4f, 121.6f ); 2136 | that.bezier_curve_to( 112.8f, 121.5f, 114.0f, 123.0f, 114.0f, 124.3f ); 2137 | that.bezier_curve_to( 113.9f, 126.1f, 106.7f, 133.9f, 106.7f, 133.9f ); 2138 | that.move_to( 118.5f, 122.9f ); 2139 | that.bezier_curve_to( 118.5f, 122.9f, 122.1f, 118.8f, 126.1f, 122.0f ); 2140 | that.bezier_curve_to( 131.4f, 126.4f, 118.7f, 131.6f, 124.3f, 134.7f ); 2141 | that.bezier_curve_to( 130.0f, 137.8f, 150.0f, 116.5f, 156.0f, 120.2f ); 2142 | that.bezier_curve_to( 160.2f, 122.8f, 149.0f, 133.5f, 155.6f, 133.6f ); 2143 | that.bezier_curve_to( 162.0f, 133.4f, 173.8f, 118.3f, 168.0f, 117.8f ); 2144 | that.move_to( 173.1f, 123.2f ); 2145 | that.bezier_curve_to( 177.8f, 124.8f, 182.8f, 123.2f, 187.0f, 119.7f ); 2146 | that.move_to( 206.1f, 118.6f ); 2147 | that.bezier_curve_to( 206.1f, 118.6f, 185.3f, 121.3f, 185.1f, 129.1f ); 2148 | that.bezier_curve_to( 185.0f, 138.7f, 199.9f, 135.4f, 203.6f, 123.6f ); 2149 | that.move_to( 205.6f, 129.9f ); 2150 | that.bezier_curve_to( 204.4f, 131.8f, 205.8f, 134.4f, 208.3f, 134.4f ); 2151 | that.bezier_curve_to( 220.3f, 134.4f, 246.6f, 117.1f, 246.6f, 117.1f ); 2152 | that.move_to( 247.0f, 122.4f ); 2153 | that.bezier_curve_to( 245.9f, 128.5f, 243.9f, 139.7f, 231.2f, 131.5f ); 2154 | that.line_cap = circle; 2155 | that.set_shadow_color( 1.0f, 0.5f, 0.0f, 1.0f ); 2156 | that.set_shadow_blur( 20.0f ); 2157 | that.set_line_width( 4.0f ); 2158 | that.set_color( stroke_style, 1.0f, 0.5f, 0.0f, 1.0f ); 2159 | that.stroke(); 2160 | that.set_shadow_blur( 5.0f ); 2161 | that.set_line_width( 3.0f ); 2162 | that.set_color( stroke_style, 1.0f, 0.625f, 0.0f, 1.0f ); 2163 | that.stroke(); 2164 | } 2165 | 2166 | } 2167 | 2168 | // ======== TEST HARNESS ======== 2169 | 2170 | // This is the table of tests to run. To add a new test to the suite, write a 2171 | // function for it above with the same function signature as the other tests, 2172 | // and then register it here. Just use zero initially for the expected hash; 2173 | // the test will fail, but it will report the hash that it produced and that 2174 | // can then be put in here. Alternately, run the program with --table to 2175 | // recompute hashes and output them in a form suitable for inserting here. 2176 | // Note that for computing expected hashes, this test program should be 2177 | // compiled with all optimizations disabled (e.g., -O0)! 2178 | // 2179 | struct test 2180 | { 2181 | unsigned hash; 2182 | int width, height; 2183 | void ( *call )( canvas &, float, float ); 2184 | char const *name; 2185 | } const tests[] = { 2186 | { 0xc99ddee7, 256, 256, scale_uniform, "scale_uniform" }, 2187 | { 0xe93d3c6f, 256, 256, scale_non_uniform, "scale_non_uniform" }, 2188 | { 0x05a0e377, 256, 256, rotate, "rotate" }, 2189 | { 0x36e7fa56, 256, 256, translate, "translate" }, 2190 | { 0xcfae3e4f, 256, 256, transform, "transform" }, 2191 | { 0x98f5594a, 256, 256, transform_fill, "transform_fill" }, 2192 | { 0x822964b0, 256, 256, transform_stroke, "transform_stroke" }, 2193 | { 0xb7056a3a, 256, 256, set_transform, "set_transform" }, 2194 | { 0x8f6dd6c3, 256, 256, global_alpha, "global_alpha" }, 2195 | { 0x98a0609d, 256, 256, global_composite_operation, "global_composite_operation" }, 2196 | { 0x9def5b00, 256, 256, shadow_color, "shadow_color" }, 2197 | { 0x8294edd8, 256, 256, shadow_offset, "shadow_offset" }, 2198 | { 0xcdeba51c, 256, 256, shadow_offset_offscreen, "shadow_offset_offscreen" }, 2199 | { 0x5b542224, 256, 256, shadow_blur, "shadow_blur" }, 2200 | { 0xd6c150e6, 256, 256, shadow_blur_offscreen, "shadow_blur_offscreen" }, 2201 | { 0x5affc092, 256, 256, shadow_blur_composite, "shadow_blur_composite" }, 2202 | { 0x1720e9b2, 256, 256, line_width, "line_width" }, 2203 | { 0xf8d2bb0d, 256, 256, line_width_angular, "line_width_angular" }, 2204 | { 0x7bda8673, 256, 256, line_cap, "line_cap" }, 2205 | { 0x53639198, 256, 256, line_cap_offscreen, "line_cap_offscreen" }, 2206 | { 0x8f49c41d, 256, 256, line_join, "line_join" }, 2207 | { 0xca27ce8c, 256, 256, line_join_offscreen, "line_join_offscreen" }, 2208 | { 0xe68273e2, 256, 256, miter_limit, "miter_limit" }, 2209 | { 0x27c38a8a, 256, 256, line_dash_offset, "line_dash_offset" }, 2210 | { 0x129f9595, 256, 256, line_dash, "line_dash" }, 2211 | { 0x88a74152, 256, 256, line_dash_closed, "line_dash_closed" }, 2212 | { 0x064f194d, 256, 256, line_dash_overlap, "line_dash_overlap" }, 2213 | { 0xf7259c0f, 256, 256, line_dash_offscreen, "line_dash_offscreen" }, 2214 | { 0xeb4338e8, 256, 256, color, "color" }, 2215 | { 0x6dc35a07, 256, 256, linear_gradient, "linear_gradient" }, 2216 | { 0x418fe678, 256, 256, radial_gradient, "radial_gradient" }, 2217 | { 0x67aada11, 256, 256, color_stop, "color_stop" }, 2218 | { 0xc6c721d6, 256, 256, pattern, "pattern" }, 2219 | { 0xb0b391cd, 256, 256, begin_path, "begin_path" }, 2220 | { 0xf79ed394, 256, 256, move_to, "move_to" }, 2221 | { 0xe9602309, 256, 256, close_path, "close_path" }, 2222 | { 0x3160ace7, 256, 256, line_to, "line_to" }, 2223 | { 0xb6176812, 256, 256, quadratic_curve_to, "quadratic_curve_to" }, 2224 | { 0x5f523029, 256, 256, bezier_curve_to, "bezier_curve_to" }, 2225 | { 0x1f847aaf, 256, 256, arc_to, "arc_to" }, 2226 | { 0x26457553, 256, 256, arc, "arc" }, 2227 | { 0x7520990c, 256, 256, rectangle, "rectangle" }, 2228 | { 0xf1d774dc, 256, 256, fill, "fill" }, 2229 | { 0x5e6e6b75, 256, 256, fill_rounding, "fill_rounding" }, 2230 | { 0xf0cf6566, 256, 256, fill_converging, "fill_converging" }, 2231 | { 0x3692d10e, 256, 256, fill_zone_plate, "fill_zone_plate" }, 2232 | { 0x2003f926, 256, 256, stroke, "stroke" }, 2233 | { 0xc44fc157, 256, 256, stroke_wide, "stroke_wide" }, 2234 | { 0x691cfe49, 256, 256, stroke_inner_join, "stroke_inner_join" }, 2235 | { 0xc0bd9324, 256, 256, stroke_spiral, "stroke_spiral" }, 2236 | { 0x3b2dae15, 256, 256, stroke_long, "stroke_long" }, 2237 | { 0xa7e06559, 256, 256, clip, "clip" }, 2238 | { 0x31e6112b, 256, 256, clip_winding, "clip_winding" }, 2239 | { 0xc2188d67, 256, 256, is_point_in_path, "is_point_in_path" }, 2240 | { 0x6505bdc9, 256, 256, is_point_in_path_offscreen, "is_point_in_path_offscreen" }, 2241 | { 0x5e792c96, 256, 256, clear_rectangle, "clear_rectangle" }, 2242 | { 0x286e96fa, 256, 256, fill_rectangle, "fill_rectangle" }, 2243 | { 0xc2b0803d, 256, 256, stroke_rectangle, "stroke_rectangle" }, 2244 | { 0xe6c4d9c7, 256, 256, text_align, "text_align" }, 2245 | { 0x72cb6b06, 256, 256, text_baseline, "text_baseline" }, 2246 | { 0x4d41daa2, 256, 256, font, "font" }, 2247 | { 0x70e3232d, 256, 256, fill_text, "fill_text" }, 2248 | { 0xed6477c8, 256, 256, stroke_text, "stroke_text" }, 2249 | { 0x32d1ee3b, 256, 256, measure_text, "measure_text" }, 2250 | { 0x78cb460c, 256, 256, draw_image, "draw_image" }, 2251 | { 0xb530077b, 256, 256, draw_image_matted, "draw_image_matted" }, 2252 | { 0xaf04e7a2, 256, 256, get_image_data, "get_image_data" }, 2253 | { 0x5acae0b6, 256, 256, put_image_data, "put_image_data" }, 2254 | { 0xb6e854b1, 256, 256, save_restore, "save_restore" }, 2255 | { 0x62bc9606, 256, 256, example_button, "example_button" }, 2256 | { 0x92731a7b, 256, 256, example_smiley, "example_smiley" }, 2257 | { 0xe2f1e1de, 256, 256, example_knot, "example_knot" }, 2258 | { 0xc02d01ea, 256, 256, example_icon, "example_icon" }, 2259 | { 0xa1607c4a, 256, 256, example_illusion, "example_illusion" }, 2260 | { 0x7c861f87, 256, 256, example_star, "example_star" }, 2261 | { 0x429ca194, 256, 256, example_neon, "example_neon" }, 2262 | }; 2263 | 2264 | // Simple glob style string matcher. This accepts both * and ? glob 2265 | // characters. It potentially has exponential run time, but as it is only 2266 | // used for matching against the names of tests, this is acceptable. 2267 | // 2268 | bool glob_match( 2269 | char const *pattern, 2270 | char const *name ) 2271 | { 2272 | if ( !*pattern && !*name ) 2273 | return true; 2274 | if ( *pattern == '*' ) 2275 | return glob_match( pattern + 1, name ) || 2276 | ( *name && glob_match( pattern, name + 1 ) ); 2277 | if ( *pattern == '?' ) 2278 | return *name && glob_match( pattern + 1, name + 1 ); 2279 | return *name == *pattern && glob_match( pattern + 1, name + 1 ); 2280 | } 2281 | 2282 | // Simple Base64 decoder. This is used at startup to decode the string 2283 | // literals containing embedded resource data, namely font files in TTF form. 2284 | // 2285 | void base64_decode( 2286 | char const *input, 2287 | vector< unsigned char > &output ) 2288 | { 2289 | int index = 0; 2290 | int data = 0; 2291 | int held = 0; 2292 | while ( int symbol = input[ index++ ] ) 2293 | { 2294 | if ( symbol == '=' ) 2295 | break; 2296 | int value = ( 'A' <= symbol && symbol <= 'Z' ? symbol - 'A' : 2297 | 'a' <= symbol && symbol <= 'z' ? symbol - 'a' + 26 : 2298 | '0' <= symbol && symbol <= '9' ? symbol - '0' + 52 : 2299 | symbol == '+' ? 62 : 2300 | symbol == '/' ? 63 : 2301 | 0 ); 2302 | data = data << 6 | value; 2303 | held += 6; 2304 | if ( held >= 8 ) 2305 | { 2306 | held -= 8; 2307 | output.push_back( static_cast< unsigned char >( ( data >> held ) & 0xff ) ); 2308 | data &= ( 1 << held ) - 1; 2309 | } 2310 | } 2311 | } 2312 | 2313 | // Time in seconds since an arbitrary point. This is only used for the 2314 | // relative difference between the values before and after a test runs, so the 2315 | // starting point does not particularly matter as long as it is consistent. 2316 | // 2317 | double get_seconds() 2318 | { 2319 | #if defined( __linux__ ) 2320 | timespec now; 2321 | clock_gettime( CLOCK_MONOTONIC, &now ); 2322 | return ( static_cast< double >( now.tv_sec ) + 2323 | static_cast< double >( now.tv_nsec ) * 1.0e-9 ); 2324 | #elif defined( _WIN32 ) 2325 | static double rate = 0.0; 2326 | if ( rate == 0.0 ) 2327 | { 2328 | static LARGE_INTEGER frequency; 2329 | QueryPerformanceFrequency( &frequency ); 2330 | rate = 1.0 / frequency.QuadPart; 2331 | } 2332 | LARGE_INTEGER now; 2333 | QueryPerformanceCounter( &now ); 2334 | return now.QuadPart * rate; 2335 | #elif defined( __MACH__ ) 2336 | static double rate = 0.0; 2337 | if ( rate == 0.0 ) 2338 | { 2339 | static mach_timebase_info_data_t frequency; 2340 | mach_timebase_info( &frequency ); 2341 | rate = frequency.numer * 1.0e-9 / frequency.denom; 2342 | } 2343 | return mach_absolute_time() * rate; 2344 | #else 2345 | timeval now; 2346 | gettimeofday( &now, 0 ); 2347 | return now.tv_sec + now.tv_usec * 1.0e-6; 2348 | #endif 2349 | } 2350 | 2351 | // Generate a 32-bit hash for an RGBA8 image. This is a custom image hash 2352 | // function that is somewhere between a checksum/cryptographic/CRC style 2353 | // hash that detects bit-level differences, and a typical perceptual image 2354 | // hash (e.g., dhash) that matches images even with substantial alterations. 2355 | // It is inspired by locality sensitive hashing techniques and is designed 2356 | // to be tolerant of small pixel variations produced by numeric differences 2357 | // from moderately aggressive compiler optimizations, while at the same time 2358 | // detecting color changes or pixel-sized shifts. For each channel within a 2359 | // pixel, it compares the value against its neighboring pixel to the right 2360 | // and down (with wrapping) to check for edge crossings. Depending on the 2361 | // strength and direction of the edge, it may then toggle some bits within 2362 | // a group at a pseudorandom position within the hash. The hashes can then 2363 | // be compared by their Hamming distance. 2364 | // 2365 | unsigned hash_image( 2366 | unsigned char const *image, 2367 | int width, 2368 | int height ) 2369 | { 2370 | unsigned hash = 0; 2371 | unsigned state = ~0u; 2372 | for ( int y = 0; y < height; ++y ) 2373 | for ( int x = 0; x < width; ++x ) 2374 | for ( int channel = 0; channel < 4; ++channel ) 2375 | { 2376 | int next_x = ( x + 1 ) % width; 2377 | int next_y = ( y + 1 ) % height; 2378 | int current = image[ ( y * width + x ) * 4 + channel ]; 2379 | int down = image[ ( next_y * width + x ) * 4 + channel ]; 2380 | int right = image[ ( y * width + next_x ) * 4 + channel ]; 2381 | int threshold = 8; 2382 | if ( channel < 3 ) 2383 | { 2384 | current *= image[ ( y * width + x ) * 4 + 3 ]; 2385 | down *= image[ ( next_y * width + x ) * 4 + 3 ]; 2386 | right *= image[ ( y * width + next_x ) * 4 + 3 ]; 2387 | threshold *= 255; 2388 | } 2389 | unsigned edges = 2390 | ( current - down > threshold * 16 ? 128u : 0u ) | 2391 | ( current - down > threshold ? 64u : 0u ) | 2392 | ( down - current > threshold * 16 ? 32u : 0u ) | 2393 | ( down - current > threshold ? 16u : 0u ) | 2394 | ( current - right > threshold * 16 ? 8u : 0u ) | 2395 | ( current - right > threshold ? 4u : 0u ) | 2396 | ( right - current > threshold * 16 ? 2u : 0u ) | 2397 | ( right - current > threshold ? 1u : 0u ); 2398 | state ^= ( state & 0x7ffff ) << 13; 2399 | state ^= state >> 17; 2400 | state ^= ( state & 0x7ffffff ) << 5; 2401 | if ( unsigned roll = state >> 27 ) 2402 | edges = ( edges & ( ~0u >> roll ) ) << roll | 2403 | edges >> ( 32 - roll ); 2404 | hash ^= edges; 2405 | } 2406 | return hash; 2407 | } 2408 | 2409 | // Simple single-function PNG writer. This writes perfectly valid, 2410 | // though uncompressed, PNG files from sRGBA8 image data, using deflate's 2411 | // uncompressed storage mode and wrapping it in a zlib and PNG container. 2412 | // There are much simpler formats for RGBA8 images, such as TGA, but support 2413 | // for reading the PNG format is nearly universal. 2414 | // 2415 | void write_png( 2416 | string const &filename, 2417 | unsigned char const *image, 2418 | int width, 2419 | int height ) 2420 | { 2421 | ofstream output( filename.c_str(), ios::binary ); 2422 | unsigned table[ 256 ]; 2423 | for ( unsigned index = 0; index < 256; ++index ) 2424 | { 2425 | unsigned value = index; 2426 | for ( int step = 0; step < 8; ++step ) 2427 | value = ( value & 1 ? 0xedb88320u : 0u ) ^ ( value >> 1 ); 2428 | table[ index ] = value; 2429 | } 2430 | int idat_size = 6 + height * ( 6 + width * 4 ); 2431 | unsigned char header[] = 2432 | { 2433 | /* 0 */ 137, 80, 78, 71, 13, 10, 26, 10, // Signature 2434 | /* 8 */ 0, 0, 0, 13, 73, 72, 68, 82, // IHDR 2435 | /* 16 */ static_cast< unsigned char >( width >> 24 ), 2436 | /* 17 */ static_cast< unsigned char >( width >> 16 ), 2437 | /* 18 */ static_cast< unsigned char >( width >> 8 ), 2438 | /* 19 */ static_cast< unsigned char >( width >> 0 ), 2439 | /* 20 */ static_cast< unsigned char >( height >> 24 ), 2440 | /* 21 */ static_cast< unsigned char >( height >> 16 ), 2441 | /* 22 */ static_cast< unsigned char >( height >> 8 ), 2442 | /* 23 */ static_cast< unsigned char >( height >> 0 ), 2443 | /* 24 */ 8, 6, 0, 0, 0, 2444 | /* 29 */ 0, 0, 0, 0, 2445 | /* 33 */ 0, 0, 0, 1, 115, 82, 71, 66, // sRGB 2446 | /* 41 */ 0, 2447 | /* 42 */ 174, 206, 28, 233, 2448 | /* 46 */ static_cast< unsigned char >( idat_size >> 24 ), // IDAT 2449 | /* 47 */ static_cast< unsigned char >( idat_size >> 16 ), 2450 | /* 48 */ static_cast< unsigned char >( idat_size >> 8 ), 2451 | /* 49 */ static_cast< unsigned char >( idat_size >> 0 ), 2452 | /* 50 */ 73, 68, 65, 84, 2453 | /* 54 */ 120, 1, 2454 | }; 2455 | unsigned crc = ~0u; 2456 | for ( int index = 12; index < 29; ++index ) 2457 | crc = table[ ( crc ^ header[ index ] ) & 0xff ] ^ ( crc >> 8 ); 2458 | header[ 29 ] = static_cast< unsigned char >( ~crc >> 24 ); 2459 | header[ 30 ] = static_cast< unsigned char >( ~crc >> 16 ); 2460 | header[ 31 ] = static_cast< unsigned char >( ~crc >> 8 ); 2461 | header[ 32 ] = static_cast< unsigned char >( ~crc >> 0 ); 2462 | output.write( reinterpret_cast< char * >( header ), 56 ); 2463 | crc = ~0u; 2464 | for ( int index = 50; index < 56; ++index ) 2465 | crc = table[ ( crc ^ header[ index ] ) & 0xff ] ^ ( crc >> 8 ); 2466 | int check_1 = 1; 2467 | int check_2 = 0; 2468 | int row_size = 1 + width * 4; 2469 | for ( int y = 0; y < height; ++y, image += width * 4 ) 2470 | { 2471 | unsigned char prefix[] = { 2472 | /* 0 */ static_cast< unsigned char >( y + 1 == height ), 2473 | /* 1 */ static_cast< unsigned char >( ( row_size >> 0 ) ), 2474 | /* 2 */ static_cast< unsigned char >( ( row_size >> 8 ) ), 2475 | /* 3 */ static_cast< unsigned char >( ~( row_size >> 0 ) ), 2476 | /* 4 */ static_cast< unsigned char >( ~( row_size >> 8 ) ), 2477 | /* 5 */ 0, 2478 | }; 2479 | output.write( reinterpret_cast< char * >( prefix ), 6 ); 2480 | for ( int index = 0; index < 6; ++index ) 2481 | crc = table[ ( crc ^ prefix[ index ] ) & 0xff ] ^ ( crc >> 8 ); 2482 | output.write( reinterpret_cast< char const * >( image ), width * 4 ); 2483 | check_2 = ( check_2 + check_1 ) % 65521; 2484 | for ( int index = 0; index < width * 4; ++index ) 2485 | { 2486 | check_1 = ( check_1 + image[ index ] ) % 65521; 2487 | check_2 = ( check_2 + check_1 ) % 65521; 2488 | crc = table[ ( crc ^ image[ index ] ) & 0xff ] ^ ( crc >> 8 ); 2489 | } 2490 | } 2491 | unsigned char footer[] = { 2492 | /* 0 */ static_cast< unsigned char >( check_2 >> 8 ), 2493 | /* 1 */ static_cast< unsigned char >( check_2 >> 0 ), 2494 | /* 2 */ static_cast< unsigned char >( check_1 >> 8 ), 2495 | /* 3 */ static_cast< unsigned char >( check_1 >> 0 ), 2496 | /* 4 */ 0, 0, 0, 0, 2497 | /* 8 */ 0, 0, 0, 0, 73, 69, 78, 68, // IEND 2498 | /* 16 */ 174, 66, 96, 130, 2499 | }; 2500 | for ( int index = 0; index < 4; ++index ) 2501 | crc = table[ ( crc ^ footer[ index ] ) & 0xff ] ^ ( crc >> 8 ); 2502 | footer[ 4 ] = static_cast< unsigned char >( ~crc >> 24 ); 2503 | footer[ 5 ] = static_cast< unsigned char >( ~crc >> 16 ); 2504 | footer[ 6 ] = static_cast< unsigned char >( ~crc >> 8 ); 2505 | footer[ 7 ] = static_cast< unsigned char >( ~crc >> 0 ); 2506 | output.write( reinterpret_cast< char * >( footer ), 20 ); 2507 | } 2508 | 2509 | // Main test runner. This parses the command line options, runs the tests, 2510 | // and may verify their output, report times and results, and write the images 2511 | // the tests produce. 2512 | // 2513 | int main( 2514 | int argc, 2515 | char **argv ) 2516 | { 2517 | string subset; 2518 | bool plain = false; 2519 | bool table = false; 2520 | bool pngs = false; 2521 | string suffix; 2522 | bool fails = false; 2523 | int bench = 1; 2524 | for ( int index = 1; index < argc; ++index ) 2525 | { 2526 | string option( argv[ index ] ); 2527 | if ( option == "--subset" && index < argc - 1 ) 2528 | subset = argv[ ++index ]; 2529 | else if ( option == "--plain" ) 2530 | plain = true; 2531 | else if ( option == "--table" ) 2532 | table = true; 2533 | else if ( option == "--pngs" ) 2534 | pngs = true; 2535 | else if ( option == "--suffix" && index < argc - 1 ) 2536 | suffix = argv[ ++index ]; 2537 | else if ( option == "--fails" ) 2538 | fails = true; 2539 | else if ( option == "--bench" && index < argc - 1 ) 2540 | bench = max( atoi( argv[ ++index ] ), 1 ); 2541 | else 2542 | { 2543 | cout << 2544 | "Usage: " << argv[ 0 ] << " [options...]\n" 2545 | "Options:\n" 2546 | " --subset : Only run tests with names globbing \n" 2547 | " --plain : Do not colorize output or use term codes\n" 2548 | " --table : Regenerate the code for table of tests\n" 2549 | " --pngs : Write PNG images showing output of tests\n" 2550 | " --suffix : Append to the filenames of PNGs\n" 2551 | " --fails : Generate output only for failures\n" 2552 | " --bench : Run each test times, show fastest\n"; 2553 | return 1; 2554 | } 2555 | } 2556 | #ifdef _WIN32 2557 | HANDLE out = GetStdHandle( STD_OUTPUT_HANDLE ); 2558 | DWORD mode = 0; 2559 | if ( out == INVALID_HANDLE_VALUE || 2560 | !GetConsoleMode( out, &mode ) || 2561 | !SetConsoleMode( out, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING ) ) 2562 | plain = true; 2563 | #else 2564 | if ( !isatty( fileno( stdout ) ) ) 2565 | plain = true; 2566 | #endif 2567 | int count = sizeof( tests ) / sizeof( test ); 2568 | int total = count; 2569 | if ( !subset.empty() ) 2570 | { 2571 | total = 0; 2572 | for ( int index = 0; index < count; ++index ) 2573 | if ( glob_match( subset.c_str(), tests[ index ].name ) ) 2574 | ++total; 2575 | } 2576 | base64_decode( font_a_base64, font_a ); 2577 | base64_decode( font_b_base64, font_b ); 2578 | base64_decode( font_c_base64, font_c ); 2579 | base64_decode( font_d_base64, font_d ); 2580 | base64_decode( font_e_base64, font_e ); 2581 | base64_decode( font_f_base64, font_f ); 2582 | base64_decode( font_g_base64, font_g ); 2583 | int failed = 0; 2584 | int done = 0; 2585 | double geo = 0.0; 2586 | for ( int index = 0; index < count; ++index ) 2587 | { 2588 | test const &entry = tests[ index ]; 2589 | if ( !subset.empty() && !glob_match( subset.c_str(), entry.name ) ) 2590 | continue; 2591 | ++done; 2592 | if ( !fails && !table && !plain ) 2593 | cout << "\33[90m" << setw( 3 ) << done << "/" << total 2594 | << " \33[33m[RUN ] \33[0;90m???????? ?????.??ms\33[m " 2595 | << entry.name << endl; 2596 | int width = entry.width; 2597 | int height = entry.height; 2598 | unsigned char *image = new unsigned char[ 4 * width * height ]; 2599 | double best = 1.0e100; 2600 | for ( int run = 0; run < bench; ++run ) 2601 | { 2602 | canvas subject( width, height ); 2603 | double start = get_seconds(); 2604 | entry.call( subject, 2605 | static_cast< float >( width ), 2606 | static_cast< float >( height ) ); 2607 | double end = get_seconds(); 2608 | best = min( best, end - start ); 2609 | if ( run == 0 ) 2610 | subject.get_image_data( image, width, height, 2611 | 4 * width, 0, 0 ); 2612 | } 2613 | geo += log( best ); 2614 | unsigned hash = hash_image( image, width, height ); 2615 | int distance = 0; 2616 | for ( unsigned bits = hash ^ entry.hash; bits; bits &= bits - 1 ) 2617 | ++distance; 2618 | bool passed = distance <= 5; 2619 | if ( !passed ) 2620 | ++failed; 2621 | else if ( fails ) 2622 | { 2623 | delete[] image; 2624 | continue; 2625 | } 2626 | if ( table ) 2627 | cout << " { 0x" << hex << setfill( '0' ) << setw( 8 ) << hash 2628 | << ", " << dec << width << ", " << height 2629 | << ", " << entry.name << ", \"" << entry.name << "\" }," << endl; 2630 | else if ( plain ) 2631 | cout << setw( 3 ) << done << "/" << total << " [" 2632 | << ( passed ? "PASS" : "FAIL" ) << "] " 2633 | << hex << setfill( '0' ) << setw( 8 ) << hash << " " 2634 | << dec << setfill( ' ' ) << setw( 8 ) 2635 | << fixed << setprecision( 2 ) << ( best * 1000.0 ) 2636 | << "ms " << entry.name << endl; 2637 | else 2638 | cout << ( fails ? "" : "\33[A" ) << "\33[90m" 2639 | << setw( 3 ) << done << "/" << total << " \33[" 2640 | << ( passed ? "32m[PASS" : "31;1m[FAIL" ) << "]\33[0;90m " 2641 | << hex << setfill( '0' ) << setw( 8 ) << hash << " " 2642 | << dec << setfill( ' ' ) << setw( 8 ) 2643 | << fixed << setprecision( 2 ) << ( best * 1000.0 ) 2644 | << "ms\33[m " << entry.name << endl; 2645 | if ( pngs ) 2646 | write_png( string( entry.name ) + suffix + ".png", 2647 | image, width, height ); 2648 | delete[] image; 2649 | } 2650 | geo = done ? exp( geo / done ) : 0.0; 2651 | if ( !table && ( !fails || failed ) ) 2652 | cout << failed << " failed, " 2653 | << setprecision( 3 ) << fixed << ( geo * 1000.0 ) << "ms geo mean\n"; 2654 | return !!failed; 2655 | } 2656 | --------------------------------------------------------------------------------