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 :: write-fill ( command n seq -- )
346 command fill-style>> n write-style-kind
347 seq length write-length
348 command fill-style>> write-style ; inline
350 M: fill-polygon write-command
351 1 over polygon>> [ write-fill ] [ [ write-point ] each ] bi ;
353 M: fill-rectangles write-command
354 2 over rectangles>> [ write-fill ] [ [ write-rectangle ] each ] bi ;
356 M: fill-path write-command
357 3 over path>> [ write-fill ] [ write-path ] bi ;
359 :: write-draw-line ( command n seq -- )
360 command line-style>> n write-style-kind
361 seq length write-length
362 command line-style>> write-style
363 command line-width>> write-unit ; inline
365 M: draw-lines write-command
366 4 over lines>> [ write-draw-line ] [ [ write-line ] each ] bi ;
368 M: draw-line-loop write-command
369 5 over points>> [ write-draw-line ] [ [ write-point ] each ] bi ;
371 M: draw-line-strip write-command
372 6 over points>> [ write-draw-line ] [ [ write-point ] each ] bi ;
374 M: draw-line-path write-command
375 7 over path>> [ write-draw-line ] [ write-path ] bi ;
377 :: write-outline-fill ( command n seq -- )
378 command fill-style>> n write-style-kind
379 command line-style>> seq length 1 - write-style-kind
380 command fill-style>> write-style
381 command line-style>> write-style
382 command line-width>> write-unit ; inline
384 M: outline-fill-polygon write-command
385 8 over points>> [ write-outline-fill ] [ [ write-point ] each ] bi ;
387 M: outline-fill-rectangles write-command
388 9 over rectangles>> [ write-outline-fill ] [ [ write-rectangle ] each ] bi ;
390 M: outline-fill-path write-command
391 10 over path>> [ write-outline-fill ] [ write-path ] bi ;
393 : write-commands ( commands -- )
394 [ write-command ] each 0 write1 ;
398 TUPLE: instruction line-width ;
400 TUPLE: diagonal-line < instruction position ;
402 C: <diagonal-line> diagonal-line
404 TUPLE: horizontal-line < instruction x ;
406 C: <horizontal-line> horizontal-line
408 TUPLE: vertical-line < instruction y ;
410 C: <vertical-line> vertical-line
412 TUPLE: cubic-bezier < instruction control0 control1 point1 ;
414 C: <cubic-bezier> cubic-bezier
416 TUPLE: arc < instruction large-arc? sweep? ;
418 TUPLE: arc-circle < arc radius target ;
420 C: <arc-circle> arc-circle
422 TUPLE: arc-ellipse < arc radius-x radius-y rotation target ;
424 C: <arc-ellipse> arc-ellipse
426 TUPLE: close-path < instruction ;
428 C: <close-path> close-path
430 TUPLE: quadratic-bezier < instruction control point1 ;
432 C: <quadratic-bezier> quadratic-bezier
434 : read-tag ( -- line-width/f tag )
435 read1 [ 4 bit? ] [ 3 bits ] bi [ [ read-unit ] [ f ] if ] dip ;
437 : read-arc ( -- large-arc? sweep? )
438 read1 [ 0 bit? ] [ 1 bit? ] bi ;
440 : read-instruction ( -- instruction )
442 { 0 [ read-point <diagonal-line> ] }
443 { 1 [ read-unit <horizontal-line> ] }
444 { 2 [ read-unit <vertical-line> ] }
445 { 3 [ [ read-point ] 3 call-n <cubic-bezier> ] }
446 { 4 [ read-arc read-unit read-point <arc-circle> ] }
447 { 5 [ read-arc [ read-unit ] 3 call-n read-point <arc-ellipse> ] }
448 { 6 [ <close-path> ] }
449 { 7 [ [ read-point ] 2 call-n <quadratic-bezier> ] }
452 : read-instructions ( n -- instructions )
453 1 + [ read-instruction ] replicate ;
455 : write-tag ( instruction n -- )
457 [ [ 4 set-bit ] when write1 ]
458 [ [ write-unit ] when* ] bi ;
460 : write-arc ( instruction -- )
461 [ large-arc?>> 0b1 0b0 ? ] [ sweep?>> [ 0b10 bitor ] when ] bi write1 ;
463 GENERIC: write-instruction ( instruction -- )
465 M: diagonal-line write-instruction
466 [ 0 write-tag ] [ position>> write-point ] bi ;
468 M: horizontal-line write-instruction
469 [ 1 write-tag ] [ x>> write-unit ] bi ;
471 M: vertical-line write-instruction
472 [ 2 write-tag ] [ y>> write-unit ] bi ;
474 M: cubic-bezier write-instruction
477 [ control0>> write-point ]
478 [ control1>> write-point ]
479 [ point1>> write-point ]
482 M: arc-circle write-instruction
486 [ radius>> write-unit ]
487 [ target>> write-point ]
490 M: arc-ellipse write-instruction
494 [ radius-x>> write-unit ]
495 [ radius-y>> write-unit ]
496 [ rotation>> write-unit ]
497 [ target>> write-point ]
500 M: close-path write-instruction
503 M: quadratic-bezier write-instruction
504 [ 7 write-tag ] [ control>> write-point ] [ point1>> write-point ] tri ;
508 TUPLE: segment start instructions ;
512 : read-segment ( n -- segment )
513 read-point swap read-instructions segment boa ;
515 : write-segment ( segment -- )
516 [ start>> write-point ] [ instructions>> [ write-instruction ] each ] bi ;
520 : read-path ( segment-count -- segments )
521 1 + [ read-varuint ] replicate [ read-segment ] map ;
523 : write-path ( segments -- )
524 [ [ instructions>> length write-length ] each ]
525 [ [ write-segment ] each ] bi ;
529 TUPLE: tinyvg header color-table commands ;
533 : read-tinyvg ( -- tinyvg )
536 dup scale>> 2^ scale-factor set
537 dup color-encoding>> color-encoding set
538 dup coordinate-range>> coordinate-range set
539 dup color-count>> read-color-table dup color-table set
544 : path>tinyvg ( path -- tinyvg )
545 binary [ read-tinyvg ] with-file-reader ;
547 : bytes>tinyvg ( byte-array -- tinyvg )
548 binary [ read-tinyvg ] with-byte-reader ;
550 : write-tinyvg ( tinyvg -- )
553 [ header>> write-tinyvg-header ]
554 [ header>> scale>> 2^ scale-factor set ]
555 [ header>> color-encoding>> color-encoding set ]
556 [ header>> coordinate-range>> coordinate-range set ]
557 [ color-table>> color-table set ]
558 [ color-table>> [ write-color ] each ]
559 [ commands>> write-commands ]
563 : tinyvg>path ( tinyvg path -- )
564 binary [ write-tinyvg ] with-file-writer ;
566 : tinyvg>bytes ( tinyvg -- byte-array )
567 binary [ write-tinyvg ] with-byte-writer ;