1 ! Copyright (C) 2021 John Benediktsson
2 ! See http://factorcode.org/license.txt for BSD license
4 USING: accessors colors combinators generalizations io io.binary
5 io.encodings.binary io.files io.streams.byte-array kernel math
6 math.bitwise namespaces sequences ;
12 : read-varuint ( -- n )
15 [ 0x7f bitand rot [ 7 * shift bitor ] keep 1 + swap ]
16 [ 0x80 bitand zero? not ] bi
19 : write-varuint ( n -- )
21 [ 0x7f bitand 0x80 bitor write1 ] [ -7 shift ] bi
24 : read-float32 ( -- n )
25 4 read le> bits>float ;
27 : write-float32 ( n -- )
28 float>bits 4 >le write ;
30 ERROR: invalid-length n ;
32 : write-length ( n -- )
33 dup 1 < [ invalid-length ] when 1 - write-varuint ;
37 CONSTANT: tinyvg-magic B{ 0x72 0x56 }
39 CONSTANT: tinyvg-version 1
41 TUPLE: tinyvg-header scale color-encoding coordinate-range width height color-count ;
43 : read-tinyvg-header ( -- header )
44 2 read tinyvg-magic assert=
45 read1 tinyvg-version assert=
46 read1 [ 4 bits ] [ -4 shift 2 bits ] [ -6 shift ] tri
47 dup { 2 1 4 } nth '[ _ read le> ] 2 call-n
48 read-varuint tinyvg-header boa ;
50 : write-tinyvg-header ( header -- )
52 tinyvg-version write1 {
54 [ color-encoding>> 4 shift bitor ]
55 [ coordinate-range>> 6 shift bitor write1 ]
58 [ coordinate-range>> { 2 1 4 } nth '[ _ >le write ] bi@ ]
59 [ color-count>> write-varuint ]
64 : read-rgba-8888 ( -- rgba )
65 [ read1 255 /f ] 4 call-n <rgba> ;
67 : write-rgba-8888 ( rgba -- )
68 >rgba-components [ 255 * >integer write1 ] 4 napply ;
70 : read-rgb-565 ( -- rgba )
73 [ -5 shift 6 bits 63 /f ]
74 [ -11 shift 5 bits 31 /f ] tri
77 : write-rgb-565 ( rgba -- )
78 >rgba-components drop {
80 [ 63 * >integer 5 shift bitor ]
81 [ 31 * >integer 11 shift bitor ]
82 } spread 2 >le write ;
84 : read-rgba-f32 ( -- rgba )
85 [ read-float32 ] 4 call-n <rgba> ;
87 : write-rgba-f32 ( rgba -- )
88 >rgba-components [ write-float32 ] 4 napply ;
90 SYMBOL: color-encoding
92 : read-color ( -- color )
94 { 0 [ read-rgba-8888 ] }
95 { 1 [ read-rgb-565 ] }
96 { 2 [ read-rgba-f32 ] }
97 { 3 [ "unsupported color encoding" throw ] }
100 : write-color ( color -- )
102 { 0 [ write-rgba-8888 ] }
103 { 1 [ write-rgb-565 ] }
104 { 2 [ write-rgba-f32 ] }
105 { 3 [ "unsupported color encoding" throw ] }
112 : read-color-table ( color-count -- color-table )
113 [ read-color ] replicate ;
115 ERROR: invalid-color color-index ;
117 : check-color ( color-index -- color-index )
118 dup color-table get length <= [ invalid-color ] unless ;
120 : read-color-index ( -- color-index )
121 read-varuint check-color ;
125 SYMBOL: coordinate-range
127 : coordinate-bytes ( -- n )
128 coordinate-range get { 2 1 4 } nth ;
133 coordinate-bytes read le> scale-factor get /f ;
135 : write-unit ( n -- )
136 scale-factor get * >integer coordinate-bytes >le write ;
144 : read-point ( -- point )
145 [ read-unit ] 2 call-n <point> ;
147 : read-points ( n -- rectangles )
148 1 + [ read-point ] replicate ;
150 : write-point ( point -- )
151 [ x>> write-unit ] [ y>> write-unit ] bi ;
155 TUPLE: rectangle x y width height ;
157 C: <rectangle> rectangle
159 : read-rectangle ( -- rectangle )
160 [ read-unit ] 4 call-n <rectangle> ;
162 : read-rectangles ( n -- rectangles )
163 1 + [ read-rectangle ] replicate ;
165 : write-rectangle ( rectangle -- )
169 [ width>> write-unit ]
170 [ height>> write-unit ]
175 TUPLE: line start end ;
179 : read-line ( -- line )
180 [ read-point ] 2 call-n <line> ;
182 : read-lines ( n -- rectangles )
183 1 + [ read-line ] replicate ;
185 : write-line ( line -- )
186 [ start>> write-point ] [ end>> write-point ] bi ;
190 TUPLE: flat-colored color-index ;
192 C: <flat-colored> flat-colored
194 : read-flat-colored ( -- style )
195 read-color-index <flat-colored> ;
197 TUPLE: gradient point0 point1 color-index0 color-index1 ;
199 TUPLE: linear-gradient < gradient ;
201 TUPLE: radial-gradient < gradient ;
203 : read-gradient ( class -- style )
204 [ [ read-point ] 2 call-n [ read-color-index ] 2 call-n ] dip boa ; inline
206 : read-style ( style-kind -- style )
208 { 0 [ read-flat-colored ] }
209 { 1 [ linear-gradient read-gradient ] }
210 { 2 [ radial-gradient read-gradient ] }
213 GENERIC: write-style ( style -- )
215 M: flat-colored write-style
216 color-index>> write-varuint ;
218 M: gradient write-style
220 [ point0>> write-point ]
221 [ point1>> write-point ]
222 [ color-index0>> write-varuint ]
223 [ color-index1>> write-varuint ]
226 : write-style-kind ( style n -- )
228 { [ dup flat-colored? ] [ drop 0 ] }
229 { [ dup linear-gradient? ] [ drop 1 ] }
230 { [ dup radial-gradient? ] [ drop 2 ] }
231 } cond 6 shift bitor write1 ;
239 TUPLE: fill fill-style ;
241 : read-fill ( style-kind -- style count )
242 read-varuint [ read-style ] dip ;
244 TUPLE: fill-polygon < fill polygon ;
246 C: <fill-polygon> fill-polygon
248 : read-fill-polygon ( style-kind -- command )
249 read-fill read-points <fill-polygon> ;
251 TUPLE: fill-rectangles < fill rectangles ;
253 C: <fill-rectangles> fill-rectangles
255 : read-fill-rectangles ( style-kind -- command )
256 read-fill read-rectangles <fill-rectangles> ;
258 TUPLE: fill-path < fill path ;
260 C: <fill-path> fill-path
262 : read-fill-path ( style-kind -- command )
263 read-fill read-path <fill-path> ;
265 TUPLE: draw-line line-style line-width ;
267 : read-draw-line ( style-kind -- line-style line-width count )
268 read-varuint [ read-style read-unit ] dip ;
270 TUPLE: draw-lines < draw-line lines ;
272 C: <draw-lines> draw-lines
274 : read-draw-lines ( style-kind -- command )
275 read-draw-line read-lines <draw-lines> ;
277 TUPLE: draw-line-loop < draw-line points ;
279 C: <draw-line-loop> draw-line-loop
281 : read-draw-line-loop ( style-kind -- command )
282 read-draw-line read-points <draw-line-loop> ;
284 TUPLE: draw-line-strip < draw-line points ;
286 C: <draw-line-strip> draw-line-strip
288 : read-draw-line-strip ( style-kind -- command )
289 read-draw-line read-points <draw-line-strip> ;
291 TUPLE: draw-line-path < draw-line path ;
293 C: <draw-line-path> draw-line-path
295 : read-draw-line-path ( style-kind -- command )
296 read-draw-line read-path <draw-line-path> ;
298 TUPLE: outline-fill fill-style line-style line-width ;
300 : read-outline-fill ( style-kind -- fill-style line-style line-width count )
301 read1 [ -6 shift ] [ 6 bits ] bi
302 [ [ read-style ] bi@ read-unit ] dip ;
304 TUPLE: outline-fill-polygon < outline-fill points ;
306 C: <outline-fill-polygon> outline-fill-polygon
308 : read-outline-fill-polygon ( style-kind -- command )
309 read-outline-fill read-points <outline-fill-polygon> ;
311 TUPLE: outline-fill-rectangles < outline-fill rectangles ;
313 C: <outline-fill-rectangles> outline-fill-rectangles
315 : read-outline-fill-rectangles ( style-kind -- command )
316 read-outline-fill read-rectangles <outline-fill-rectangles> ;
318 TUPLE: outline-fill-path < outline-fill path ;
320 C: <outline-fill-path> outline-fill-path
322 : read-outline-fill-path ( style-kind -- command )
323 read-outline-fill read-path <outline-fill-path> ;
325 : read-command ( -- command/f )
326 read1 [ -6 shift ] [ 6 bits ] bi {
327 { 0 [ 0 assert= f ] } ! end-of-document
328 { 1 [ read-fill-polygon ] }
329 { 2 [ read-fill-rectangles ] }
330 { 3 [ read-fill-path ] }
331 { 4 [ read-draw-lines ] }
332 { 5 [ read-draw-line-loop ] }
333 { 6 [ read-draw-line-strip ] }
334 { 7 [ read-draw-line-path ] }
335 { 8 [ read-outline-fill-polygon ] }
336 { 9 [ read-outline-fill-rectangles ] }
337 { 10 [ read-outline-fill-path ] }
340 : read-commands ( -- commands )
341 [ read-command dup ] [ ] produce nip ;
343 GENERIC: write-command ( command -- )
345 M: fill-polygon write-command
347 [ fill-style>> 1 write-style-kind ]
348 [ polygon>> length write-length ]
349 [ fill-style>> write-style ]
350 [ polygon>> [ write-point ] each ]
353 M: fill-rectangles write-command
355 [ fill-style>> 2 write-style-kind ]
356 [ rectangles>> length write-length ]
357 [ fill-style>> write-style ]
358 [ rectangles>> [ write-rectangle ] each ]
361 M: fill-path write-command
363 [ fill-style>> 3 write-style-kind ]
364 [ path>> segments>> length write-length ]
365 [ fill-style>> write-style ]
366 [ path>> write-path ]
369 M: draw-lines write-command
371 [ line-style>> 4 write-style-kind ]
372 [ lines>> length write-length ]
373 [ line-style>> write-style ]
374 [ line-width>> write-unit ]
375 [ lines>> [ write-line ] each ]
378 M: draw-line-loop write-command
380 [ line-style>> 5 write-style-kind ]
381 [ points>> length write-length ]
382 [ line-style>> write-style ]
383 [ line-width>> write-unit ]
384 [ points>> [ write-point ] each ]
387 M: draw-line-strip write-command
389 [ line-style>> 6 write-style-kind ]
390 [ points>> length write-length ]
391 [ line-style>> write-style ]
392 [ line-width>> write-unit ]
393 [ points>> [ write-point ] each ]
396 M: draw-line-path write-command
398 [ line-style>> 7 write-style-kind ]
399 [ path>> segments>> length write-length ]
400 [ line-style>> write-style ]
401 [ line-width>> write-unit ]
402 [ path>> write-path ]
405 M: outline-fill-polygon write-command
407 [ fill-style>> 8 write-style-kind ]
408 [ [ line-style>> ] [ points>> length 1 - ] bi write-style-kind ]
409 [ fill-style>> write-style ]
410 [ line-style>> write-style ]
411 [ line-width>> write-unit ]
412 [ points>> [ write-point ] each ]
415 M: outline-fill-rectangles write-command
417 [ fill-style>> 9 write-style-kind ]
418 [ [ line-style>> ] [ rectangles>> length 1 - ] bi write-style-kind ]
419 [ fill-style>> write-style ]
420 [ line-style>> write-style ]
421 [ line-width>> write-unit ]
422 [ rectangles>> [ write-rectangle ] each ]
425 M: outline-fill-path write-command
427 [ fill-style>> 10 write-style-kind ]
428 [ [ line-style>> ] [ path>> segments>> length 1 - ] bi write-style-kind ]
429 [ fill-style>> write-style ]
430 [ line-style>> write-style ]
431 [ line-width>> write-unit ]
432 [ path>> write-path ]
435 : write-commands ( commands -- )
436 [ write-command ] each 0 write1 ;
440 TUPLE: instruction line-width ;
442 TUPLE: diagonal-line < instruction position ;
444 C: <diagonal-line> diagonal-line
446 TUPLE: horizontal-line < instruction x ;
448 C: <horizontal-line> horizontal-line
450 TUPLE: vertical-line < instruction y ;
452 C: <vertical-line> vertical-line
454 TUPLE: cubic-bezier < instruction control0 control1 point1 ;
456 C: <cubic-bezier> cubic-bezier
458 TUPLE: arc < instruction large-arc? sweep? ;
460 TUPLE: arc-circle < arc radius target ;
462 C: <arc-circle> arc-circle
464 TUPLE: arc-ellipse < arc radius-x radius-y rotation target ;
466 C: <arc-ellipse> arc-ellipse
468 TUPLE: close-path < instruction ;
470 C: <close-path> close-path
472 TUPLE: quadratic-bezier < instruction control point1 ;
474 C: <quadratic-bezier> quadratic-bezier
476 : read-tag ( -- line-width/f tag )
477 read1 [ 4 bit? ] [ 3 bits ] bi [ [ read-unit ] [ f ] if ] dip ;
479 : read-arc ( -- large-arc? sweep? )
480 read1 [ 0 bit? ] [ 1 bit? ] bi ;
482 : read-instruction ( -- instruction )
484 { 0 [ read-point <diagonal-line> ] }
485 { 1 [ read-unit <horizontal-line> ] }
486 { 2 [ read-unit <vertical-line> ] }
487 { 3 [ [ read-point ] 3 call-n <cubic-bezier> ] }
488 { 4 [ read-arc read-unit read-point <arc-circle> ] }
489 { 5 [ read-arc [ read-unit ] 3 call-n read-point <arc-ellipse> ] }
490 { 6 [ <close-path> ] }
491 { 7 [ [ read-point ] 2 call-n <quadratic-bezier> ] }
494 : read-instructions ( n -- instructions )
495 1 + [ read-instruction ] replicate ;
497 : write-tag ( instruction n -- )
499 [ [ 4 set-bit ] when write1 ]
500 [ [ write-unit ] when* ] bi ;
502 : write-arc ( instruction -- )
503 [ large-arc?>> 0b1 0b0 ? ] [ sweep?>> [ 0b10 bitor ] when ] bi write1 ;
505 GENERIC: write-instruction ( instruction -- )
507 M: diagonal-line write-instruction
508 [ 0 write-tag ] [ position>> write-point ] bi ;
510 M: horizontal-line write-instruction
511 [ 1 write-tag ] [ x>> write-unit ] bi ;
513 M: vertical-line write-instruction
514 [ 2 write-tag ] [ y>> write-unit ] bi ;
516 M: cubic-bezier write-instruction
519 [ control0>> write-point ]
520 [ control1>> write-point ]
521 [ point1>> write-point ]
524 M: arc-circle write-instruction
528 [ radius>> write-unit ]
529 [ target>> write-point ]
532 M: arc-ellipse write-instruction
536 [ radius-x>> write-unit ]
537 [ radius-y>> write-unit ]
538 [ rotation>> write-unit ]
539 [ target>> write-point ]
542 M: close-path write-instruction
545 M: quadratic-bezier write-instruction
546 [ 7 write-tag ] [ control>> write-point ] [ point1>> write-point ] tri ;
550 TUPLE: segment start instructions ;
554 : read-segment ( n -- segment )
555 read-point swap read-instructions segment boa ;
557 : read-segments ( n -- segments )
558 1 + [ read-varuint ] replicate [ read-segment ] map ;
560 : write-segment ( segment -- )
561 [ start>> write-point ] [ instructions>> [ write-instruction ] each ] bi ;
563 : write-segments ( segments -- )
564 [ [ instructions>> length write-length ] each ]
565 [ [ write-segment ] each ] bi ;
569 TUPLE: path segments ;
573 : read-path ( segment-count -- path )
574 read-segments path boa ;
576 : write-path ( path -- )
577 segments>> write-segments ;
581 TUPLE: tinyvg header color-table commands ;
585 : read-tinyvg ( -- tinyvg )
588 dup scale>> 2^ scale-factor set
589 dup color-encoding>> color-encoding set
590 dup coordinate-range>> coordinate-range set
591 dup color-count>> read-color-table dup color-table set
596 : path>tinyvg ( path -- tinyvg )
597 binary [ read-tinyvg ] with-file-reader ;
599 : bytes>tinyvg ( byte-array -- tinyvg )
600 binary [ read-tinyvg ] with-byte-reader ;
602 : write-tinyvg ( tinyvg -- )
605 [ header>> write-tinyvg-header ]
606 [ header>> scale>> 2^ scale-factor set ]
607 [ header>> color-encoding>> color-encoding set ]
608 [ header>> coordinate-range>> coordinate-range set ]
609 [ color-table>> color-table set ]
610 [ color-table>> [ write-color ] each ]
611 [ commands>> write-commands ]
615 : tinyvg>path ( tinyvg path -- )
616 binary [ write-tinyvg ] with-file-writer ;
618 : tinyvg>bytes ( tinyvg -- byte-array )
619 binary [ write-tinyvg ] with-byte-writer ;