├── .gitignore ├── .rustfmt.toml ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── Makefile ├── README.md ├── assets ├── fonts │ ├── jetbrains │ │ ├── JetBrainsMono-Bold.ttf │ │ └── JetBrainsMono-Regular.ttf │ ├── noto │ │ ├── LICENSE │ │ ├── NotoSans-Bold.ttf │ │ ├── NotoSans-BoldItalic.ttf │ │ ├── NotoSans-Italic.ttf │ │ ├── NotoSans-Regular.ttf │ │ ├── NotoSansArabic-Bold.ttf │ │ ├── NotoSansArabic-Regular.ttf │ │ ├── NotoSansDevanagari-Bold.ttf │ │ ├── NotoSansDevanagari-Regular.ttf │ │ ├── NotoSansSC-Bold.otf │ │ ├── NotoSansSC-Regular.otf │ │ ├── NotoSansThai-Bold.ttf │ │ └── NotoSansThai-Regular.ttf │ └── siliguri │ │ ├── HindSiliguri-Bold.ttf │ │ ├── HindSiliguri-Regular.ttf │ │ └── OFL.txt └── shaders │ ├── g_fragment.glsl │ └── g_vertex.glsl ├── examples ├── 0.json └── 1.json ├── resources └── provok.gif └── src ├── bitmaps ├── atlas.rs └── mod.rs ├── color.rs ├── font ├── ftwrap.rs ├── hbwrap.rs ├── loader │ ├── mod.rs │ └── parser.rs ├── mod.rs ├── rasterizer │ ├── freetype.rs │ └── mod.rs └── shaper │ ├── harfbuzz.rs │ └── mod.rs ├── glyph_atlas.rs ├── input.rs ├── language.rs ├── main.rs ├── render_state.rs └── utils.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | **/*.rs.bk 3 | .DS_Store -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | newline_style = "Unix" 2 | # The "Default" setting has a heuristic which splits lines too aggresively. 3 | use_small_heuristics = "Max" 4 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.14.1" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "a55f82cfe485775d02112886f4169bde0c5894d75e79ead7eafe7e40a25e45f7" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler" 16 | version = "1.0.2" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 19 | 20 | [[package]] 21 | name = "android_glue" 22 | version = "0.2.3" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "000444226fcff248f2bc4c7625be32c63caccfecc2723a2b9f78a7487a49c407" 25 | 26 | [[package]] 27 | name = "anyhow" 28 | version = "1.0.42" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "595d3cfa7a60d4555cb5067b99f07142a08ea778de5cf993f7b75c7d8fabc486" 31 | 32 | [[package]] 33 | name = "approx" 34 | version = "0.5.1" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" 37 | dependencies = [ 38 | "num-traits", 39 | ] 40 | 41 | [[package]] 42 | name = "atty" 43 | version = "0.2.14" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 46 | dependencies = [ 47 | "hermit-abi", 48 | "libc", 49 | "winapi", 50 | ] 51 | 52 | [[package]] 53 | name = "autocfg" 54 | version = "1.0.1" 55 | source = "registry+https://github.com/rust-lang/crates.io-index" 56 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 57 | 58 | [[package]] 59 | name = "backtrace" 60 | version = "0.3.56" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | checksum = "9d117600f438b1707d4e4ae15d3595657288f8235a0eb593e80ecc98ab34e1bc" 63 | dependencies = [ 64 | "addr2line", 65 | "cfg-if 1.0.0", 66 | "libc", 67 | "miniz_oxide", 68 | "object", 69 | "rustc-demangle", 70 | ] 71 | 72 | [[package]] 73 | name = "bitflags" 74 | version = "1.2.1" 75 | source = "registry+https://github.com/rust-lang/crates.io-index" 76 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 77 | 78 | [[package]] 79 | name = "block" 80 | version = "0.1.6" 81 | source = "registry+https://github.com/rust-lang/crates.io-index" 82 | checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" 83 | 84 | [[package]] 85 | name = "bumpalo" 86 | version = "3.9.1" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" 89 | 90 | [[package]] 91 | name = "calloop" 92 | version = "0.9.3" 93 | source = "registry+https://github.com/rust-lang/crates.io-index" 94 | checksum = "bf2eec61efe56aa1e813f5126959296933cf0700030e4314786c48779a66ab82" 95 | dependencies = [ 96 | "log", 97 | "nix", 98 | ] 99 | 100 | [[package]] 101 | name = "cc" 102 | version = "1.0.67" 103 | source = "registry+https://github.com/rust-lang/crates.io-index" 104 | checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd" 105 | 106 | [[package]] 107 | name = "cfg-if" 108 | version = "0.1.10" 109 | source = "registry+https://github.com/rust-lang/crates.io-index" 110 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 111 | 112 | [[package]] 113 | name = "cfg-if" 114 | version = "1.0.0" 115 | source = "registry+https://github.com/rust-lang/crates.io-index" 116 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 117 | 118 | [[package]] 119 | name = "cgl" 120 | version = "0.3.2" 121 | source = "registry+https://github.com/rust-lang/crates.io-index" 122 | checksum = "0ced0551234e87afee12411d535648dd89d2e7f34c78b753395567aff3d447ff" 123 | dependencies = [ 124 | "libc", 125 | ] 126 | 127 | [[package]] 128 | name = "clap" 129 | version = "3.1.8" 130 | source = "registry+https://github.com/rust-lang/crates.io-index" 131 | checksum = "71c47df61d9e16dc010b55dba1952a57d8c215dbb533fd13cdd13369aac73b1c" 132 | dependencies = [ 133 | "atty", 134 | "bitflags", 135 | "indexmap", 136 | "lazy_static", 137 | "os_str_bytes", 138 | "strsim", 139 | "termcolor", 140 | "terminal_size", 141 | "textwrap", 142 | ] 143 | 144 | [[package]] 145 | name = "cmake" 146 | version = "0.1.45" 147 | source = "registry+https://github.com/rust-lang/crates.io-index" 148 | checksum = "eb6210b637171dfba4cda12e579ac6dc73f5165ad56133e5d72ef3131f320855" 149 | dependencies = [ 150 | "cc", 151 | ] 152 | 153 | [[package]] 154 | name = "cocoa" 155 | version = "0.24.0" 156 | source = "registry+https://github.com/rust-lang/crates.io-index" 157 | checksum = "6f63902e9223530efb4e26ccd0cf55ec30d592d3b42e21a28defc42a9586e832" 158 | dependencies = [ 159 | "bitflags", 160 | "block", 161 | "cocoa-foundation", 162 | "core-foundation 0.9.1", 163 | "core-graphics 0.22.2", 164 | "foreign-types", 165 | "libc", 166 | "objc", 167 | ] 168 | 169 | [[package]] 170 | name = "cocoa-foundation" 171 | version = "0.1.0" 172 | source = "registry+https://github.com/rust-lang/crates.io-index" 173 | checksum = "7ade49b65d560ca58c403a479bb396592b155c0185eada742ee323d1d68d6318" 174 | dependencies = [ 175 | "bitflags", 176 | "block", 177 | "core-foundation 0.9.1", 178 | "core-graphics-types", 179 | "foreign-types", 180 | "libc", 181 | "objc", 182 | ] 183 | 184 | [[package]] 185 | name = "core-foundation" 186 | version = "0.7.0" 187 | source = "registry+https://github.com/rust-lang/crates.io-index" 188 | checksum = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171" 189 | dependencies = [ 190 | "core-foundation-sys 0.7.0", 191 | "libc", 192 | ] 193 | 194 | [[package]] 195 | name = "core-foundation" 196 | version = "0.9.1" 197 | source = "registry+https://github.com/rust-lang/crates.io-index" 198 | checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62" 199 | dependencies = [ 200 | "core-foundation-sys 0.8.2", 201 | "libc", 202 | ] 203 | 204 | [[package]] 205 | name = "core-foundation-sys" 206 | version = "0.7.0" 207 | source = "registry+https://github.com/rust-lang/crates.io-index" 208 | checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" 209 | 210 | [[package]] 211 | name = "core-foundation-sys" 212 | version = "0.8.2" 213 | source = "registry+https://github.com/rust-lang/crates.io-index" 214 | checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b" 215 | 216 | [[package]] 217 | name = "core-graphics" 218 | version = "0.19.2" 219 | source = "registry+https://github.com/rust-lang/crates.io-index" 220 | checksum = "b3889374e6ea6ab25dba90bb5d96202f61108058361f6dc72e8b03e6f8bbe923" 221 | dependencies = [ 222 | "bitflags", 223 | "core-foundation 0.7.0", 224 | "foreign-types", 225 | "libc", 226 | ] 227 | 228 | [[package]] 229 | name = "core-graphics" 230 | version = "0.22.2" 231 | source = "registry+https://github.com/rust-lang/crates.io-index" 232 | checksum = "269f35f69b542b80e736a20a89a05215c0ce80c2c03c514abb2e318b78379d86" 233 | dependencies = [ 234 | "bitflags", 235 | "core-foundation 0.9.1", 236 | "core-graphics-types", 237 | "foreign-types", 238 | "libc", 239 | ] 240 | 241 | [[package]] 242 | name = "core-graphics-types" 243 | version = "0.1.1" 244 | source = "registry+https://github.com/rust-lang/crates.io-index" 245 | checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b" 246 | dependencies = [ 247 | "bitflags", 248 | "core-foundation 0.9.1", 249 | "foreign-types", 250 | "libc", 251 | ] 252 | 253 | [[package]] 254 | name = "core-text" 255 | version = "19.2.0" 256 | source = "registry+https://github.com/rust-lang/crates.io-index" 257 | checksum = "99d74ada66e07c1cefa18f8abfba765b486f250de2e4a999e5727fc0dd4b4a25" 258 | dependencies = [ 259 | "core-foundation 0.9.1", 260 | "core-graphics 0.22.2", 261 | "foreign-types", 262 | "libc", 263 | ] 264 | 265 | [[package]] 266 | name = "core-video-sys" 267 | version = "0.1.4" 268 | source = "registry+https://github.com/rust-lang/crates.io-index" 269 | checksum = "34ecad23610ad9757664d644e369246edde1803fcb43ed72876565098a5d3828" 270 | dependencies = [ 271 | "cfg-if 0.1.10", 272 | "core-foundation-sys 0.7.0", 273 | "core-graphics 0.19.2", 274 | "libc", 275 | "objc", 276 | ] 277 | 278 | [[package]] 279 | name = "cty" 280 | version = "0.2.2" 281 | source = "registry+https://github.com/rust-lang/crates.io-index" 282 | checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" 283 | 284 | [[package]] 285 | name = "darling" 286 | version = "0.13.2" 287 | source = "registry+https://github.com/rust-lang/crates.io-index" 288 | checksum = "4e92cb285610dd935f60ee8b4d62dd1988bd12b7ea50579bd6a138201525318e" 289 | dependencies = [ 290 | "darling_core", 291 | "darling_macro", 292 | ] 293 | 294 | [[package]] 295 | name = "darling_core" 296 | version = "0.13.2" 297 | source = "registry+https://github.com/rust-lang/crates.io-index" 298 | checksum = "5c29e95ab498b18131ea460b2c0baa18cbf041231d122b0b7bfebef8c8e88989" 299 | dependencies = [ 300 | "fnv", 301 | "ident_case", 302 | "proc-macro2", 303 | "quote", 304 | "strsim", 305 | "syn", 306 | ] 307 | 308 | [[package]] 309 | name = "darling_macro" 310 | version = "0.13.2" 311 | source = "registry+https://github.com/rust-lang/crates.io-index" 312 | checksum = "b21dd6b221dd547528bd6fb15f1a3b7ab03b9a06f76bff288a8c629bcfbe7f0e" 313 | dependencies = [ 314 | "darling_core", 315 | "quote", 316 | "syn", 317 | ] 318 | 319 | [[package]] 320 | name = "derivative" 321 | version = "2.2.0" 322 | source = "registry+https://github.com/rust-lang/crates.io-index" 323 | checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" 324 | dependencies = [ 325 | "proc-macro2", 326 | "quote", 327 | "syn", 328 | ] 329 | 330 | [[package]] 331 | name = "dispatch" 332 | version = "0.2.0" 333 | source = "registry+https://github.com/rust-lang/crates.io-index" 334 | checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" 335 | 336 | [[package]] 337 | name = "dlib" 338 | version = "0.5.0" 339 | source = "registry+https://github.com/rust-lang/crates.io-index" 340 | checksum = "ac1b7517328c04c2aa68422fc60a41b92208182142ed04a25879c26c8f878794" 341 | dependencies = [ 342 | "libloading", 343 | ] 344 | 345 | [[package]] 346 | name = "downcast-rs" 347 | version = "1.2.0" 348 | source = "registry+https://github.com/rust-lang/crates.io-index" 349 | checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" 350 | 351 | [[package]] 352 | name = "euclid" 353 | version = "0.22.4" 354 | source = "registry+https://github.com/rust-lang/crates.io-index" 355 | checksum = "7b9b0b0868386d989090b2431c4c2a4d31228490a132ca5db3a36184331782d3" 356 | dependencies = [ 357 | "num-traits", 358 | ] 359 | 360 | [[package]] 361 | name = "find-crate" 362 | version = "0.6.3" 363 | source = "registry+https://github.com/rust-lang/crates.io-index" 364 | checksum = "59a98bbaacea1c0eb6a0876280051b892eb73594fd90cf3b20e9c817029c57d2" 365 | dependencies = [ 366 | "toml", 367 | ] 368 | 369 | [[package]] 370 | name = "fnv" 371 | version = "1.0.7" 372 | source = "registry+https://github.com/rust-lang/crates.io-index" 373 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 374 | 375 | [[package]] 376 | name = "foreign-types" 377 | version = "0.3.2" 378 | source = "registry+https://github.com/rust-lang/crates.io-index" 379 | checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 380 | dependencies = [ 381 | "foreign-types-shared", 382 | ] 383 | 384 | [[package]] 385 | name = "foreign-types-shared" 386 | version = "0.1.1" 387 | source = "registry+https://github.com/rust-lang/crates.io-index" 388 | checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 389 | 390 | [[package]] 391 | name = "freetype" 392 | version = "0.7.0" 393 | source = "registry+https://github.com/rust-lang/crates.io-index" 394 | checksum = "bee38378a9e3db1cc693b4f88d166ae375338a0ff75cb8263e1c601d51f35dc6" 395 | dependencies = [ 396 | "freetype-sys", 397 | "libc", 398 | ] 399 | 400 | [[package]] 401 | name = "freetype-sys" 402 | version = "0.13.1" 403 | source = "registry+https://github.com/rust-lang/crates.io-index" 404 | checksum = "a37d4011c0cc628dfa766fcc195454f4b068d7afdc2adfd28861191d866e731a" 405 | dependencies = [ 406 | "cmake", 407 | "libc", 408 | "pkg-config", 409 | ] 410 | 411 | [[package]] 412 | name = "getrandom" 413 | version = "0.2.6" 414 | source = "registry+https://github.com/rust-lang/crates.io-index" 415 | checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad" 416 | dependencies = [ 417 | "cfg-if 1.0.0", 418 | "libc", 419 | "wasi 0.10.2+wasi-snapshot-preview1", 420 | ] 421 | 422 | [[package]] 423 | name = "gimli" 424 | version = "0.23.0" 425 | source = "registry+https://github.com/rust-lang/crates.io-index" 426 | checksum = "f6503fe142514ca4799d4c26297c4248239fe8838d827db6bd6065c6ed29a6ce" 427 | 428 | [[package]] 429 | name = "gl_generator" 430 | version = "0.14.0" 431 | source = "registry+https://github.com/rust-lang/crates.io-index" 432 | checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d" 433 | dependencies = [ 434 | "khronos_api", 435 | "log", 436 | "xml-rs", 437 | ] 438 | 439 | [[package]] 440 | name = "glium" 441 | version = "0.31.0" 442 | source = "registry+https://github.com/rust-lang/crates.io-index" 443 | checksum = "0ab4f09b43d8ee427a700cb9ed3b20e0e858d62a509edded1a98ca5707d68e19" 444 | dependencies = [ 445 | "backtrace", 446 | "fnv", 447 | "gl_generator", 448 | "glutin", 449 | "lazy_static", 450 | "memoffset", 451 | "smallvec", 452 | "takeable-option", 453 | ] 454 | 455 | [[package]] 456 | name = "glutin" 457 | version = "0.28.0" 458 | source = "registry+https://github.com/rust-lang/crates.io-index" 459 | checksum = "00ea9dbe544bc8a657c4c4a798c2d16cd01b549820e47657297549d28371f6d2" 460 | dependencies = [ 461 | "android_glue", 462 | "cgl", 463 | "cocoa", 464 | "core-foundation 0.9.1", 465 | "glutin_egl_sys", 466 | "glutin_emscripten_sys", 467 | "glutin_gles2_sys", 468 | "glutin_glx_sys", 469 | "glutin_wgl_sys", 470 | "lazy_static", 471 | "libloading", 472 | "log", 473 | "objc", 474 | "osmesa-sys", 475 | "parking_lot", 476 | "wayland-client", 477 | "wayland-egl", 478 | "winapi", 479 | "winit", 480 | ] 481 | 482 | [[package]] 483 | name = "glutin_egl_sys" 484 | version = "0.1.5" 485 | source = "registry+https://github.com/rust-lang/crates.io-index" 486 | checksum = "2abb6aa55523480c4adc5a56bbaa249992e2dddb2fc63dc96e04a3355364c211" 487 | dependencies = [ 488 | "gl_generator", 489 | "winapi", 490 | ] 491 | 492 | [[package]] 493 | name = "glutin_emscripten_sys" 494 | version = "0.1.1" 495 | source = "registry+https://github.com/rust-lang/crates.io-index" 496 | checksum = "80de4146df76e8a6c32b03007bc764ff3249dcaeb4f675d68a06caf1bac363f1" 497 | 498 | [[package]] 499 | name = "glutin_gles2_sys" 500 | version = "0.1.5" 501 | source = "registry+https://github.com/rust-lang/crates.io-index" 502 | checksum = "e8094e708b730a7c8a1954f4f8a31880af00eb8a1c5b5bf85d28a0a3c6d69103" 503 | dependencies = [ 504 | "gl_generator", 505 | "objc", 506 | ] 507 | 508 | [[package]] 509 | name = "glutin_glx_sys" 510 | version = "0.1.7" 511 | source = "registry+https://github.com/rust-lang/crates.io-index" 512 | checksum = "7e393c8fc02b807459410429150e9c4faffdb312d59b8c038566173c81991351" 513 | dependencies = [ 514 | "gl_generator", 515 | "x11-dl", 516 | ] 517 | 518 | [[package]] 519 | name = "glutin_wgl_sys" 520 | version = "0.1.5" 521 | source = "registry+https://github.com/rust-lang/crates.io-index" 522 | checksum = "3da5951a1569dbab865c6f2a863efafff193a93caf05538d193e9e3816d21696" 523 | dependencies = [ 524 | "gl_generator", 525 | ] 526 | 527 | [[package]] 528 | name = "harfbuzz-sys" 529 | version = "0.5.0" 530 | source = "registry+https://github.com/rust-lang/crates.io-index" 531 | checksum = "bf8c27ca13930dc4ffe474880040fe9e0f03c2121600dc9c95423624cab3e467" 532 | dependencies = [ 533 | "cc", 534 | "core-graphics 0.22.2", 535 | "core-text", 536 | "foreign-types", 537 | "freetype", 538 | "pkg-config", 539 | ] 540 | 541 | [[package]] 542 | name = "hashbrown" 543 | version = "0.11.2" 544 | source = "registry+https://github.com/rust-lang/crates.io-index" 545 | checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" 546 | 547 | [[package]] 548 | name = "hermit-abi" 549 | version = "0.1.18" 550 | source = "registry+https://github.com/rust-lang/crates.io-index" 551 | checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" 552 | dependencies = [ 553 | "libc", 554 | ] 555 | 556 | [[package]] 557 | name = "ident_case" 558 | version = "1.0.1" 559 | source = "registry+https://github.com/rust-lang/crates.io-index" 560 | checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" 561 | 562 | [[package]] 563 | name = "indexmap" 564 | version = "1.8.1" 565 | source = "registry+https://github.com/rust-lang/crates.io-index" 566 | checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" 567 | dependencies = [ 568 | "autocfg", 569 | "hashbrown", 570 | ] 571 | 572 | [[package]] 573 | name = "instant" 574 | version = "0.1.9" 575 | source = "registry+https://github.com/rust-lang/crates.io-index" 576 | checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" 577 | dependencies = [ 578 | "cfg-if 1.0.0", 579 | "js-sys", 580 | "wasm-bindgen", 581 | "web-sys", 582 | ] 583 | 584 | [[package]] 585 | name = "itoa" 586 | version = "0.4.7" 587 | source = "registry+https://github.com/rust-lang/crates.io-index" 588 | checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" 589 | 590 | [[package]] 591 | name = "jni-sys" 592 | version = "0.3.0" 593 | source = "registry+https://github.com/rust-lang/crates.io-index" 594 | checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" 595 | 596 | [[package]] 597 | name = "js-sys" 598 | version = "0.3.56" 599 | source = "registry+https://github.com/rust-lang/crates.io-index" 600 | checksum = "a38fc24e30fd564ce974c02bf1d337caddff65be6cc4735a1f7eab22a7440f04" 601 | dependencies = [ 602 | "wasm-bindgen", 603 | ] 604 | 605 | [[package]] 606 | name = "khronos_api" 607 | version = "3.1.0" 608 | source = "registry+https://github.com/rust-lang/crates.io-index" 609 | checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" 610 | 611 | [[package]] 612 | name = "lazy_static" 613 | version = "1.4.0" 614 | source = "registry+https://github.com/rust-lang/crates.io-index" 615 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 616 | 617 | [[package]] 618 | name = "libc" 619 | version = "0.2.121" 620 | source = "registry+https://github.com/rust-lang/crates.io-index" 621 | checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f" 622 | 623 | [[package]] 624 | name = "libloading" 625 | version = "0.7.0" 626 | source = "registry+https://github.com/rust-lang/crates.io-index" 627 | checksum = "6f84d96438c15fcd6c3f244c8fce01d1e2b9c6b5623e9c711dc9286d8fc92d6a" 628 | dependencies = [ 629 | "cfg-if 1.0.0", 630 | "winapi", 631 | ] 632 | 633 | [[package]] 634 | name = "lock_api" 635 | version = "0.4.2" 636 | source = "registry+https://github.com/rust-lang/crates.io-index" 637 | checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312" 638 | dependencies = [ 639 | "scopeguard", 640 | ] 641 | 642 | [[package]] 643 | name = "log" 644 | version = "0.4.14" 645 | source = "registry+https://github.com/rust-lang/crates.io-index" 646 | checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" 647 | dependencies = [ 648 | "cfg-if 1.0.0", 649 | ] 650 | 651 | [[package]] 652 | name = "malloc_buf" 653 | version = "0.0.6" 654 | source = "registry+https://github.com/rust-lang/crates.io-index" 655 | checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" 656 | dependencies = [ 657 | "libc", 658 | ] 659 | 660 | [[package]] 661 | name = "maybe-uninit" 662 | version = "2.0.0" 663 | source = "registry+https://github.com/rust-lang/crates.io-index" 664 | checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" 665 | 666 | [[package]] 667 | name = "memchr" 668 | version = "2.4.1" 669 | source = "registry+https://github.com/rust-lang/crates.io-index" 670 | checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" 671 | 672 | [[package]] 673 | name = "memmap2" 674 | version = "0.3.1" 675 | source = "registry+https://github.com/rust-lang/crates.io-index" 676 | checksum = "00b6c2ebff6180198788f5db08d7ce3bc1d0b617176678831a7510825973e357" 677 | dependencies = [ 678 | "libc", 679 | ] 680 | 681 | [[package]] 682 | name = "memoffset" 683 | version = "0.6.5" 684 | source = "registry+https://github.com/rust-lang/crates.io-index" 685 | checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" 686 | dependencies = [ 687 | "autocfg", 688 | ] 689 | 690 | [[package]] 691 | name = "miniz_oxide" 692 | version = "0.4.4" 693 | source = "registry+https://github.com/rust-lang/crates.io-index" 694 | checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" 695 | dependencies = [ 696 | "adler", 697 | "autocfg", 698 | ] 699 | 700 | [[package]] 701 | name = "mio" 702 | version = "0.8.2" 703 | source = "registry+https://github.com/rust-lang/crates.io-index" 704 | checksum = "52da4364ffb0e4fe33a9841a98a3f3014fb964045ce4f7a45a398243c8d6b0c9" 705 | dependencies = [ 706 | "libc", 707 | "log", 708 | "miow", 709 | "ntapi", 710 | "wasi 0.11.0+wasi-snapshot-preview1", 711 | "winapi", 712 | ] 713 | 714 | [[package]] 715 | name = "miow" 716 | version = "0.3.7" 717 | source = "registry+https://github.com/rust-lang/crates.io-index" 718 | checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" 719 | dependencies = [ 720 | "winapi", 721 | ] 722 | 723 | [[package]] 724 | name = "ndk" 725 | version = "0.5.0" 726 | source = "registry+https://github.com/rust-lang/crates.io-index" 727 | checksum = "96d868f654c72e75f8687572699cdabe755f03effbb62542768e995d5b8d699d" 728 | dependencies = [ 729 | "bitflags", 730 | "jni-sys", 731 | "ndk-sys", 732 | "num_enum", 733 | "thiserror", 734 | ] 735 | 736 | [[package]] 737 | name = "ndk-context" 738 | version = "0.1.0" 739 | source = "registry+https://github.com/rust-lang/crates.io-index" 740 | checksum = "4e3c5cc68637e21fe8f077f6a1c9e0b9ca495bb74895226b476310f613325884" 741 | 742 | [[package]] 743 | name = "ndk-glue" 744 | version = "0.5.1" 745 | source = "registry+https://github.com/rust-lang/crates.io-index" 746 | checksum = "a1c68f70683c5fc9a747a383744206cd371741b2f0b31781ab6770487ec572e2" 747 | dependencies = [ 748 | "lazy_static", 749 | "libc", 750 | "log", 751 | "ndk", 752 | "ndk-context", 753 | "ndk-macro", 754 | "ndk-sys", 755 | ] 756 | 757 | [[package]] 758 | name = "ndk-macro" 759 | version = "0.3.0" 760 | source = "registry+https://github.com/rust-lang/crates.io-index" 761 | checksum = "0df7ac00c4672f9d5aece54ee3347520b7e20f158656c7db2e6de01902eb7a6c" 762 | dependencies = [ 763 | "darling", 764 | "proc-macro-crate 1.1.3", 765 | "proc-macro2", 766 | "quote", 767 | "syn", 768 | ] 769 | 770 | [[package]] 771 | name = "ndk-sys" 772 | version = "0.2.2" 773 | source = "registry+https://github.com/rust-lang/crates.io-index" 774 | checksum = "e1bcdd74c20ad5d95aacd60ef9ba40fdf77f767051040541df557b7a9b2a2121" 775 | 776 | [[package]] 777 | name = "nix" 778 | version = "0.22.3" 779 | source = "registry+https://github.com/rust-lang/crates.io-index" 780 | checksum = "e4916f159ed8e5de0082076562152a76b7a1f64a01fd9d1e0fea002c37624faf" 781 | dependencies = [ 782 | "bitflags", 783 | "cc", 784 | "cfg-if 1.0.0", 785 | "libc", 786 | "memoffset", 787 | ] 788 | 789 | [[package]] 790 | name = "nom" 791 | version = "6.1.2" 792 | source = "registry+https://github.com/rust-lang/crates.io-index" 793 | checksum = "e7413f999671bd4745a7b624bd370a569fb6bc574b23c83a3c5ed2e453f3d5e2" 794 | dependencies = [ 795 | "memchr", 796 | "version_check", 797 | ] 798 | 799 | [[package]] 800 | name = "ntapi" 801 | version = "0.3.6" 802 | source = "registry+https://github.com/rust-lang/crates.io-index" 803 | checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" 804 | dependencies = [ 805 | "winapi", 806 | ] 807 | 808 | [[package]] 809 | name = "num-traits" 810 | version = "0.2.14" 811 | source = "registry+https://github.com/rust-lang/crates.io-index" 812 | checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" 813 | dependencies = [ 814 | "autocfg", 815 | ] 816 | 817 | [[package]] 818 | name = "num_enum" 819 | version = "0.5.1" 820 | source = "registry+https://github.com/rust-lang/crates.io-index" 821 | checksum = "226b45a5c2ac4dd696ed30fa6b94b057ad909c7b7fc2e0d0808192bced894066" 822 | dependencies = [ 823 | "derivative", 824 | "num_enum_derive", 825 | ] 826 | 827 | [[package]] 828 | name = "num_enum_derive" 829 | version = "0.5.1" 830 | source = "registry+https://github.com/rust-lang/crates.io-index" 831 | checksum = "1c0fd9eba1d5db0994a239e09c1be402d35622277e35468ba891aa5e3188ce7e" 832 | dependencies = [ 833 | "proc-macro-crate 0.1.5", 834 | "proc-macro2", 835 | "quote", 836 | "syn", 837 | ] 838 | 839 | [[package]] 840 | name = "objc" 841 | version = "0.2.7" 842 | source = "registry+https://github.com/rust-lang/crates.io-index" 843 | checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" 844 | dependencies = [ 845 | "malloc_buf", 846 | ] 847 | 848 | [[package]] 849 | name = "object" 850 | version = "0.23.0" 851 | source = "registry+https://github.com/rust-lang/crates.io-index" 852 | checksum = "a9a7ab5d64814df0fe4a4b5ead45ed6c5f181ee3ff04ba344313a6c80446c5d4" 853 | 854 | [[package]] 855 | name = "once_cell" 856 | version = "1.7.2" 857 | source = "registry+https://github.com/rust-lang/crates.io-index" 858 | checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" 859 | 860 | [[package]] 861 | name = "os_str_bytes" 862 | version = "6.0.0" 863 | source = "registry+https://github.com/rust-lang/crates.io-index" 864 | checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64" 865 | dependencies = [ 866 | "memchr", 867 | ] 868 | 869 | [[package]] 870 | name = "osmesa-sys" 871 | version = "0.1.2" 872 | source = "registry+https://github.com/rust-lang/crates.io-index" 873 | checksum = "88cfece6e95d2e717e0872a7f53a8684712ad13822a7979bc760b9c77ec0013b" 874 | dependencies = [ 875 | "shared_library", 876 | ] 877 | 878 | [[package]] 879 | name = "palette" 880 | version = "0.6.0" 881 | source = "registry+https://github.com/rust-lang/crates.io-index" 882 | checksum = "f9735f7e1e51a3f740bacd5dc2724b61a7806f23597a8736e679f38ee3435d18" 883 | dependencies = [ 884 | "approx", 885 | "num-traits", 886 | "palette_derive", 887 | "phf", 888 | ] 889 | 890 | [[package]] 891 | name = "palette_derive" 892 | version = "0.6.0" 893 | source = "registry+https://github.com/rust-lang/crates.io-index" 894 | checksum = "7799c3053ea8a6d8a1193c7ba42f534e7863cf52e378a7f90406f4a645d33bad" 895 | dependencies = [ 896 | "find-crate", 897 | "proc-macro2", 898 | "quote", 899 | "syn", 900 | ] 901 | 902 | [[package]] 903 | name = "parking_lot" 904 | version = "0.11.1" 905 | source = "registry+https://github.com/rust-lang/crates.io-index" 906 | checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" 907 | dependencies = [ 908 | "instant", 909 | "lock_api", 910 | "parking_lot_core", 911 | ] 912 | 913 | [[package]] 914 | name = "parking_lot_core" 915 | version = "0.8.3" 916 | source = "registry+https://github.com/rust-lang/crates.io-index" 917 | checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018" 918 | dependencies = [ 919 | "cfg-if 1.0.0", 920 | "instant", 921 | "libc", 922 | "redox_syscall", 923 | "smallvec", 924 | "winapi", 925 | ] 926 | 927 | [[package]] 928 | name = "percent-encoding" 929 | version = "2.1.0" 930 | source = "registry+https://github.com/rust-lang/crates.io-index" 931 | checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" 932 | 933 | [[package]] 934 | name = "phf" 935 | version = "0.9.0" 936 | source = "registry+https://github.com/rust-lang/crates.io-index" 937 | checksum = "b2ac8b67553a7ca9457ce0e526948cad581819238f4a9d1ea74545851fa24f37" 938 | dependencies = [ 939 | "phf_macros", 940 | "phf_shared", 941 | "proc-macro-hack", 942 | ] 943 | 944 | [[package]] 945 | name = "phf_generator" 946 | version = "0.9.1" 947 | source = "registry+https://github.com/rust-lang/crates.io-index" 948 | checksum = "d43f3220d96e0080cc9ea234978ccd80d904eafb17be31bb0f76daaea6493082" 949 | dependencies = [ 950 | "phf_shared", 951 | "rand", 952 | ] 953 | 954 | [[package]] 955 | name = "phf_macros" 956 | version = "0.9.0" 957 | source = "registry+https://github.com/rust-lang/crates.io-index" 958 | checksum = "b706f5936eb50ed880ae3009395b43ed19db5bff2ebd459c95e7bf013a89ab86" 959 | dependencies = [ 960 | "phf_generator", 961 | "phf_shared", 962 | "proc-macro-hack", 963 | "proc-macro2", 964 | "quote", 965 | "syn", 966 | ] 967 | 968 | [[package]] 969 | name = "phf_shared" 970 | version = "0.9.0" 971 | source = "registry+https://github.com/rust-lang/crates.io-index" 972 | checksum = "a68318426de33640f02be62b4ae8eb1261be2efbc337b60c54d845bf4484e0d9" 973 | dependencies = [ 974 | "siphasher", 975 | ] 976 | 977 | [[package]] 978 | name = "pkg-config" 979 | version = "0.3.19" 980 | source = "registry+https://github.com/rust-lang/crates.io-index" 981 | checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" 982 | 983 | [[package]] 984 | name = "ppv-lite86" 985 | version = "0.2.10" 986 | source = "registry+https://github.com/rust-lang/crates.io-index" 987 | checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" 988 | 989 | [[package]] 990 | name = "proc-macro-crate" 991 | version = "0.1.5" 992 | source = "registry+https://github.com/rust-lang/crates.io-index" 993 | checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" 994 | dependencies = [ 995 | "toml", 996 | ] 997 | 998 | [[package]] 999 | name = "proc-macro-crate" 1000 | version = "1.1.3" 1001 | source = "registry+https://github.com/rust-lang/crates.io-index" 1002 | checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a" 1003 | dependencies = [ 1004 | "thiserror", 1005 | "toml", 1006 | ] 1007 | 1008 | [[package]] 1009 | name = "proc-macro-hack" 1010 | version = "0.5.19" 1011 | source = "registry+https://github.com/rust-lang/crates.io-index" 1012 | checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" 1013 | 1014 | [[package]] 1015 | name = "proc-macro2" 1016 | version = "1.0.36" 1017 | source = "registry+https://github.com/rust-lang/crates.io-index" 1018 | checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" 1019 | dependencies = [ 1020 | "unicode-xid", 1021 | ] 1022 | 1023 | [[package]] 1024 | name = "provok" 1025 | version = "0.1.0" 1026 | dependencies = [ 1027 | "anyhow", 1028 | "clap", 1029 | "euclid", 1030 | "freetype", 1031 | "glium", 1032 | "harfbuzz-sys", 1033 | "libc", 1034 | "log", 1035 | "palette", 1036 | "serde", 1037 | "serde_json", 1038 | "thiserror", 1039 | "ttf-parser", 1040 | ] 1041 | 1042 | [[package]] 1043 | name = "quote" 1044 | version = "1.0.9" 1045 | source = "registry+https://github.com/rust-lang/crates.io-index" 1046 | checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" 1047 | dependencies = [ 1048 | "proc-macro2", 1049 | ] 1050 | 1051 | [[package]] 1052 | name = "rand" 1053 | version = "0.8.5" 1054 | source = "registry+https://github.com/rust-lang/crates.io-index" 1055 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 1056 | dependencies = [ 1057 | "libc", 1058 | "rand_chacha", 1059 | "rand_core", 1060 | ] 1061 | 1062 | [[package]] 1063 | name = "rand_chacha" 1064 | version = "0.3.1" 1065 | source = "registry+https://github.com/rust-lang/crates.io-index" 1066 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 1067 | dependencies = [ 1068 | "ppv-lite86", 1069 | "rand_core", 1070 | ] 1071 | 1072 | [[package]] 1073 | name = "rand_core" 1074 | version = "0.6.3" 1075 | source = "registry+https://github.com/rust-lang/crates.io-index" 1076 | checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" 1077 | dependencies = [ 1078 | "getrandom", 1079 | ] 1080 | 1081 | [[package]] 1082 | name = "raw-window-handle" 1083 | version = "0.4.3" 1084 | source = "registry+https://github.com/rust-lang/crates.io-index" 1085 | checksum = "b800beb9b6e7d2df1fe337c9e3d04e3af22a124460fb4c30fcc22c9117cefb41" 1086 | dependencies = [ 1087 | "cty", 1088 | ] 1089 | 1090 | [[package]] 1091 | name = "redox_syscall" 1092 | version = "0.2.5" 1093 | source = "registry+https://github.com/rust-lang/crates.io-index" 1094 | checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9" 1095 | dependencies = [ 1096 | "bitflags", 1097 | ] 1098 | 1099 | [[package]] 1100 | name = "rustc-demangle" 1101 | version = "0.1.18" 1102 | source = "registry+https://github.com/rust-lang/crates.io-index" 1103 | checksum = "6e3bad0ee36814ca07d7968269dd4b7ec89ec2da10c4bb613928d3077083c232" 1104 | 1105 | [[package]] 1106 | name = "ryu" 1107 | version = "1.0.5" 1108 | source = "registry+https://github.com/rust-lang/crates.io-index" 1109 | checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" 1110 | 1111 | [[package]] 1112 | name = "scoped-tls" 1113 | version = "1.0.0" 1114 | source = "registry+https://github.com/rust-lang/crates.io-index" 1115 | checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" 1116 | 1117 | [[package]] 1118 | name = "scopeguard" 1119 | version = "1.1.0" 1120 | source = "registry+https://github.com/rust-lang/crates.io-index" 1121 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 1122 | 1123 | [[package]] 1124 | name = "serde" 1125 | version = "1.0.126" 1126 | source = "registry+https://github.com/rust-lang/crates.io-index" 1127 | checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03" 1128 | dependencies = [ 1129 | "serde_derive", 1130 | ] 1131 | 1132 | [[package]] 1133 | name = "serde_derive" 1134 | version = "1.0.126" 1135 | source = "registry+https://github.com/rust-lang/crates.io-index" 1136 | checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43" 1137 | dependencies = [ 1138 | "proc-macro2", 1139 | "quote", 1140 | "syn", 1141 | ] 1142 | 1143 | [[package]] 1144 | name = "serde_json" 1145 | version = "1.0.64" 1146 | source = "registry+https://github.com/rust-lang/crates.io-index" 1147 | checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79" 1148 | dependencies = [ 1149 | "itoa", 1150 | "ryu", 1151 | "serde", 1152 | ] 1153 | 1154 | [[package]] 1155 | name = "shared_library" 1156 | version = "0.1.9" 1157 | source = "registry+https://github.com/rust-lang/crates.io-index" 1158 | checksum = "5a9e7e0f2bfae24d8a5b5a66c5b257a83c7412304311512a0c054cd5e619da11" 1159 | dependencies = [ 1160 | "lazy_static", 1161 | "libc", 1162 | ] 1163 | 1164 | [[package]] 1165 | name = "siphasher" 1166 | version = "0.3.5" 1167 | source = "registry+https://github.com/rust-lang/crates.io-index" 1168 | checksum = "cbce6d4507c7e4a3962091436e56e95290cb71fa302d0d270e32130b75fbff27" 1169 | 1170 | [[package]] 1171 | name = "smallvec" 1172 | version = "1.6.1" 1173 | source = "registry+https://github.com/rust-lang/crates.io-index" 1174 | checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" 1175 | 1176 | [[package]] 1177 | name = "smithay-client-toolkit" 1178 | version = "0.15.3" 1179 | source = "registry+https://github.com/rust-lang/crates.io-index" 1180 | checksum = "1325f292209cee78d5035530932422a30aa4c8fda1a16593ac083c1de211e68a" 1181 | dependencies = [ 1182 | "bitflags", 1183 | "calloop", 1184 | "dlib", 1185 | "lazy_static", 1186 | "log", 1187 | "memmap2", 1188 | "nix", 1189 | "pkg-config", 1190 | "wayland-client", 1191 | "wayland-cursor", 1192 | "wayland-protocols", 1193 | ] 1194 | 1195 | [[package]] 1196 | name = "strsim" 1197 | version = "0.10.0" 1198 | source = "registry+https://github.com/rust-lang/crates.io-index" 1199 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 1200 | 1201 | [[package]] 1202 | name = "syn" 1203 | version = "1.0.90" 1204 | source = "registry+https://github.com/rust-lang/crates.io-index" 1205 | checksum = "704df27628939572cd88d33f171cd6f896f4eaca85252c6e0a72d8d8287ee86f" 1206 | dependencies = [ 1207 | "proc-macro2", 1208 | "quote", 1209 | "unicode-xid", 1210 | ] 1211 | 1212 | [[package]] 1213 | name = "takeable-option" 1214 | version = "0.5.0" 1215 | source = "registry+https://github.com/rust-lang/crates.io-index" 1216 | checksum = "36ae8932fcfea38b7d3883ae2ab357b0d57a02caaa18ebb4f5ece08beaec4aa0" 1217 | 1218 | [[package]] 1219 | name = "termcolor" 1220 | version = "1.1.3" 1221 | source = "registry+https://github.com/rust-lang/crates.io-index" 1222 | checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" 1223 | dependencies = [ 1224 | "winapi-util", 1225 | ] 1226 | 1227 | [[package]] 1228 | name = "terminal_size" 1229 | version = "0.1.17" 1230 | source = "registry+https://github.com/rust-lang/crates.io-index" 1231 | checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df" 1232 | dependencies = [ 1233 | "libc", 1234 | "winapi", 1235 | ] 1236 | 1237 | [[package]] 1238 | name = "textwrap" 1239 | version = "0.15.0" 1240 | source = "registry+https://github.com/rust-lang/crates.io-index" 1241 | checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" 1242 | dependencies = [ 1243 | "terminal_size", 1244 | ] 1245 | 1246 | [[package]] 1247 | name = "thiserror" 1248 | version = "1.0.24" 1249 | source = "registry+https://github.com/rust-lang/crates.io-index" 1250 | checksum = "e0f4a65597094d4483ddaed134f409b2cb7c1beccf25201a9f73c719254fa98e" 1251 | dependencies = [ 1252 | "thiserror-impl", 1253 | ] 1254 | 1255 | [[package]] 1256 | name = "thiserror-impl" 1257 | version = "1.0.24" 1258 | source = "registry+https://github.com/rust-lang/crates.io-index" 1259 | checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0" 1260 | dependencies = [ 1261 | "proc-macro2", 1262 | "quote", 1263 | "syn", 1264 | ] 1265 | 1266 | [[package]] 1267 | name = "toml" 1268 | version = "0.5.8" 1269 | source = "registry+https://github.com/rust-lang/crates.io-index" 1270 | checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" 1271 | dependencies = [ 1272 | "serde", 1273 | ] 1274 | 1275 | [[package]] 1276 | name = "ttf-parser" 1277 | version = "0.15.0" 1278 | source = "registry+https://github.com/rust-lang/crates.io-index" 1279 | checksum = "c74c96594835e10fa545e2a51e8709f30b173a092bfd6036ef2cec53376244f3" 1280 | 1281 | [[package]] 1282 | name = "unicode-xid" 1283 | version = "0.2.1" 1284 | source = "registry+https://github.com/rust-lang/crates.io-index" 1285 | checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" 1286 | 1287 | [[package]] 1288 | name = "version_check" 1289 | version = "0.9.3" 1290 | source = "registry+https://github.com/rust-lang/crates.io-index" 1291 | checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" 1292 | 1293 | [[package]] 1294 | name = "wasi" 1295 | version = "0.10.2+wasi-snapshot-preview1" 1296 | source = "registry+https://github.com/rust-lang/crates.io-index" 1297 | checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" 1298 | 1299 | [[package]] 1300 | name = "wasi" 1301 | version = "0.11.0+wasi-snapshot-preview1" 1302 | source = "registry+https://github.com/rust-lang/crates.io-index" 1303 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1304 | 1305 | [[package]] 1306 | name = "wasm-bindgen" 1307 | version = "0.2.79" 1308 | source = "registry+https://github.com/rust-lang/crates.io-index" 1309 | checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06" 1310 | dependencies = [ 1311 | "cfg-if 1.0.0", 1312 | "wasm-bindgen-macro", 1313 | ] 1314 | 1315 | [[package]] 1316 | name = "wasm-bindgen-backend" 1317 | version = "0.2.79" 1318 | source = "registry+https://github.com/rust-lang/crates.io-index" 1319 | checksum = "8b21c0df030f5a177f3cba22e9bc4322695ec43e7257d865302900290bcdedca" 1320 | dependencies = [ 1321 | "bumpalo", 1322 | "lazy_static", 1323 | "log", 1324 | "proc-macro2", 1325 | "quote", 1326 | "syn", 1327 | "wasm-bindgen-shared", 1328 | ] 1329 | 1330 | [[package]] 1331 | name = "wasm-bindgen-macro" 1332 | version = "0.2.79" 1333 | source = "registry+https://github.com/rust-lang/crates.io-index" 1334 | checksum = "2f4203d69e40a52ee523b2529a773d5ffc1dc0071801c87b3d270b471b80ed01" 1335 | dependencies = [ 1336 | "quote", 1337 | "wasm-bindgen-macro-support", 1338 | ] 1339 | 1340 | [[package]] 1341 | name = "wasm-bindgen-macro-support" 1342 | version = "0.2.79" 1343 | source = "registry+https://github.com/rust-lang/crates.io-index" 1344 | checksum = "bfa8a30d46208db204854cadbb5d4baf5fcf8071ba5bf48190c3e59937962ebc" 1345 | dependencies = [ 1346 | "proc-macro2", 1347 | "quote", 1348 | "syn", 1349 | "wasm-bindgen-backend", 1350 | "wasm-bindgen-shared", 1351 | ] 1352 | 1353 | [[package]] 1354 | name = "wasm-bindgen-shared" 1355 | version = "0.2.79" 1356 | source = "registry+https://github.com/rust-lang/crates.io-index" 1357 | checksum = "3d958d035c4438e28c70e4321a2911302f10135ce78a9c7834c0cab4123d06a2" 1358 | 1359 | [[package]] 1360 | name = "wayland-client" 1361 | version = "0.29.4" 1362 | source = "registry+https://github.com/rust-lang/crates.io-index" 1363 | checksum = "91223460e73257f697d9e23d401279123d36039a3f7a449e983f123292d4458f" 1364 | dependencies = [ 1365 | "bitflags", 1366 | "downcast-rs", 1367 | "libc", 1368 | "nix", 1369 | "scoped-tls", 1370 | "wayland-commons", 1371 | "wayland-scanner", 1372 | "wayland-sys", 1373 | ] 1374 | 1375 | [[package]] 1376 | name = "wayland-commons" 1377 | version = "0.29.4" 1378 | source = "registry+https://github.com/rust-lang/crates.io-index" 1379 | checksum = "94f6e5e340d7c13490eca867898c4cec5af56c27a5ffe5c80c6fc4708e22d33e" 1380 | dependencies = [ 1381 | "nix", 1382 | "once_cell", 1383 | "smallvec", 1384 | "wayland-sys", 1385 | ] 1386 | 1387 | [[package]] 1388 | name = "wayland-cursor" 1389 | version = "0.29.4" 1390 | source = "registry+https://github.com/rust-lang/crates.io-index" 1391 | checksum = "c52758f13d5e7861fc83d942d3d99bf270c83269575e52ac29e5b73cb956a6bd" 1392 | dependencies = [ 1393 | "nix", 1394 | "wayland-client", 1395 | "xcursor", 1396 | ] 1397 | 1398 | [[package]] 1399 | name = "wayland-egl" 1400 | version = "0.29.4" 1401 | source = "registry+https://github.com/rust-lang/crates.io-index" 1402 | checksum = "83281d69ee162b59031c666385e93bde4039ec553b90c4191cdb128ceea29a3a" 1403 | dependencies = [ 1404 | "wayland-client", 1405 | "wayland-sys", 1406 | ] 1407 | 1408 | [[package]] 1409 | name = "wayland-protocols" 1410 | version = "0.29.4" 1411 | source = "registry+https://github.com/rust-lang/crates.io-index" 1412 | checksum = "60147ae23303402e41fe034f74fb2c35ad0780ee88a1c40ac09a3be1e7465741" 1413 | dependencies = [ 1414 | "bitflags", 1415 | "wayland-client", 1416 | "wayland-commons", 1417 | "wayland-scanner", 1418 | ] 1419 | 1420 | [[package]] 1421 | name = "wayland-scanner" 1422 | version = "0.29.4" 1423 | source = "registry+https://github.com/rust-lang/crates.io-index" 1424 | checksum = "39a1ed3143f7a143187156a2ab52742e89dac33245ba505c17224df48939f9e0" 1425 | dependencies = [ 1426 | "proc-macro2", 1427 | "quote", 1428 | "xml-rs", 1429 | ] 1430 | 1431 | [[package]] 1432 | name = "wayland-sys" 1433 | version = "0.29.4" 1434 | source = "registry+https://github.com/rust-lang/crates.io-index" 1435 | checksum = "d9341df79a8975679188e37dab3889bfa57c44ac2cb6da166f519a81cbe452d4" 1436 | dependencies = [ 1437 | "dlib", 1438 | "lazy_static", 1439 | "pkg-config", 1440 | ] 1441 | 1442 | [[package]] 1443 | name = "web-sys" 1444 | version = "0.3.56" 1445 | source = "registry+https://github.com/rust-lang/crates.io-index" 1446 | checksum = "c060b319f29dd25724f09a2ba1418f142f539b2be99fbf4d2d5a8f7330afb8eb" 1447 | dependencies = [ 1448 | "js-sys", 1449 | "wasm-bindgen", 1450 | ] 1451 | 1452 | [[package]] 1453 | name = "winapi" 1454 | version = "0.3.9" 1455 | source = "registry+https://github.com/rust-lang/crates.io-index" 1456 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1457 | dependencies = [ 1458 | "winapi-i686-pc-windows-gnu", 1459 | "winapi-x86_64-pc-windows-gnu", 1460 | ] 1461 | 1462 | [[package]] 1463 | name = "winapi-i686-pc-windows-gnu" 1464 | version = "0.4.0" 1465 | source = "registry+https://github.com/rust-lang/crates.io-index" 1466 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1467 | 1468 | [[package]] 1469 | name = "winapi-util" 1470 | version = "0.1.5" 1471 | source = "registry+https://github.com/rust-lang/crates.io-index" 1472 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 1473 | dependencies = [ 1474 | "winapi", 1475 | ] 1476 | 1477 | [[package]] 1478 | name = "winapi-x86_64-pc-windows-gnu" 1479 | version = "0.4.0" 1480 | source = "registry+https://github.com/rust-lang/crates.io-index" 1481 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1482 | 1483 | [[package]] 1484 | name = "winit" 1485 | version = "0.26.1" 1486 | source = "registry+https://github.com/rust-lang/crates.io-index" 1487 | checksum = "9b43cc931d58b99461188607efd7acb2a093e65fc621f54cad78517a6063e73a" 1488 | dependencies = [ 1489 | "bitflags", 1490 | "cocoa", 1491 | "core-foundation 0.9.1", 1492 | "core-graphics 0.22.2", 1493 | "core-video-sys", 1494 | "dispatch", 1495 | "instant", 1496 | "lazy_static", 1497 | "libc", 1498 | "log", 1499 | "mio", 1500 | "ndk", 1501 | "ndk-glue", 1502 | "ndk-sys", 1503 | "objc", 1504 | "parking_lot", 1505 | "percent-encoding", 1506 | "raw-window-handle", 1507 | "smithay-client-toolkit", 1508 | "wasm-bindgen", 1509 | "wayland-client", 1510 | "wayland-protocols", 1511 | "web-sys", 1512 | "winapi", 1513 | "x11-dl", 1514 | ] 1515 | 1516 | [[package]] 1517 | name = "x11-dl" 1518 | version = "2.18.5" 1519 | source = "registry+https://github.com/rust-lang/crates.io-index" 1520 | checksum = "2bf981e3a5b3301209754218f962052d4d9ee97e478f4d26d4a6eced34c1fef8" 1521 | dependencies = [ 1522 | "lazy_static", 1523 | "libc", 1524 | "maybe-uninit", 1525 | "pkg-config", 1526 | ] 1527 | 1528 | [[package]] 1529 | name = "xcursor" 1530 | version = "0.3.3" 1531 | source = "registry+https://github.com/rust-lang/crates.io-index" 1532 | checksum = "3a9a231574ae78801646617cefd13bfe94be907c0e4fa979cfd8b770aa3c5d08" 1533 | dependencies = [ 1534 | "nom", 1535 | ] 1536 | 1537 | [[package]] 1538 | name = "xml-rs" 1539 | version = "0.8.3" 1540 | source = "registry+https://github.com/rust-lang/crates.io-index" 1541 | checksum = "b07db065a5cf61a7e4ba64f29e67db906fb1787316516c4e6e5ff0fea1efcd8a" 1542 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["o2sh "] 3 | description = "Text Renderer" 4 | edition = "2018" 5 | exclude = ["resources/*"] 6 | license = "MIT" 7 | name = "provok" 8 | readme = "README.md" 9 | repository = "https://github.com/o2sh/provok" 10 | version = "0.1.0" 11 | 12 | [dependencies] 13 | anyhow = "1.0" 14 | clap = {version = "3.1.8", features = ["cargo", "wrap_help"]} 15 | euclid = "0.22.4" 16 | freetype = "0.7.0" 17 | glium = "0.31.0" 18 | harfbuzz-sys = "0.5.0" 19 | libc = "0.2.94" 20 | log = "0.4.14" 21 | palette = "0.6.0" 22 | serde = {version = "1.0.126", features = ["derive"]} 23 | serde_json = "1.0.64" 24 | thiserror = "1.0" 25 | ttf-parser = "0.15.0" 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Ossama Hjaji 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | build: 2 | cargo build --release 3 | 4 | install: 5 | cargo install --path "." 6 | 7 | uninstall: 8 | cargo uninstall provok 9 | 10 | clean: 11 | cargo clean 12 | 13 | release-mac: 14 | strip target/release/provok 15 | mkdir -p release 16 | tar -C ./target/release/ -czvf ./release/provok-mac.tar.gz ./provok 17 | 18 | release-win: 19 | mkdir -p release 20 | tar -C ./target/release/ -czvf ./release/provok-win.tar.gz ./provok.exe 21 | 22 | release-linux: 23 | strip target/release/provok 24 | mkdir -p release 25 | tar -C ./target/release/ -czvf ./release/provok-linux.tar.gz ./provok 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Provok 2 | 3 | Text Renderer written in Rust using HarfBuzz for shaping, FreeType for rasterization and OpenGL for rendering. 4 | 5 |

6 | 7 | ## Input 8 | 9 | Provok is fed with a [JSON file](./examples/0.json) that consists of an array of word alongside their display parameters (fg_color, boldness, italic, etc.): 10 | 11 | ```text 12 | { 13 | "font_size": 50, 14 | "words": [ 15 | { 16 | "text": "\"PROVOK\"", 17 | "canvas_color": "#E24E43", 18 | "bg_color": "#EFB715", 19 | "fg_color": "#E24E43", 20 | "bold": true 21 | }, 22 | ... 23 | } 24 | 25 | ``` 26 | 27 | ## How To Use 28 | 29 | First, you need to have installed the [Rust toolchain](https://www.rust-lang.org/tools/install) and [HarfBuzz](https://harfbuzz.github.io) on your machine, then: 30 | 31 | ```text 32 | git clone https://github.com/o2sh/provok --depth=1 33 | cd provok 34 | make install 35 | provok 36 | ``` 37 | 38 | You can also provide your own custom input file with the `--input` CLI flag: 39 | 40 | ```text 41 | provok -i /path/to/input-file 42 | ``` 43 | 44 | You can specify the frequency (in frame per second) at which the word will appear using the `--frequency` CLI flag: 45 | 46 | ```text 47 | provok -f 5 48 | ``` 49 | -------------------------------------------------------------------------------- /assets/fonts/jetbrains/JetBrainsMono-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/o2sh/provok/13e81a62a0b4105613c6d5452ad3e532ccc8512d/assets/fonts/jetbrains/JetBrainsMono-Bold.ttf -------------------------------------------------------------------------------- /assets/fonts/jetbrains/JetBrainsMono-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/o2sh/provok/13e81a62a0b4105613c6d5452ad3e532ccc8512d/assets/fonts/jetbrains/JetBrainsMono-Regular.ttf -------------------------------------------------------------------------------- /assets/fonts/noto/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2018 The Noto Project Authors (github.com/googlei18n/noto-fonts) 2 | 3 | This Font Software is licensed under the SIL Open Font License, 4 | Version 1.1. 5 | 6 | This license is copied below, and is also available with a FAQ at: 7 | http://scripts.sil.org/OFL 8 | 9 | ----------------------------------------------------------- 10 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 11 | ----------------------------------------------------------- 12 | 13 | PREAMBLE 14 | The goals of the Open Font License (OFL) are to stimulate worldwide 15 | development of collaborative font projects, to support the font 16 | creation efforts of academic and linguistic communities, and to 17 | provide a free and open framework in which fonts may be shared and 18 | improved in partnership with others. 19 | 20 | The OFL allows the licensed fonts to be used, studied, modified and 21 | redistributed freely as long as they are not sold by themselves. The 22 | fonts, including any derivative works, can be bundled, embedded, 23 | redistributed and/or sold with any software provided that any reserved 24 | names are not used by derivative works. The fonts and derivatives, 25 | however, cannot be released under any other type of license. The 26 | requirement for fonts to remain under this license does not apply to 27 | any document created using the fonts or their derivatives. 28 | 29 | DEFINITIONS 30 | "Font Software" refers to the set of files released by the Copyright 31 | Holder(s) under this license and clearly marked as such. This may 32 | include source files, build scripts and documentation. 33 | 34 | "Reserved Font Name" refers to any names specified as such after the 35 | copyright statement(s). 36 | 37 | "Original Version" refers to the collection of Font Software 38 | components as distributed by the Copyright Holder(s). 39 | 40 | "Modified Version" refers to any derivative made by adding to, 41 | deleting, or substituting -- in part or in whole -- any of the 42 | components of the Original Version, by changing formats or by porting 43 | the Font Software to a new environment. 44 | 45 | "Author" refers to any designer, engineer, programmer, technical 46 | writer or other person who contributed to the Font Software. 47 | 48 | PERMISSION & CONDITIONS 49 | Permission is hereby granted, free of charge, to any person obtaining 50 | a copy of the Font Software, to use, study, copy, merge, embed, 51 | modify, redistribute, and sell modified and unmodified copies of the 52 | Font Software, subject to the following conditions: 53 | 54 | 1) Neither the Font Software nor any of its individual components, in 55 | Original or Modified Versions, may be sold by itself. 56 | 57 | 2) Original or Modified Versions of the Font Software may be bundled, 58 | redistributed and/or sold with any software, provided that each copy 59 | contains the above copyright notice and this license. These can be 60 | included either as stand-alone text files, human-readable headers or 61 | in the appropriate machine-readable metadata fields within text or 62 | binary files as long as those fields can be easily viewed by the user. 63 | 64 | 3) No Modified Version of the Font Software may use the Reserved Font 65 | Name(s) unless explicit written permission is granted by the 66 | corresponding Copyright Holder. This restriction only applies to the 67 | primary font name as presented to the users. 68 | 69 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 70 | Software shall not be used to promote, endorse or advertise any 71 | Modified Version, except to acknowledge the contribution(s) of the 72 | Copyright Holder(s) and the Author(s) or with their explicit written 73 | permission. 74 | 75 | 5) The Font Software, modified or unmodified, in part or in whole, 76 | must be distributed entirely under this license, and must not be 77 | distributed under any other license. The requirement for fonts to 78 | remain under this license does not apply to any document created using 79 | the Font Software. 80 | 81 | TERMINATION 82 | This license becomes null and void if any of the above conditions are 83 | not met. 84 | 85 | DISCLAIMER 86 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 87 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 88 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 89 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 90 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 91 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 92 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 93 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 94 | OTHER DEALINGS IN THE FONT SOFTWARE. 95 | -------------------------------------------------------------------------------- /assets/fonts/noto/NotoSans-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/o2sh/provok/13e81a62a0b4105613c6d5452ad3e532ccc8512d/assets/fonts/noto/NotoSans-Bold.ttf -------------------------------------------------------------------------------- /assets/fonts/noto/NotoSans-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/o2sh/provok/13e81a62a0b4105613c6d5452ad3e532ccc8512d/assets/fonts/noto/NotoSans-BoldItalic.ttf -------------------------------------------------------------------------------- /assets/fonts/noto/NotoSans-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/o2sh/provok/13e81a62a0b4105613c6d5452ad3e532ccc8512d/assets/fonts/noto/NotoSans-Italic.ttf -------------------------------------------------------------------------------- /assets/fonts/noto/NotoSans-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/o2sh/provok/13e81a62a0b4105613c6d5452ad3e532ccc8512d/assets/fonts/noto/NotoSans-Regular.ttf -------------------------------------------------------------------------------- /assets/fonts/noto/NotoSansArabic-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/o2sh/provok/13e81a62a0b4105613c6d5452ad3e532ccc8512d/assets/fonts/noto/NotoSansArabic-Bold.ttf -------------------------------------------------------------------------------- /assets/fonts/noto/NotoSansArabic-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/o2sh/provok/13e81a62a0b4105613c6d5452ad3e532ccc8512d/assets/fonts/noto/NotoSansArabic-Regular.ttf -------------------------------------------------------------------------------- /assets/fonts/noto/NotoSansDevanagari-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/o2sh/provok/13e81a62a0b4105613c6d5452ad3e532ccc8512d/assets/fonts/noto/NotoSansDevanagari-Bold.ttf -------------------------------------------------------------------------------- /assets/fonts/noto/NotoSansDevanagari-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/o2sh/provok/13e81a62a0b4105613c6d5452ad3e532ccc8512d/assets/fonts/noto/NotoSansDevanagari-Regular.ttf -------------------------------------------------------------------------------- /assets/fonts/noto/NotoSansSC-Bold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/o2sh/provok/13e81a62a0b4105613c6d5452ad3e532ccc8512d/assets/fonts/noto/NotoSansSC-Bold.otf -------------------------------------------------------------------------------- /assets/fonts/noto/NotoSansSC-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/o2sh/provok/13e81a62a0b4105613c6d5452ad3e532ccc8512d/assets/fonts/noto/NotoSansSC-Regular.otf -------------------------------------------------------------------------------- /assets/fonts/noto/NotoSansThai-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/o2sh/provok/13e81a62a0b4105613c6d5452ad3e532ccc8512d/assets/fonts/noto/NotoSansThai-Bold.ttf -------------------------------------------------------------------------------- /assets/fonts/noto/NotoSansThai-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/o2sh/provok/13e81a62a0b4105613c6d5452ad3e532ccc8512d/assets/fonts/noto/NotoSansThai-Regular.ttf -------------------------------------------------------------------------------- /assets/fonts/siliguri/HindSiliguri-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/o2sh/provok/13e81a62a0b4105613c6d5452ad3e532ccc8512d/assets/fonts/siliguri/HindSiliguri-Bold.ttf -------------------------------------------------------------------------------- /assets/fonts/siliguri/HindSiliguri-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/o2sh/provok/13e81a62a0b4105613c6d5452ad3e532ccc8512d/assets/fonts/siliguri/HindSiliguri-Regular.ttf -------------------------------------------------------------------------------- /assets/fonts/siliguri/OFL.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Indian Type Foundry (info@indiantypefoundry.com) 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | This license is copied below, and is also available with a FAQ at: 5 | http://scripts.sil.org/OFL 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /assets/shaders/g_fragment.glsl: -------------------------------------------------------------------------------- 1 | #version 330 2 | precision mediump float; 3 | 4 | in vec2 o_tex; 5 | in vec4 o_fg_color; 6 | in vec4 o_bg_color; 7 | 8 | uniform sampler2D glyph_tex; 9 | uniform bool draw_bg; 10 | 11 | out vec4 color; 12 | 13 | void main() { 14 | if (draw_bg) { 15 | color = o_bg_color; 16 | } else { 17 | color = texture(glyph_tex, o_tex); 18 | color.rgb = o_fg_color.rgb; 19 | } 20 | } -------------------------------------------------------------------------------- /assets/shaders/g_vertex.glsl: -------------------------------------------------------------------------------- 1 | #version 330 2 | precision mediump float; 3 | in vec2 position; 4 | in vec2 tex; 5 | in vec4 fg_color; 6 | in vec4 bg_color; 7 | 8 | uniform mat4 projection; 9 | uniform bool draw_bg; 10 | 11 | out vec2 o_tex; 12 | out vec4 o_fg_color; 13 | out vec4 o_bg_color; 14 | 15 | void main() { 16 | o_tex = tex; 17 | o_fg_color = fg_color; 18 | o_bg_color = bg_color; 19 | 20 | gl_Position = projection * vec4(position, 0.0, 1.0); 21 | } 22 | -------------------------------------------------------------------------------- /examples/0.json: -------------------------------------------------------------------------------- 1 | { 2 | "font_size": 70, 3 | "words": [ 4 | { 5 | "text": "\"PROVOK\"", 6 | "canvas_color": "#000000", 7 | "fg_color": "#FF0202", 8 | "bold": true 9 | }, 10 | { 11 | "text": "الإستفزاز", 12 | "canvas_color": "#000000", 13 | "fg_color": "#00FF28", 14 | "bold": true 15 | }, 16 | { 17 | "text": "去挑衅", 18 | "canvas_color": "#000000", 19 | "fg_color": "#000AFF", 20 | "bold": true 21 | }, 22 | { 23 | "text": "उकसाने के लिए", 24 | "canvas_color": "#000000", 25 | "fg_color": "#FFFC02", 26 | "bold": true 27 | }, 28 | { 29 | "text": "побуждение", 30 | "canvas_color": "#000000", 31 | "fg_color": "#02FFE1", 32 | "bold": true 33 | }, 34 | { 35 | "text": "เพื่อกระตุ้น", 36 | "canvas_color": "#000000", 37 | "fg_color": "#CC00FF", 38 | "bold": true 39 | } 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /examples/1.json: -------------------------------------------------------------------------------- 1 | { 2 | "font_size": 45, 3 | "words": [ 4 | { 5 | "text": "\"PROVOK\"", 6 | "canvas_color": "#E24E43", 7 | "bg_color": "#EFB715", 8 | "fg_color": "#E24E43", 9 | "bold": true 10 | }, 11 | { 12 | "text": "الإستفزاز", 13 | "canvas_color": "#72A7C8", 14 | "fg_color": "#2D2425", 15 | "bold": true 16 | }, 17 | { 18 | "text": "去挑衅", 19 | "canvas_color": "#D53071", 20 | "fg_color": "#008165", 21 | "bg_color": "#E03427", 22 | "bold": true 23 | }, 24 | { 25 | "text": "उकसाने के लिए", 26 | "canvas_color": "#FFA141", 27 | "fg_color": "#000060", 28 | "bold": true 29 | }, 30 | { 31 | "text": "побуждение", 32 | "canvas_color": "#F7EAB8", 33 | "fg_color": "#F7EAB8", 34 | "bg_color": "#CA332E", 35 | "bold": true 36 | }, 37 | { 38 | "text": "เพื่อกระตุ้น", 39 | "canvas_color": "#66C89B", 40 | "fg_color": "#D4FDA2", 41 | "bold": true 42 | } 43 | ] 44 | } -------------------------------------------------------------------------------- /resources/provok.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/o2sh/provok/13e81a62a0b4105613c6d5452ad3e532ccc8512d/resources/provok.gif -------------------------------------------------------------------------------- /src/bitmaps/atlas.rs: -------------------------------------------------------------------------------- 1 | use crate::bitmaps::{BitmapImage, Texture2d, TextureRect}; 2 | use crate::utils::{Point, Rect, Size}; 3 | use anyhow::{ensure, Result}; 4 | use std::rc::Rc; 5 | use thiserror::*; 6 | 7 | #[derive(Debug, Error)] 8 | #[error("Texture Size exceeded, need {}", size)] 9 | pub struct OutOfTextureSpace { 10 | pub size: usize, 11 | } 12 | 13 | pub struct Atlas 14 | where 15 | T: Texture2d, 16 | { 17 | texture: Rc, 18 | side: usize, 19 | bottom: usize, 20 | tallest: usize, 21 | left: usize, 22 | } 23 | 24 | impl Atlas 25 | where 26 | T: Texture2d, 27 | { 28 | pub fn new(texture: &Rc) -> Result { 29 | ensure!(texture.width() == texture.height(), "texture must be square!"); 30 | Ok(Self { 31 | texture: Rc::clone(texture), 32 | side: texture.width(), 33 | bottom: 0, 34 | tallest: 0, 35 | left: 0, 36 | }) 37 | } 38 | 39 | #[inline] 40 | pub fn texture(&self) -> Rc { 41 | Rc::clone(&self.texture) 42 | } 43 | 44 | pub fn allocate(&mut self, im: &dyn BitmapImage) -> Result, OutOfTextureSpace> { 45 | let (width, height) = im.image_dimensions(); 46 | let reserve_width = width + 2; 47 | let reserve_height = height + 2; 48 | 49 | if reserve_width > self.side || reserve_height > self.side { 50 | return Err(OutOfTextureSpace { 51 | size: reserve_width.max(reserve_height).next_power_of_two(), 52 | }); 53 | } 54 | let x_left = self.side - self.left; 55 | if x_left < reserve_width { 56 | self.bottom += self.tallest; 57 | self.left = 0; 58 | self.tallest = 0; 59 | } 60 | 61 | let y_left = self.side - self.bottom; 62 | if y_left < reserve_height { 63 | return Err(OutOfTextureSpace { 64 | size: (self.side + reserve_width.max(reserve_height)).next_power_of_two(), 65 | }); 66 | } 67 | 68 | let rect = Rect::new( 69 | Point::new(self.left as isize + 1, self.bottom as isize + 1), 70 | Size::new(width as isize, height as isize), 71 | ); 72 | 73 | self.texture.write(rect, im); 74 | 75 | let tex_coords = self.texture.to_texture_coords(rect); 76 | 77 | self.left += reserve_width; 78 | self.tallest = self.tallest.max(reserve_height); 79 | 80 | Ok(Sprite { texture: Rc::clone(&self.texture), tex_coords, width, height }) 81 | } 82 | } 83 | 84 | pub struct Sprite 85 | where 86 | T: Texture2d, 87 | { 88 | pub texture: Rc, 89 | pub tex_coords: TextureRect, 90 | pub width: usize, 91 | pub height: usize, 92 | } 93 | 94 | impl Clone for Sprite 95 | where 96 | T: Texture2d, 97 | { 98 | fn clone(&self) -> Self { 99 | Self { 100 | texture: Rc::clone(&self.texture), 101 | tex_coords: self.tex_coords, 102 | width: self.width, 103 | height: self.height, 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/bitmaps/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::color::Color; 2 | use crate::utils::Rect; 3 | use glium::texture::SrgbTexture2d; 4 | 5 | pub mod atlas; 6 | 7 | pub struct TextureUnit; 8 | pub type TextureCoord = euclid::Point2D; 9 | pub type TextureRect = euclid::Rect; 10 | pub type TextureSize = euclid::Size2D; 11 | 12 | pub trait Texture2d { 13 | fn write(&self, rect: Rect, im: &dyn BitmapImage); 14 | fn width(&self) -> usize; 15 | fn height(&self) -> usize; 16 | fn to_texture_coords(&self, coords: Rect) -> TextureRect { 17 | let coords = coords.to_f32(); 18 | let width = self.width() as f32; 19 | let height = self.height() as f32; 20 | TextureRect::new( 21 | TextureCoord::new(coords.min_x() / width, coords.min_y() / height), 22 | TextureSize::new(coords.size.width / width, coords.size.height / height), 23 | ) 24 | } 25 | } 26 | 27 | impl Texture2d for SrgbTexture2d { 28 | fn write(&self, rect: Rect, im: &dyn BitmapImage) { 29 | let (im_width, im_height) = im.image_dimensions(); 30 | 31 | let source = glium::texture::RawImage2d { 32 | data: im 33 | .pixels() 34 | .iter() 35 | .map(|&p| { 36 | let (r, g, b, a) = Color(p).as_rgba(); 37 | 38 | fn conv(v: u8) -> u8 { 39 | let f = (v as f32) / 255.; 40 | let c = if f <= 0.0031308 { 41 | f * 12.92 42 | } else { 43 | f.powf(1.0 / 2.4) * 1.055 - 0.055 44 | }; 45 | (c * 255.).ceil() as u8 46 | } 47 | Color::rgba(conv(b), conv(g), conv(r), conv(a)).0 48 | }) 49 | .collect(), 50 | width: im_width as u32, 51 | height: im_height as u32, 52 | format: glium::texture::ClientFormat::U8U8U8U8, 53 | }; 54 | 55 | SrgbTexture2d::write( 56 | self, 57 | glium::Rect { 58 | left: rect.min_x() as u32, 59 | bottom: rect.min_y() as u32, 60 | width: rect.size.width as u32, 61 | height: rect.size.height as u32, 62 | }, 63 | source, 64 | ) 65 | } 66 | 67 | fn width(&self) -> usize { 68 | SrgbTexture2d::width(self) as usize 69 | } 70 | 71 | fn height(&self) -> usize { 72 | SrgbTexture2d::height(self) as usize 73 | } 74 | } 75 | 76 | pub trait BitmapImage { 77 | unsafe fn pixel_data(&self) -> *const u8; 78 | 79 | fn image_dimensions(&self) -> (usize, usize); 80 | 81 | fn pixels(&self) -> &[u32] { 82 | let (width, height) = self.image_dimensions(); 83 | unsafe { 84 | let first = self.pixel_data() as *const u32; 85 | std::slice::from_raw_parts(first, width * height) 86 | } 87 | } 88 | } 89 | 90 | pub struct Image { 91 | data: Vec, 92 | width: usize, 93 | height: usize, 94 | } 95 | 96 | impl Image { 97 | pub fn new(width: usize, height: usize) -> Image { 98 | let size = height * width * 4; 99 | let mut data = vec![0; size]; 100 | data.resize(size, 0); 101 | Image { data, width, height } 102 | } 103 | 104 | pub fn with_rgba32(width: usize, height: usize, stride: usize, data: &[u8]) -> Image { 105 | let mut image = Image::new(width, height); 106 | for y in 0..height { 107 | let src_offset = y * stride; 108 | let dest_offset = y * width * 4; 109 | for x in 0..width { 110 | let red = data[src_offset + (x * 4)]; 111 | let green = data[src_offset + (x * 4) + 1]; 112 | let blue = data[src_offset + (x * 4) + 2]; 113 | let alpha = data[src_offset + (x * 4) + 3]; 114 | image.data[dest_offset + (x * 4)] = blue; 115 | image.data[dest_offset + (x * 4) + 1] = green; 116 | image.data[dest_offset + (x * 4) + 2] = red; 117 | image.data[dest_offset + (x * 4) + 3] = alpha; 118 | } 119 | } 120 | image 121 | } 122 | } 123 | 124 | impl BitmapImage for Image { 125 | unsafe fn pixel_data(&self) -> *const u8 { 126 | self.data.as_ptr() 127 | } 128 | 129 | fn image_dimensions(&self) -> (usize, usize) { 130 | (self.width, self.height) 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/color.rs: -------------------------------------------------------------------------------- 1 | use palette::{Srgb, Srgba}; 2 | use serde::{self, Deserialize, Deserializer, Serialize, Serializer}; 3 | use std::result::Result; 4 | 5 | #[derive(Debug, Clone, Copy, Default, Eq, PartialEq, Hash)] 6 | pub struct RgbColor { 7 | pub red: u8, 8 | pub green: u8, 9 | pub blue: u8, 10 | } 11 | 12 | impl RgbColor { 13 | pub fn new(red: u8, green: u8, blue: u8) -> Self { 14 | Self { red, green, blue } 15 | } 16 | 17 | pub fn to_rgb_string(self) -> String { 18 | format!("#{:02x}{:02x}{:02x}", self.red, self.green, self.blue) 19 | } 20 | 21 | pub fn from_rgb_str(s: &str) -> Option { 22 | if s.as_bytes()[0] == b'#' && s.len() == 7 { 23 | let mut chars = s.chars().skip(1); 24 | 25 | macro_rules! digit { 26 | () => {{ 27 | let hi = match chars.next().unwrap().to_digit(16) { 28 | Some(v) => (v as u8) << 4, 29 | None => return None, 30 | }; 31 | let lo = match chars.next().unwrap().to_digit(16) { 32 | Some(v) => v as u8, 33 | None => return None, 34 | }; 35 | hi | lo 36 | }}; 37 | } 38 | Some(Self::new(digit!(), digit!(), digit!())) 39 | } else { 40 | None 41 | } 42 | } 43 | 44 | pub fn from_named(name: &str) -> Option { 45 | palette::named::from_str(&name.to_ascii_lowercase()).map(|color| { 46 | let color = Srgb::::from_format(color); 47 | Self::new(color.red, color.green, color.blue) 48 | }) 49 | } 50 | 51 | pub fn from_named_or_rgb_string(s: &str) -> Option { 52 | RgbColor::from_rgb_str(s).or_else(|| RgbColor::from_named(s)) 53 | } 54 | } 55 | 56 | impl Serialize for RgbColor { 57 | fn serialize(&self, serializer: S) -> Result 58 | where 59 | S: Serializer, 60 | { 61 | let s = self.to_rgb_string(); 62 | s.serialize(serializer) 63 | } 64 | } 65 | 66 | impl<'de> Deserialize<'de> for RgbColor { 67 | fn deserialize(deserializer: D) -> Result 68 | where 69 | D: Deserializer<'de>, 70 | { 71 | let s = String::deserialize(deserializer)?; 72 | RgbColor::from_named_or_rgb_string(&s) 73 | .ok_or_else(|| format!("unknown color name: {}", s)) 74 | .map_err(serde::de::Error::custom) 75 | } 76 | } 77 | 78 | #[derive(Copy, Clone, Debug)] 79 | pub struct Color(pub u32); 80 | 81 | impl From for Color { 82 | fn from(s: Srgb) -> Color { 83 | let b: Srgb = s.into_format(); 84 | let b = b.into_components(); 85 | Color::rgb(b.0, b.1, b.2) 86 | } 87 | } 88 | 89 | impl From for Color { 90 | fn from(s: Srgba) -> Color { 91 | let b: Srgba = s.into_format(); 92 | let b = b.into_components(); 93 | Color::rgba(b.0, b.1, b.2, b.3) 94 | } 95 | } 96 | 97 | impl From for Srgb { 98 | fn from(c: Color) -> Srgb { 99 | let c = c.as_rgba(); 100 | let s = Srgb::::new(c.0, c.1, c.2); 101 | s.into_format() 102 | } 103 | } 104 | 105 | impl From for Srgba { 106 | fn from(c: Color) -> Srgba { 107 | let c = c.as_rgba(); 108 | let s = Srgba::::new(c.0, c.1, c.2, c.3); 109 | s.into_format() 110 | } 111 | } 112 | 113 | impl Color { 114 | pub fn rgb(red: u8, green: u8, blue: u8) -> Color { 115 | Color::rgba(red, green, blue, 0xff) 116 | } 117 | 118 | pub fn rgba(red: u8, green: u8, blue: u8, alpha: u8) -> Color { 119 | let word = (blue as u32) << 24 | (green as u32) << 16 | (red as u32) << 8 | alpha as u32; 120 | Color(word.to_be()) 121 | } 122 | 123 | pub fn as_rgba(self) -> (u8, u8, u8, u8) { 124 | let host = u32::from_be(self.0); 125 | ((host >> 8) as u8, (host >> 16) as u8, (host >> 24) as u8, (host & 0xff) as u8) 126 | } 127 | 128 | pub fn to_tuple_rgba(self) -> (f32, f32, f32, f32) { 129 | let c: Srgba = self.into(); 130 | c.into_format().into_components() 131 | } 132 | } 133 | 134 | pub fn to_tuple_rgba(color: RgbColor) -> (f32, f32, f32, f32) { 135 | let color = Color::rgba(color.red, color.green, color.blue, 0xff); 136 | color.to_tuple_rgba() 137 | } 138 | -------------------------------------------------------------------------------- /src/font/ftwrap.rs: -------------------------------------------------------------------------------- 1 | use crate::font::loader::FontDataHandle; 2 | use anyhow::{anyhow, Context, Result}; 3 | pub use freetype::freetype::*; 4 | use libc::{self, c_long, c_void, size_t}; 5 | use std::ptr; 6 | use std::rc::Rc; 7 | 8 | #[inline] 9 | pub fn succeeded(error: FT_Error) -> bool { 10 | error == freetype::freetype::FT_Err_Ok as FT_Error 11 | } 12 | 13 | fn ft_result(err: FT_Error, t: T) -> Result { 14 | if succeeded(err) { 15 | Ok(t) 16 | } else { 17 | Err(anyhow!("FreeType error {:?} 0x{:x}", err, err)) 18 | } 19 | } 20 | 21 | fn render_mode_to_load_target(render_mode: FT_Render_Mode) -> u32 { 22 | (render_mode as u32) & 15 << 16 23 | } 24 | 25 | pub fn compute_load_flags() -> (i32, FT_Render_Mode) { 26 | let render = FT_Render_Mode::FT_RENDER_MODE_LCD; 27 | 28 | let flags = render_mode_to_load_target(render); 29 | 30 | (flags as i32, render) 31 | } 32 | 33 | impl Clone for Face { 34 | fn clone(&self) -> Self { 35 | let err = unsafe { FT_Reference_Library(self.lib) }; 36 | if err != freetype::freetype::FT_Err_Ok as FT_Error { 37 | panic!("Failed to reference library"); 38 | } 39 | let err = unsafe { FT_Reference_Face(self.face) }; 40 | if err != freetype::freetype::FT_Err_Ok as FT_Error { 41 | panic!("Failed to reference face"); 42 | } 43 | Face { lib: self.lib, face: self.face, bytes: self.bytes.clone() } 44 | } 45 | } 46 | 47 | pub struct Face { 48 | lib: FT_Library, 49 | pub face: FT_Face, 50 | bytes: Rc>, 51 | } 52 | 53 | impl Drop for Face { 54 | fn drop(&mut self) { 55 | let err = unsafe { FT_Done_Face(self.face) }; 56 | if err != freetype::freetype::FT_Err_Ok as FT_Error { 57 | panic!("Failed to drop face"); 58 | } 59 | let err = unsafe { FT_Done_Library(self.lib) }; 60 | if err != freetype::freetype::FT_Err_Ok as FT_Error { 61 | panic!("Failed to drop library") 62 | } 63 | } 64 | } 65 | 66 | impl Face { 67 | pub fn set_font_size(&mut self, point_size: f64, dpi: u32) -> Result<()> { 68 | let size = (point_size * 64.0) as FT_F26Dot6; 69 | self.set_char_size(size, 0, dpi, 0) 70 | } 71 | 72 | fn set_char_size( 73 | &mut self, 74 | char_width: FT_F26Dot6, 75 | char_height: FT_F26Dot6, 76 | horz_resolution: FT_UInt, 77 | vert_resolution: FT_UInt, 78 | ) -> Result<()> { 79 | ft_result( 80 | unsafe { 81 | FT_Set_Char_Size( 82 | self.face, 83 | char_width, 84 | char_height, 85 | horz_resolution, 86 | vert_resolution, 87 | ) 88 | }, 89 | (), 90 | ) 91 | } 92 | 93 | pub fn load_and_render_glyph( 94 | &mut self, 95 | glyph_index: FT_UInt, 96 | load_flags: FT_Int32, 97 | render_mode: FT_Render_Mode, 98 | ) -> Result<&FT_GlyphSlotRec_> { 99 | unsafe { 100 | let res = FT_Load_Glyph(self.face, glyph_index, load_flags); 101 | let slot = ft_result(res, &mut *(*self.face).glyph)?; 102 | ft_result(FT_Render_Glyph(slot, render_mode), slot) 103 | } 104 | } 105 | } 106 | 107 | extern "C" fn alloc_library(_memory: FT_Memory, size: c_long) -> *mut c_void { 108 | unsafe { libc::malloc(size as size_t) } 109 | } 110 | 111 | extern "C" fn free_library(_memory: FT_Memory, block: *mut c_void) { 112 | unsafe { libc::free(block) } 113 | } 114 | 115 | extern "C" fn realloc_library( 116 | _memory: FT_Memory, 117 | _cur_size: c_long, 118 | new_size: c_long, 119 | block: *mut c_void, 120 | ) -> *mut c_void { 121 | unsafe { libc::realloc(block, new_size as size_t) } 122 | } 123 | 124 | static mut MEMORY: FT_MemoryRec_ = FT_MemoryRec_ { 125 | user: 0 as *mut c_void, 126 | alloc: Some(alloc_library), 127 | free: Some(free_library), 128 | realloc: Some(realloc_library), 129 | }; 130 | 131 | pub struct Library { 132 | lib: FT_Library, 133 | } 134 | 135 | impl Drop for Library { 136 | fn drop(&mut self) { 137 | unsafe { 138 | FT_Done_Library(self.lib); 139 | } 140 | } 141 | } 142 | 143 | impl Library { 144 | pub fn new() -> Result { 145 | let mut lib = ptr::null_mut(); 146 | 147 | let err = unsafe { FT_New_Library(&mut MEMORY, &mut lib) }; 148 | if err == freetype::freetype::FT_Err_Ok as FT_Error { 149 | unsafe { 150 | FT_Add_Default_Modules(lib); 151 | } 152 | Ok(Library { lib }) 153 | } else { 154 | panic!("failed to create new library") 155 | } 156 | } 157 | 158 | pub fn new_face(&self, handle: &FontDataHandle) -> Result { 159 | unsafe { self.new_memory_face(self.lib, handle) } 160 | } 161 | pub unsafe fn new_memory_face( 162 | &self, 163 | library_raw: FT_Library, 164 | handle: &FontDataHandle, 165 | ) -> Result { 166 | let data = Rc::new(handle.data.to_vec()); 167 | let mut face = ptr::null_mut(); 168 | 169 | let res = FT_New_Memory_Face( 170 | self.lib, 171 | data.as_ptr(), 172 | data.len() as _, 173 | handle.index as _, 174 | &mut face as *mut _, 175 | ); 176 | FT_Reference_Library(library_raw); 177 | Ok(Face { 178 | lib: library_raw, 179 | face: ft_result(res, face) 180 | .with_context(|| format!("FT_New_Memory_Face for index {}", handle.index))?, 181 | bytes: data, 182 | }) 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /src/font/hbwrap.rs: -------------------------------------------------------------------------------- 1 | use freetype; 2 | 3 | pub use harfbuzz::*; 4 | use harfbuzz_sys as harfbuzz; 5 | 6 | use anyhow::{ensure, Result}; 7 | use std::mem; 8 | use std::slice; 9 | 10 | extern "C" { 11 | pub fn hb_ft_font_create_referenced(face: freetype::freetype::FT_Face) -> *mut hb_font_t; 12 | } 13 | 14 | pub fn feature_from_string(s: &str) -> Result { 15 | unsafe { 16 | let mut feature = mem::zeroed(); 17 | ensure!( 18 | hb_feature_from_string(s.as_ptr() as *const i8, s.len() as i32, &mut feature as *mut _,) 19 | != 0, 20 | "failed to create feature from {}", 21 | s 22 | ); 23 | Ok(feature) 24 | } 25 | } 26 | 27 | pub struct Font { 28 | font: *mut hb_font_t, 29 | } 30 | 31 | impl Drop for Font { 32 | fn drop(&mut self) { 33 | unsafe { 34 | hb_font_destroy(self.font); 35 | } 36 | } 37 | } 38 | 39 | impl Font { 40 | pub fn new(face: freetype::freetype::FT_Face) -> Font { 41 | Font { font: unsafe { hb_ft_font_create_referenced(face as _) } } 42 | } 43 | 44 | pub fn shape(&mut self, buf: &mut Buffer, features: &[hb_feature_t]) { 45 | unsafe { hb_shape(self.font, buf.buf, features.as_ptr(), features.len() as u32) } 46 | } 47 | } 48 | 49 | pub struct Buffer { 50 | buf: *mut hb_buffer_t, 51 | } 52 | 53 | impl Drop for Buffer { 54 | fn drop(&mut self) { 55 | unsafe { 56 | hb_buffer_destroy(self.buf); 57 | } 58 | } 59 | } 60 | 61 | impl Buffer { 62 | pub fn new() -> Result { 63 | let buf = unsafe { hb_buffer_create() }; 64 | ensure!(unsafe { hb_buffer_allocation_successful(buf) } != 0, "hb_buffer_create failed"); 65 | Ok(Buffer { buf }) 66 | } 67 | 68 | pub fn guess_segment_properties(&mut self) { 69 | unsafe { hb_buffer_guess_segment_properties(self.buf) }; 70 | } 71 | pub fn get_script(&self) -> hb_script_t { 72 | unsafe { hb_buffer_get_script(self.buf) } 73 | } 74 | 75 | pub fn add_utf8(&mut self, buf: &[u8]) { 76 | unsafe { 77 | hb_buffer_add_utf8( 78 | self.buf, 79 | buf.as_ptr() as *const i8, 80 | buf.len() as i32, 81 | 0, 82 | buf.len() as i32, 83 | ); 84 | } 85 | } 86 | 87 | pub fn add_str(&mut self, s: &str) { 88 | self.add_utf8(s.as_bytes()) 89 | } 90 | 91 | pub fn glyph_infos(&self) -> &[hb_glyph_info_t] { 92 | unsafe { 93 | let mut len: u32 = 0; 94 | let info = hb_buffer_get_glyph_infos(self.buf, &mut len as *mut _); 95 | slice::from_raw_parts(info, len as usize) 96 | } 97 | } 98 | 99 | pub fn glyph_positions(&self) -> &[hb_glyph_position_t] { 100 | unsafe { 101 | let mut len: u32 = 0; 102 | let pos = hb_buffer_get_glyph_positions(self.buf, &mut len as *mut _); 103 | slice::from_raw_parts(pos, len as usize) 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/font/loader/mod.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | pub mod parser; 3 | 4 | #[derive(Debug)] 5 | pub struct Names { 6 | full_name: String, 7 | unique: Option, 8 | family: Option, 9 | sub_family: Option, 10 | postscript_name: Option, 11 | } 12 | 13 | #[derive(Clone, PartialEq, Eq)] 14 | pub struct FontDataHandle { 15 | pub name: String, 16 | pub data: Vec, 17 | pub index: u32, 18 | } 19 | -------------------------------------------------------------------------------- /src/font/loader/parser.rs: -------------------------------------------------------------------------------- 1 | use crate::font::loader::{FontDataHandle, Names}; 2 | use crate::input::FontAttributes; 3 | use anyhow::{bail, Result}; 4 | 5 | pub fn load_built_in_font(font_attributes: &FontAttributes) -> Result { 6 | let mut font_info = vec![]; 7 | load_built_in_fonts(&mut font_info).ok(); 8 | match_font_info(font_attributes, font_info) 9 | } 10 | 11 | fn match_font_info( 12 | attr: &FontAttributes, 13 | mut font_info: Vec<(Names, FontDataHandle)>, 14 | ) -> Result { 15 | font_info.sort_by_key(|(names, _)| names.full_name.clone()); 16 | 17 | for (names, handle) in &font_info { 18 | if font_info_matches(attr, names) { 19 | return Ok(handle.clone()); 20 | } 21 | } 22 | bail!("Could not find font"); 23 | } 24 | 25 | fn font_info_matches(attr: &FontAttributes, names: &Names) -> bool { 26 | if let Some(fam) = names.family.as_ref() { 27 | if attr.family == *fam { 28 | match names.sub_family.as_deref() { 29 | Some("Italic") if attr.italic && !attr.bold => return true, 30 | Some("Bold") if attr.bold && !attr.italic => return true, 31 | Some("Bold Italic") if attr.bold && attr.italic => return true, 32 | Some("Medium") | Some("Regular") | None if !attr.italic && !attr.bold => { 33 | return true 34 | } 35 | _ => {} 36 | } 37 | } 38 | } 39 | attr.family == names.full_name && !attr.bold && !attr.italic 40 | } 41 | 42 | fn load_built_in_fonts(font_info: &mut Vec<(Names, FontDataHandle)>) -> Result<()> { 43 | macro_rules! font { 44 | ($font:literal) => { 45 | (include_bytes!($font) as &'static [u8], $font) 46 | }; 47 | } 48 | for (data, name) in &[ 49 | font!("../../../assets/fonts/noto/NotoSansArabic-Bold.ttf"), 50 | font!("../../../assets/fonts/noto/NotoSansArabic-Regular.ttf"), 51 | font!("../../../assets/fonts/noto/NotoSansSC-Bold.otf"), 52 | font!("../../../assets/fonts/noto/NotoSansSC-Regular.otf"), 53 | font!("../../../assets/fonts/noto/NotoSansDevanagari-Bold.ttf"), 54 | font!("../../../assets/fonts/noto/NotoSansDevanagari-Regular.ttf"), 55 | font!("../../../assets/fonts/noto/NotoSans-Bold.ttf"), 56 | font!("../../../assets/fonts/noto/NotoSans-BoldItalic.ttf"), 57 | font!("../../../assets/fonts/noto/NotoSans-Italic.ttf"), 58 | font!("../../../assets/fonts/noto/NotoSans-Regular.ttf"), 59 | font!("../../../assets/fonts/noto/NotoSansThai-Bold.ttf"), 60 | font!("../../../assets/fonts/noto/NotoSansThai-Regular.ttf"), 61 | font!("../../../assets/fonts/siliguri/HindSiliguri-Bold.ttf"), 62 | font!("../../../assets/fonts/siliguri/HindSiliguri-Regular.ttf"), 63 | ] { 64 | let face = ttf_parser::Face::from_slice(data, 0)?; 65 | let full_name = face 66 | .names() 67 | .into_iter() 68 | .find(|name| name.name_id == ttf_parser::name_id::FULL_NAME) 69 | .and_then(|name| name.to_string()) 70 | .unwrap(); 71 | 72 | let postscript_name = face 73 | .names() 74 | .into_iter() 75 | .find(|name| name.name_id == ttf_parser::name_id::POST_SCRIPT_NAME) 76 | .and_then(|name| name.to_string()); 77 | 78 | let unique = face 79 | .names() 80 | .into_iter() 81 | .find(|name| name.name_id == ttf_parser::name_id::UNIQUE_ID) 82 | .and_then(|name| name.to_string()); 83 | 84 | let sub_family = face 85 | .names() 86 | .into_iter() 87 | .find(|name| name.name_id == ttf_parser::name_id::SUBFAMILY) 88 | .and_then(|name| name.to_string()); 89 | 90 | let family = face 91 | .names() 92 | .into_iter() 93 | .find(|name| name.name_id == ttf_parser::name_id::FAMILY) 94 | .and_then(|name| name.to_string()); 95 | 96 | let names = Names { full_name, unique, family, sub_family, postscript_name }; 97 | 98 | font_info.push(( 99 | names, 100 | FontDataHandle { data: data.to_vec(), name: name.to_string(), index: 0 }, 101 | )); 102 | } 103 | 104 | Ok(()) 105 | } 106 | -------------------------------------------------------------------------------- /src/font/mod.rs: -------------------------------------------------------------------------------- 1 | use std::cell::RefCell; 2 | use std::collections::HashMap; 3 | use std::rc::Rc; 4 | 5 | pub mod ftwrap; 6 | pub mod hbwrap; 7 | pub mod loader; 8 | pub mod rasterizer; 9 | pub mod shaper; 10 | 11 | use crate::font::loader::parser::load_built_in_font; 12 | use crate::font::rasterizer::FontRasterizer; 13 | pub use crate::font::rasterizer::{FontMetrics, RasterizedGlyph}; 14 | use crate::font::shaper::FontShaper; 15 | pub use crate::font::shaper::GlyphInfo; 16 | use crate::input::TextStyle; 17 | use anyhow::Result; 18 | 19 | pub struct LoadedFont { 20 | rasterizer: Box, 21 | shaper: Box, 22 | } 23 | 24 | impl LoadedFont { 25 | pub fn shape(&self, text: &str) -> Result> { 26 | self.shaper.shape(text) 27 | } 28 | 29 | pub fn rasterize(&self, glyph_pos: u32) -> Result { 30 | self.rasterizer.rasterize(glyph_pos) 31 | } 32 | } 33 | 34 | pub struct FontConfiguration { 35 | fonts: RefCell>>, 36 | font_size: f64, 37 | dpi: u32, 38 | lib: ftwrap::Library, 39 | } 40 | 41 | impl FontConfiguration { 42 | pub fn new(font_size: f64, dpi: u32) -> Result { 43 | let lib = ftwrap::Library::new()?; 44 | Ok(Self { fonts: RefCell::new(HashMap::new()), font_size, dpi, lib }) 45 | } 46 | 47 | pub fn get_font(&self, style: &TextStyle) -> Result> { 48 | let mut fonts = self.fonts.borrow_mut(); 49 | if let Some(entry) = fonts.get(style) { 50 | return Ok(Rc::clone(entry)); 51 | } 52 | let font_data_handle = load_built_in_font(&style.font_attributes)?; 53 | let mut face = self.lib.new_face(&font_data_handle)?; 54 | face.set_font_size(self.font_size, self.dpi)?; 55 | let shaper = shaper::new_shaper(&face)?; 56 | let rasterizer = rasterizer::new_rasterizer(&face)?; 57 | let loaded = Rc::new(LoadedFont { rasterizer, shaper }); 58 | 59 | fonts.insert(style.clone(), Rc::clone(&loaded)); 60 | 61 | Ok(loaded) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/font/rasterizer/freetype.rs: -------------------------------------------------------------------------------- 1 | use crate::font::rasterizer::FontRasterizer; 2 | use crate::font::{ftwrap, RasterizedGlyph}; 3 | use crate::utils::PixelLength; 4 | use anyhow::Result; 5 | use freetype::freetype::FT_GlyphSlotRec_; 6 | use std::cell::RefCell; 7 | use std::slice; 8 | 9 | pub struct FreeTypeRasterizer { 10 | face: RefCell, 11 | } 12 | 13 | impl FontRasterizer for FreeTypeRasterizer { 14 | fn rasterize(&self, glyph_pos: u32) -> Result { 15 | let (load_flags, render_mode) = ftwrap::compute_load_flags(); 16 | 17 | let mut face = self.face.borrow_mut(); 18 | let ft_glyph = face.load_and_render_glyph(glyph_pos, load_flags, render_mode)?; 19 | 20 | let pitch = ft_glyph.bitmap.pitch.abs() as usize; 21 | let data = unsafe { 22 | slice::from_raw_parts_mut(ft_glyph.bitmap.buffer, ft_glyph.bitmap.rows as usize * pitch) 23 | }; 24 | 25 | let glyph = self.rasterize(pitch, ft_glyph, data); 26 | Ok(glyph) 27 | } 28 | } 29 | 30 | impl FreeTypeRasterizer { 31 | fn rasterize(&self, pitch: usize, ft_glyph: &FT_GlyphSlotRec_, data: &[u8]) -> RasterizedGlyph { 32 | let width = ft_glyph.bitmap.width as usize / 3; 33 | let height = ft_glyph.bitmap.rows as usize; 34 | let size = (width * height * 4) as usize; 35 | let mut rgba = vec![0u8; size]; 36 | for y in 0..height { 37 | let src_offset = y * pitch as usize; 38 | let dest_offset = y * width * 4; 39 | for x in 0..width { 40 | let red = data[src_offset + (x * 3)]; 41 | let green = data[src_offset + (x * 3) + 1]; 42 | let blue = data[src_offset + (x * 3) + 2]; 43 | let alpha = red.min(green).min(blue); 44 | rgba[dest_offset + (x * 4)] = red; 45 | rgba[dest_offset + (x * 4) + 1] = green; 46 | rgba[dest_offset + (x * 4) + 2] = blue; 47 | rgba[dest_offset + (x * 4) + 3] = alpha; 48 | } 49 | } 50 | RasterizedGlyph { 51 | data: rgba, 52 | height, 53 | width, 54 | left: PixelLength::new(ft_glyph.bitmap_left as f64), 55 | top: PixelLength::new(ft_glyph.bitmap_top as f64), 56 | } 57 | } 58 | 59 | pub fn new(face: &ftwrap::Face) -> Result { 60 | let cloned_face = face.clone(); 61 | Ok(Self { face: RefCell::new(cloned_face) }) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/font/rasterizer/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::font::ftwrap; 2 | use crate::utils::PixelLength; 3 | use anyhow::Result; 4 | 5 | pub mod freetype; 6 | 7 | pub struct RasterizedGlyph { 8 | pub data: Vec, 9 | pub height: usize, 10 | pub width: usize, 11 | pub top: PixelLength, 12 | pub left: PixelLength, 13 | } 14 | 15 | #[derive(Copy, Clone, Debug, PartialEq)] 16 | pub struct FontMetrics { 17 | pub cell_width: PixelLength, 18 | pub cell_height: PixelLength, 19 | pub descender: PixelLength, 20 | pub underline_thickness: PixelLength, 21 | pub underline_position: PixelLength, 22 | } 23 | 24 | pub trait FontRasterizer { 25 | fn rasterize(&self, glyph_pos: u32) -> Result; 26 | } 27 | 28 | pub fn new_rasterizer(face: &ftwrap::Face) -> Result> { 29 | Ok(Box::new(freetype::FreeTypeRasterizer::new(face)?)) 30 | } 31 | -------------------------------------------------------------------------------- /src/font/shaper/harfbuzz.rs: -------------------------------------------------------------------------------- 1 | use crate::font::ftwrap; 2 | use crate::font::hbwrap as harfbuzz; 3 | use crate::font::shaper::{FontShaper, GlyphInfo}; 4 | use crate::utils::PixelLength; 5 | use anyhow::Result; 6 | use std::cell::RefCell; 7 | 8 | #[derive(Clone)] 9 | struct Info<'a> { 10 | codepoint: harfbuzz::hb_codepoint_t, 11 | pos: &'a harfbuzz::hb_glyph_position_t, 12 | } 13 | 14 | fn make_glyphinfo(info: &Info) -> GlyphInfo { 15 | GlyphInfo { 16 | glyph_pos: info.codepoint, 17 | x_advance: PixelLength::new(f64::from(info.pos.x_advance) / 64.0), 18 | y_advance: PixelLength::new(f64::from(info.pos.y_advance) / 64.0), 19 | x_offset: PixelLength::new(f64::from(info.pos.x_offset) / 64.0), 20 | y_offset: PixelLength::new(f64::from(info.pos.y_offset) / 64.0), 21 | } 22 | } 23 | 24 | pub struct HarfbuzzShaper { 25 | font: RefCell, 26 | } 27 | 28 | impl FontShaper for HarfbuzzShaper { 29 | fn shape(&self, text: &str) -> Result> { 30 | let features = vec![ 31 | harfbuzz::feature_from_string("kern")?, 32 | harfbuzz::feature_from_string("liga")?, 33 | harfbuzz::feature_from_string("clig")?, 34 | ]; 35 | let mut buf = harfbuzz::Buffer::new()?; 36 | buf.add_str(text); 37 | 38 | buf.guess_segment_properties(); 39 | let mut font = self.font.borrow_mut(); 40 | font.shape(&mut buf, features.as_slice()); 41 | 42 | let hb_infos = buf.glyph_infos(); 43 | let positions = buf.glyph_positions(); 44 | 45 | let mut cluster = Vec::new(); 46 | 47 | for (i, info) in hb_infos.iter().enumerate() { 48 | let info = Info { codepoint: info.codepoint, pos: &positions[i] }; 49 | let glyph = make_glyphinfo(&info); 50 | cluster.push(glyph); 51 | } 52 | 53 | Ok(cluster) 54 | } 55 | } 56 | 57 | impl HarfbuzzShaper { 58 | pub fn new(face: &ftwrap::Face) -> Result { 59 | let font = harfbuzz::Font::new(face.face); 60 | Ok(Self { font: RefCell::new(font) }) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/font/shaper/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::font::ftwrap; 2 | use crate::utils::PixelLength; 3 | use anyhow::Result; 4 | 5 | pub mod harfbuzz; 6 | 7 | #[derive(Clone, Debug)] 8 | pub struct GlyphInfo { 9 | pub glyph_pos: u32, 10 | pub x_advance: PixelLength, 11 | pub y_advance: PixelLength, 12 | pub x_offset: PixelLength, 13 | pub y_offset: PixelLength, 14 | } 15 | 16 | pub trait FontShaper { 17 | fn shape(&self, text: &str) -> Result>; 18 | } 19 | 20 | pub fn new_shaper(face: &ftwrap::Face) -> Result> { 21 | Ok(Box::new(harfbuzz::HarfbuzzShaper::new(face)?)) 22 | } 23 | -------------------------------------------------------------------------------- /src/glyph_atlas.rs: -------------------------------------------------------------------------------- 1 | use crate::bitmaps::atlas::{Atlas, Sprite}; 2 | use crate::bitmaps::{Image, Texture2d}; 3 | use crate::font::{GlyphInfo, RasterizedGlyph}; 4 | use crate::utils::PixelLength; 5 | use anyhow::Result; 6 | use glium::texture::SrgbTexture2d; 7 | use glium::Display; 8 | use std::rc::Rc; 9 | 10 | pub struct GlyphTexture { 11 | pub x_offset: PixelLength, 12 | pub y_offset: PixelLength, 13 | pub bearing_x: PixelLength, 14 | pub bearing_y: PixelLength, 15 | pub texture: Sprite, 16 | } 17 | 18 | pub struct GlyphAtlas { 19 | pub atlas: Atlas, 20 | } 21 | 22 | impl GlyphAtlas { 23 | pub fn new(backend: &Display, size: usize) -> Result { 24 | let surface = Rc::new(SrgbTexture2d::empty_with_format( 25 | backend, 26 | glium::texture::SrgbFormat::U8U8U8U8, 27 | glium::texture::MipmapsOption::NoMipmap, 28 | size as u32, 29 | size as u32, 30 | )?); 31 | let atlas = Atlas::new(&surface).expect("failed to create new texture atlas"); 32 | 33 | Ok(Self { atlas }) 34 | } 35 | } 36 | 37 | impl GlyphAtlas { 38 | pub fn load_glyph( 39 | &mut self, 40 | glyph: RasterizedGlyph, 41 | info: &GlyphInfo, 42 | ) -> Result> { 43 | let raw_im = Image::with_rgba32( 44 | glyph.width as usize, 45 | glyph.height as usize, 46 | 4 * glyph.width as usize, 47 | &glyph.data, 48 | ); 49 | 50 | let bearing_x = glyph.left; 51 | let bearing_y = glyph.top; 52 | let x_offset = info.x_offset; 53 | let y_offset = info.y_offset; 54 | 55 | let texture = self.atlas.allocate(&raw_im)?; 56 | 57 | let glyph = GlyphTexture { texture, x_offset, y_offset, bearing_x, bearing_y }; 58 | 59 | Ok(glyph) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/input.rs: -------------------------------------------------------------------------------- 1 | use crate::color::RgbColor; 2 | use crate::font::hbwrap as harfbuzz; 3 | use crate::language; 4 | use anyhow::Result; 5 | use serde::Deserialize; 6 | 7 | #[derive(Debug, Deserialize, Clone)] 8 | struct InputJson { 9 | font_size: usize, 10 | words: Vec, 11 | } 12 | 13 | #[derive(Debug, Deserialize, Clone)] 14 | struct WordJson { 15 | text: String, 16 | canvas_color: String, 17 | fg_color: String, 18 | bg_color: Option, 19 | bold: Option, 20 | italic: Option, 21 | } 22 | 23 | pub struct Input { 24 | pub config: Config, 25 | pub words: Vec, 26 | } 27 | 28 | #[derive(Debug, Deserialize, Clone)] 29 | pub struct Config { 30 | pub font_size: f64, 31 | pub dpi: u32, 32 | } 33 | 34 | #[derive(Clone)] 35 | pub struct Word { 36 | pub text: String, 37 | pub canvas_color: RgbColor, 38 | pub style: TextStyle, 39 | } 40 | 41 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 42 | pub struct TextStyle { 43 | pub fg_color: RgbColor, 44 | pub bg_color: Option, 45 | pub font_attributes: FontAttributes, 46 | } 47 | 48 | #[derive(Debug, Deserialize, Clone, PartialEq, Eq, Hash)] 49 | pub struct FontAttributes { 50 | pub family: String, 51 | pub bold: bool, 52 | pub italic: bool, 53 | } 54 | 55 | impl Input { 56 | pub fn new(path: &str) -> Result { 57 | let input_json = InputJson::parse(path)?; 58 | let mut words: Vec = Vec::new(); 59 | for word_json in input_json.words.iter() { 60 | let bg_color = 61 | word_json.bg_color.as_ref().map(|c| RgbColor::from_named_or_rgb_string(c).unwrap()); 62 | let mut buf = harfbuzz::Buffer::new()?; 63 | buf.add_str(&word_json.text); 64 | buf.guess_segment_properties(); 65 | let hb_script = buf.get_script(); 66 | words.push(Word { 67 | text: String::from(&word_json.text), 68 | canvas_color: RgbColor::from_named_or_rgb_string(&word_json.canvas_color).unwrap(), 69 | style: TextStyle { 70 | fg_color: RgbColor::from_named_or_rgb_string(&word_json.fg_color).unwrap(), 71 | bg_color, 72 | font_attributes: FontAttributes { 73 | family: language::get_font(&hb_script).into(), 74 | bold: word_json.bold.unwrap_or(false), 75 | italic: word_json.italic.unwrap_or(false), 76 | }, 77 | }, 78 | }); 79 | } 80 | 81 | Ok(Self { config: Config { font_size: input_json.font_size as f64, dpi: 96 }, words }) 82 | } 83 | } 84 | 85 | impl InputJson { 86 | fn parse(path: &str) -> Result { 87 | let data = std::fs::read_to_string(path)?; 88 | let input: InputJson = serde_json::from_str(&data)?; 89 | Ok(input) 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/language.rs: -------------------------------------------------------------------------------- 1 | use crate::font::hbwrap as harfbuzz; 2 | 3 | macro_rules! languages { 4 | ($( { $hb_script:ident, $font:literal } ),* ,) => { 5 | pub fn get_font(hb_script: &u32 ) -> &str { 6 | match *hb_script{ 7 | $( harfbuzz::$hb_script=> $font, )* 8 | _ => unimplemented!("font: Language {:?}", hb_script), 9 | } 10 | } 11 | }; 12 | } 13 | 14 | languages! { 15 | { HB_SCRIPT_LATIN, "Noto Sans" }, 16 | { HB_SCRIPT_ARABIC, "Noto Sans Arabic" }, 17 | { HB_SCRIPT_HAN, "Noto Sans SC" }, 18 | { HB_SCRIPT_KATAKANA, "Noto Sans JP" }, 19 | { HB_SCRIPT_CYRILLIC, "Noto Sans" }, 20 | { HB_SCRIPT_DEVANAGARI, "Noto Sans Devanagari" }, 21 | { HB_SCRIPT_THAI, "Noto Sans Thai" }, 22 | { HB_SCRIPT_BENGALI, "Hind Siliguri" }, 23 | } 24 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate glium; 3 | 4 | use anyhow::Result; 5 | use clap::{crate_description, crate_name, crate_version, AppSettings, Arg}; 6 | use font::FontConfiguration; 7 | use glium::glutin::dpi::LogicalSize; 8 | use glium::glutin::event::Event; 9 | use glium::glutin::event::StartCause; 10 | use glium::glutin::event::WindowEvent; 11 | use glium::glutin::event_loop::ControlFlow; 12 | use glium::glutin::event_loop::EventLoop; 13 | use glium::glutin::window::WindowBuilder; 14 | use glium::glutin::ContextBuilder; 15 | use glium::{BlendingFunction, Display, Frame, LinearBlendingFactor, Surface}; 16 | use input::{Input, Word}; 17 | use render_state::RenderState; 18 | use std::cell::RefCell; 19 | use std::rc::Rc; 20 | use std::time::{Duration, Instant}; 21 | 22 | mod bitmaps; 23 | mod color; 24 | mod font; 25 | mod glyph_atlas; 26 | mod input; 27 | mod language; 28 | mod render_state; 29 | mod utils; 30 | 31 | const FPS: u32 = 60; 32 | static DEFAULT_INPUT_FILE: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/examples/0.json"); 33 | 34 | fn run(input_path: &str, frequency: u32) -> Result<()> { 35 | let event_loop = EventLoop::new(); 36 | let (window_width, window_height) = (720., 405.); 37 | let wb = WindowBuilder::new().with_inner_size(LogicalSize::new(window_width, window_height)); 38 | let cb = ContextBuilder::new(); 39 | let display = Display::new(wb, cb, &event_loop)?; 40 | let input = Rc::new(Input::new(input_path)?); 41 | let fontconfig = Rc::new(FontConfiguration::new(input.config.font_size, input.config.dpi)?); 42 | let render_state = RefCell::new(RenderState::new(&display)?); 43 | let mut frame_count = 0; 44 | let mut count = 0; 45 | event_loop.run(move |event, _, control_flow| { 46 | match event { 47 | Event::WindowEvent { event, .. } => match event { 48 | WindowEvent::CloseRequested => { 49 | *control_flow = ControlFlow::Exit; 50 | return; 51 | } 52 | _ => return, 53 | }, 54 | Event::NewEvents(cause) => match cause { 55 | StartCause::ResumeTimeReached { .. } => (), 56 | StartCause::Init => (), 57 | _ => return, 58 | }, 59 | _ => return, 60 | } 61 | let next_frame_time = Instant::now() + Duration::from_micros(1_000_000 / FPS as u64); 62 | *control_flow = ControlFlow::WaitUntil(next_frame_time); 63 | let mut target = display.draw(); 64 | 65 | paint_screen( 66 | &fontconfig, 67 | &render_state, 68 | &display, 69 | &mut target, 70 | &input.words, 71 | window_width, 72 | window_height, 73 | &mut count, 74 | frame_count, 75 | frequency, 76 | ) 77 | .unwrap(); 78 | target.finish().unwrap(); 79 | 80 | frame_count += 1; 81 | }); 82 | } 83 | 84 | fn paint_screen( 85 | fontconfig: &Rc, 86 | render_state: &RefCell, 87 | display: &Display, 88 | frame: &mut Frame, 89 | words: &[Word], 90 | window_width: f64, 91 | window_height: f64, 92 | count: &mut u32, 93 | frame_count: u32, 94 | frequency: u32, 95 | ) -> Result<()> { 96 | let mut gl_state = render_state.borrow_mut(); 97 | let projection = euclid::Transform3D::::ortho( 98 | -(window_width as f32) / 2.0, 99 | window_width as f32 / 2.0, 100 | window_height as f32 / 2.0, 101 | -(window_height as f32) / 2.0, 102 | -1.0, 103 | 1.0, 104 | ) 105 | .to_arrays(); 106 | 107 | let draw_params_with_alpha = glium::DrawParameters { 108 | blend: glium::Blend { 109 | color: BlendingFunction::Addition { 110 | source: LinearBlendingFactor::SourceAlpha, 111 | destination: LinearBlendingFactor::OneMinusSourceAlpha, 112 | }, 113 | alpha: BlendingFunction::Addition { 114 | source: LinearBlendingFactor::One, 115 | destination: LinearBlendingFactor::OneMinusSourceAlpha, 116 | }, 117 | constant_value: (0.0, 0.0, 0.0, 0.0), 118 | }, 119 | 120 | ..Default::default() 121 | }; 122 | 123 | if frame_count % (60 / frequency) == 0 { 124 | let idx = *count as usize % words.len(); 125 | let w = &words[idx]; 126 | gl_state.word = Some(w.clone()); 127 | gl_state.compute_glyph_vertices(display, fontconfig)?; 128 | *count += 1; 129 | } 130 | 131 | gl_state.compute_bg_vertices(display, window_width, window_height)?; 132 | 133 | frame.draw( 134 | gl_state.bg_vertex_buffer.as_ref().unwrap(), 135 | gl_state.bg_index_buffer.as_ref().unwrap(), 136 | &gl_state.glyph_program, 137 | &uniform! { 138 | projection: projection, 139 | draw_bg: true 140 | }, 141 | &draw_params_with_alpha, 142 | )?; 143 | 144 | if gl_state.word.as_ref().unwrap().style.bg_color.is_some() { 145 | frame.draw( 146 | gl_state.glyph_bg_vertex_buffer.as_ref().unwrap(), 147 | gl_state.glyph_bg_index_buffer.as_ref().unwrap(), 148 | &gl_state.glyph_program, 149 | &uniform! { 150 | projection: projection, 151 | draw_bg: true 152 | }, 153 | &Default::default(), 154 | )?; 155 | } 156 | 157 | let tex = gl_state.glyph_atlas.atlas.texture(); 158 | 159 | frame.draw( 160 | gl_state.glyph_vertex_buffer.as_ref().unwrap(), 161 | gl_state.glyph_index_buffer.as_ref().unwrap(), 162 | &gl_state.glyph_program, 163 | &uniform! { 164 | projection: projection, 165 | glyph_tex: &*tex, 166 | draw_bg: false 167 | }, 168 | &draw_params_with_alpha, 169 | )?; 170 | 171 | Ok(()) 172 | } 173 | 174 | fn main() -> Result<()> { 175 | let matches = clap::Command::new(crate_name!()) 176 | .version(crate_version!()) 177 | .about(crate_description!()) 178 | .setting(AppSettings::DeriveDisplayOrder) 179 | .arg( 180 | Arg::new("input") 181 | .short('i') 182 | .long("input") 183 | .help("Which input to use.") 184 | .takes_value(true), 185 | ) 186 | .arg( 187 | Arg::new("frequency") 188 | .short('f') 189 | .long("frequency") 190 | .default_value("6") 191 | .help("frequency in frame per second.") 192 | .takes_value(true) 193 | .validator(|t| match t.parse::() { 194 | Ok(_) => Ok(()), 195 | Err(_) => Err(String::from("must be a number")), 196 | }), 197 | ) 198 | .get_matches(); 199 | 200 | let input_path = matches.value_of("input").unwrap_or(DEFAULT_INPUT_FILE); 201 | let frequency: u32 = matches.value_of("frequency").unwrap().parse()?; 202 | run(input_path, frequency)?; 203 | Ok(()) 204 | } 205 | -------------------------------------------------------------------------------- /src/render_state.rs: -------------------------------------------------------------------------------- 1 | use crate::color; 2 | use crate::font::FontConfiguration; 3 | use crate::glyph_atlas::GlyphAtlas; 4 | use crate::input::Word; 5 | use anyhow::Result; 6 | use glium::texture::SrgbTexture2d; 7 | use glium::Display; 8 | use glium::Program; 9 | use glium::{IndexBuffer, VertexBuffer}; 10 | 11 | const PADDING: f32 = 15.; 12 | 13 | const ATLAS_SIZE: usize = 8192; 14 | 15 | static GLYPH_VERTEX_SHADER: &str = 16 | include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/assets/shaders/g_vertex.glsl")); 17 | 18 | static GLYPH_FRAGMENT_SHADER: &str = 19 | include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/assets/shaders/g_fragment.glsl")); 20 | 21 | pub const V_TOP_LEFT: usize = 0; 22 | pub const V_TOP_RIGHT: usize = 1; 23 | pub const V_BOT_LEFT: usize = 2; 24 | pub const V_BOT_RIGHT: usize = 3; 25 | 26 | #[derive(Copy, Clone, Default)] 27 | pub struct Vertex { 28 | pub position: (f32, f32), 29 | pub tex: (f32, f32), 30 | pub fg_color: (f32, f32, f32, f32), 31 | pub bg_color: (f32, f32, f32, f32), 32 | } 33 | 34 | implement_vertex!(Vertex, position, tex, fg_color, bg_color); 35 | 36 | pub struct RenderState { 37 | pub glyph_atlas: GlyphAtlas, 38 | pub glyph_program: Program, 39 | pub glyph_vertex_buffer: Option>, 40 | pub glyph_index_buffer: Option>, 41 | pub glyph_bg_vertex_buffer: Option>, 42 | pub glyph_bg_index_buffer: Option>, 43 | pub bg_vertex_buffer: Option>, 44 | pub bg_index_buffer: Option>, 45 | pub word: Option, 46 | } 47 | 48 | impl RenderState { 49 | pub fn new(display: &Display) -> Result { 50 | let glyph_program = compile_shaders(display)?; 51 | let glyph_atlas = GlyphAtlas::new(display, ATLAS_SIZE)?; 52 | Ok(Self { 53 | glyph_atlas, 54 | glyph_program, 55 | glyph_vertex_buffer: None, 56 | glyph_index_buffer: None, 57 | glyph_bg_vertex_buffer: None, 58 | glyph_bg_index_buffer: None, 59 | bg_vertex_buffer: None, 60 | bg_index_buffer: None, 61 | word: None, 62 | }) 63 | } 64 | 65 | pub fn compute_glyph_vertices( 66 | &mut self, 67 | display: &Display, 68 | fontconfig: &FontConfiguration, 69 | ) -> Result<()> { 70 | self.compute_g_vertices(display, fontconfig)?; 71 | 72 | if let Some(bg_color) = self.word.as_ref().unwrap().style.bg_color { 73 | self.compute_bg_g_vertices(bg_color, display)?; 74 | } 75 | Ok(()) 76 | } 77 | 78 | pub fn compute_g_vertices( 79 | &mut self, 80 | display: &Display, 81 | fontconfig: &FontConfiguration, 82 | ) -> Result<()> { 83 | let mut verts = Vec::new(); 84 | let mut indices = Vec::new(); 85 | let word = self.word.as_ref().unwrap(); 86 | let fg_color = color::to_tuple_rgba(word.style.fg_color); 87 | 88 | let font = fontconfig.get_font(&word.style)?; 89 | let glyph_infos = font.shape(&word.text)?; 90 | let width = glyph_infos.iter().fold(0., |acc, info| acc + info.x_advance.get() as f32); 91 | let mut x = -width / 2.; 92 | let mut y = 0.; 93 | for glyph_info in &glyph_infos { 94 | let rasterized_glyph = font.rasterize(glyph_info.glyph_pos)?; 95 | let glyph = self.glyph_atlas.load_glyph(rasterized_glyph, glyph_info)?; 96 | 97 | let x0 = x + (glyph.x_offset + glyph.bearing_x).get() as f32; 98 | let y0 = y - (glyph.y_offset + glyph.bearing_y).get() as f32; 99 | 100 | let x1 = x0 + glyph.texture.width as f32; 101 | let y1 = y0 + glyph.texture.height as f32; 102 | 103 | x += glyph_info.x_advance.get() as f32; 104 | y += glyph_info.y_advance.get() as f32; 105 | let idx = verts.len() as u32; 106 | verts.push(Vertex { 107 | position: (x0, y0), 108 | tex: (glyph.texture.tex_coords.min_x(), glyph.texture.tex_coords.min_y()), 109 | fg_color, 110 | ..Default::default() 111 | }); 112 | verts.push(Vertex { 113 | position: (x1, y0), 114 | tex: (glyph.texture.tex_coords.max_x(), glyph.texture.tex_coords.min_y()), 115 | fg_color, 116 | ..Default::default() 117 | }); 118 | verts.push(Vertex { 119 | position: (x0, y1), 120 | tex: (glyph.texture.tex_coords.min_x(), glyph.texture.tex_coords.max_y()), 121 | fg_color, 122 | ..Default::default() 123 | }); 124 | verts.push(Vertex { 125 | position: (x1, y1), 126 | tex: (glyph.texture.tex_coords.max_x(), glyph.texture.tex_coords.max_y()), 127 | fg_color, 128 | ..Default::default() 129 | }); 130 | 131 | indices.push(idx + V_TOP_LEFT as u32); 132 | indices.push(idx + V_TOP_RIGHT as u32); 133 | indices.push(idx + V_BOT_LEFT as u32); 134 | 135 | indices.push(idx + V_TOP_RIGHT as u32); 136 | indices.push(idx + V_BOT_LEFT as u32); 137 | indices.push(idx + V_BOT_RIGHT as u32); 138 | } 139 | 140 | self.glyph_vertex_buffer = Some(VertexBuffer::dynamic(display, &verts)?); 141 | self.glyph_index_buffer = 142 | Some(IndexBuffer::new(display, glium::index::PrimitiveType::TrianglesList, &indices)?); 143 | Ok(()) 144 | } 145 | 146 | pub fn compute_bg_g_vertices( 147 | &mut self, 148 | bg_color: color::RgbColor, 149 | display: &Display, 150 | ) -> Result<()> { 151 | let bg_color = color::to_tuple_rgba(bg_color); 152 | let mut verts = Vec::new(); 153 | let mut indices = Vec::new(); 154 | let (mut top, mut left, mut bottom, mut right) = (0f32, 0f32, 0f32, 0f32); 155 | let glyph_vertex_buffer = self.glyph_vertex_buffer.as_mut().unwrap(); 156 | let g_verts = glyph_vertex_buffer.slice_mut(..).unwrap().map_read(); 157 | for v in g_verts.iter() { 158 | left = left.min(v.position.0); 159 | right = right.max(v.position.0); 160 | top = top.min(v.position.1); 161 | bottom = bottom.max(v.position.1); 162 | } 163 | 164 | left -= PADDING as f32; 165 | right += PADDING as f32; 166 | top -= PADDING as f32; 167 | bottom += PADDING as f32; 168 | 169 | verts.push(Vertex { position: (left, top), bg_color, ..Default::default() }); 170 | verts.push(Vertex { position: (right, top), bg_color, ..Default::default() }); 171 | verts.push(Vertex { position: (left, bottom), bg_color, ..Default::default() }); 172 | verts.push(Vertex { position: (right, bottom), bg_color, ..Default::default() }); 173 | 174 | indices.push(V_TOP_LEFT as u32); 175 | indices.push(V_TOP_RIGHT as u32); 176 | indices.push(V_BOT_LEFT as u32); 177 | 178 | indices.push(V_TOP_RIGHT as u32); 179 | indices.push(V_BOT_LEFT as u32); 180 | indices.push(V_BOT_RIGHT as u32); 181 | 182 | self.glyph_bg_vertex_buffer = Some(VertexBuffer::dynamic(display, &verts)?); 183 | self.glyph_bg_index_buffer = 184 | Some(IndexBuffer::new(display, glium::index::PrimitiveType::TrianglesList, &indices)?); 185 | Ok(()) 186 | } 187 | 188 | pub fn compute_bg_vertices( 189 | &mut self, 190 | display: &Display, 191 | window_width: f64, 192 | window_height: f64, 193 | ) -> Result<()> { 194 | let canvas_color = self.word.as_ref().unwrap().canvas_color; 195 | let bg_color = color::to_tuple_rgba(canvas_color); 196 | let mut verts = Vec::new(); 197 | let mut indices = Vec::new(); 198 | let (w, h) = (window_width as f32 / 2., window_height as f32 / 2.); 199 | 200 | verts.push(Vertex { position: (-w, -h), bg_color, ..Default::default() }); 201 | verts.push(Vertex { position: (w, -h), bg_color, ..Default::default() }); 202 | verts.push(Vertex { position: (-w, h), bg_color, ..Default::default() }); 203 | verts.push(Vertex { position: (w, h), bg_color, ..Default::default() }); 204 | 205 | indices.push(V_TOP_LEFT as u32); 206 | indices.push(V_TOP_RIGHT as u32); 207 | indices.push(V_BOT_LEFT as u32); 208 | 209 | indices.push(V_TOP_RIGHT as u32); 210 | indices.push(V_BOT_LEFT as u32); 211 | indices.push(V_BOT_RIGHT as u32); 212 | 213 | self.bg_vertex_buffer = Some(VertexBuffer::dynamic(display, &verts)?); 214 | self.bg_index_buffer = 215 | Some(IndexBuffer::new(display, glium::index::PrimitiveType::TrianglesList, &indices)?); 216 | 217 | Ok(()) 218 | } 219 | } 220 | 221 | fn compile_shaders(display: &Display) -> Result { 222 | let glyph_source = glium::program::ProgramCreationInput::SourceCode { 223 | vertex_shader: GLYPH_VERTEX_SHADER, 224 | fragment_shader: GLYPH_FRAGMENT_SHADER, 225 | outputs_srgb: true, 226 | tessellation_control_shader: None, 227 | tessellation_evaluation_shader: None, 228 | transform_feedback_varyings: None, 229 | uses_point_size: false, 230 | geometry_shader: None, 231 | }; 232 | let glyph_program = glium::Program::new(display, glyph_source)?; 233 | 234 | Ok(glyph_program) 235 | } 236 | -------------------------------------------------------------------------------- /src/utils.rs: -------------------------------------------------------------------------------- 1 | pub struct PixelUnit; 2 | pub type PixelLength = euclid::Length; 3 | pub type Point = euclid::Point2D; 4 | pub type Rect = euclid::Rect; 5 | pub type Size = euclid::Size2D; 6 | --------------------------------------------------------------------------------