├── .github └── workflows │ └── node.js.yml ├── .gitignore ├── .npmignore ├── .nycrc ├── LICENSE ├── README.md ├── bin ├── install_emscripten.sh └── wrap_wasm.js ├── examples ├── decode.js ├── decode_encode.js ├── decode_full_sequence.js └── encode.js ├── img2sixel.js ├── index.html ├── package-lock.json ├── package.json ├── palette.png ├── sixel_textcursor.sh ├── src ├── Colors.test.ts ├── Colors.ts ├── Decoder.test.ts ├── Decoder.ts ├── Params.test.ts ├── Params.ts ├── Quantizer.ts ├── SixelEncoder.test.ts ├── SixelEncoder.ts ├── Types.ts ├── bundle_decode.ts ├── bundle_encode.ts ├── index.benchmark.ts ├── index.ts └── upng.ts ├── testfiles ├── biplane.six ├── biplane_clean.six ├── boticelli.six ├── boticelli_clean.six ├── cat2.six ├── cat2_clean.six ├── chess.six ├── chess_clean.six ├── demo05.six ├── demo05_clean.six ├── fullhd_12bit_noise.six ├── fullhd_12bit_noise_clean.six ├── gnuplot.six ├── gnuplot_clean.six ├── leonardo.six ├── leonardo_clean.six ├── michael.six ├── michael_clean.six ├── oriole.six ├── oriole2.six ├── oriole2_clean.six ├── oriole_clean.six ├── rose16.six ├── rose16_clean.six ├── sampsa1.sixel ├── sampsa1_clean.sixel ├── sampsa_reencoded.six ├── sampsa_reencoded_clean.six ├── screen.six ├── screen_clean.six ├── sem.six ├── sem_clean.six ├── space.six ├── space_clean.six ├── test1.sixel ├── test1_clean.sixel ├── test2.sixel ├── test2_clean.sixel ├── testhlong.six ├── testhlong_clean.six ├── time.six ├── time_clean.six ├── trontank.six ├── trontank_clean.six ├── zx81.six └── zx81_clean.six ├── tsconfig.esm.json ├── tsconfig.json ├── tslint.json ├── upng.js ├── wasm ├── README.md ├── build.sh ├── decoder-simd.cpp ├── decoder.cpp └── wasmer_example.py └── webpack.config.js /.github/workflows/node.js.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean install of node dependencies, cache/restore them, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: Node.js CI 5 | 6 | on: 7 | push: 8 | branches: [ master ] 9 | pull_request: 10 | branches: [ master ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | matrix: 19 | node-version: [12.x, 14.x, 16.x] 20 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ 21 | 22 | steps: 23 | - uses: actions/checkout@v2 24 | - name: Use Node.js ${{ matrix.node-version }} 25 | uses: actions/setup-node@v2 26 | with: 27 | node-version: ${{ matrix.node-version }} 28 | cache: 'npm' 29 | - run: npm ci 30 | - run: npm run build --if-present 31 | - run: npm test 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /dist/ 2 | /lib/ 3 | /lib-esm/ 4 | /node_modules/ 5 | /coverage 6 | /.nyc_output 7 | /benchmark 8 | /.vscode 9 | /wasm/decoder.wasm 10 | /wasm/settings.json 11 | /src/wasm.ts 12 | /emsdk 13 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | testfiles 2 | *.png 3 | *.jpg 4 | *.sixel 5 | node_example* 6 | index.html 7 | sixel_textcursor.sh 8 | webpack.config.js 9 | coverage 10 | .nyc_output 11 | .nycrc 12 | benchmark 13 | lib/*.benchmark.* 14 | lib/*.test.* 15 | src/*.benchmark.* 16 | src/*.test.* 17 | img2sixel.js 18 | .vscode 19 | lib-esm 20 | emsdk 21 | bin 22 | .github 23 | examples 24 | src 25 | wasm 26 | tsconfig.esm.json 27 | tsconfig.json 28 | tslint.json 29 | -------------------------------------------------------------------------------- /.nycrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@istanbuljs/nyc-config-typescript" 3 | } 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019, Joerg Breitbart 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /bin/install_emscripten.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ -d emsdk ]; then 4 | exit 0 5 | fi 6 | 7 | # pull emscripten on fresh checkout 8 | echo "Fetching emscripten..." 9 | 10 | git clone https://github.com/emscripten-core/emsdk.git 11 | cd emsdk 12 | 13 | # wasm module is only tested with 2.0.25, install by default 14 | ./emsdk install 2.0.25 15 | ./emsdk activate 2.0.25 16 | 17 | cd .. 18 | -------------------------------------------------------------------------------- /bin/wrap_wasm.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const LIMITS = require('../wasm/settings.json'); 3 | 4 | const file = ` 5 | export const LIMITS = { 6 | CHUNK_SIZE: ${LIMITS.CHUNK_SIZE}, 7 | PALETTE_SIZE: ${LIMITS.PALETTE_SIZE}, 8 | MAX_WIDTH: ${LIMITS.MAX_WIDTH}, 9 | BYTES: '${fs.readFileSync('wasm/decoder.wasm').toString('base64')}' 10 | }; 11 | `; 12 | fs.writeFileSync('src/wasm.ts', file); 13 | -------------------------------------------------------------------------------- /examples/decode.js: -------------------------------------------------------------------------------- 1 | const { decodeAsync, PALETTE_ANSI_256 } = require('../lib/index'); 2 | const { createCanvas, createImageData } = require('canvas'); 3 | const fs = require('fs'); 4 | const open = require('open'); 5 | 6 | /** 7 | * Note on *_clean.six files: 8 | * 9 | * Normally SIXEL data is embedded in a DCS sequence like this: 10 | * DCS P1 ; P2 ; P3 q ST 11 | * 12 | * To handle this properly we would have to include an escape 13 | * sequence parser in the example which is beyond the scope. Thus 14 | * the _clean.six files are stripped down to the part. 15 | */ 16 | fs.readFile('testfiles/screen_clean.six', (err, data) => { 17 | // example with decodeAsync 18 | // about the used options: 19 | // - palette: 20 | // colors to start with (some older sixel images dont define 21 | // their colors, instead rely on a predefined default palette) 22 | // default is 16 colors of VT340, here we set it to 256 colors of xterm 23 | // - memoryUsage: 24 | // hard limit the pixel memory to avoid running out of memory (default 128 MB) 25 | // note this cannot be approximated from sixel data size without 26 | // parsing (1 kB of sixel data can easily create >10M pixels) 27 | decodeAsync(data, {palette: PALETTE_ANSI_256, memoryLimit: 65536 * 20}) 28 | .then(result => { 29 | // transfer bitmap data to ImageData object 30 | const imageData = createImageData(result.width, result.height); 31 | new Uint32Array(imageData.data.buffer).set(result.data32); 32 | 33 | // draw ImageData to canvas 34 | const canvas = createCanvas(result.width, result.height); 35 | const ctx = canvas.getContext('2d'); 36 | ctx.putImageData(imageData, 0, 0); 37 | 38 | // write to some file and show it 39 | const targetFile = __dirname + '/node_decode_output.png'; 40 | const out = fs.createWriteStream(targetFile); 41 | const stream = canvas.createPNGStream(); 42 | stream.pipe(out); 43 | out.on('finish', () => open(targetFile)); 44 | }); 45 | }) 46 | -------------------------------------------------------------------------------- /examples/decode_encode.js: -------------------------------------------------------------------------------- 1 | const { introducer, FINALIZER, sixelEncode, decode } = require('../lib/index'); 2 | const fs = require('fs'); 3 | 4 | /** 5 | * Note on *_clean.six files: 6 | * 7 | * Normally SIXEL data is embedded in a DCS sequence like this: 8 | * DCS P1 ; P2 ; P3 q ST 9 | * 10 | * To handle this properly we would have to include an escape 11 | * sequence parser in the example which is beyond the scope. Thus 12 | * the _clean.six files are stripped down to the part. 13 | */ 14 | fs.readFile('testfiles/biplane_clean.six', (err, data) => { 15 | 16 | // decoding with sync version (does not work in browser main, use decodeAsync there) 17 | const img = decode(data); 18 | 19 | // extract colors 20 | const palette = new Set(); 21 | for (let i = 0; i < img.data32.length; ++i) { 22 | palette.add(img.data32[i]); 23 | } 24 | 25 | // encode to sixel again 26 | const sixelData = sixelEncode( 27 | new Uint8Array(img.data32.buffer), 28 | img.width, 29 | img.height, 30 | Array.from(palette) 31 | ); 32 | 33 | // write to sixel capable terminal 34 | // `sixelEncode` gives us only the sixel data part, 35 | // to get a full sequence, we need to add the introducer and the finalizer 36 | // (never forget the finalizer or the terminal will "hang") 37 | console.log(introducer(1) + sixelData + FINALIZER); 38 | }) 39 | -------------------------------------------------------------------------------- /examples/decode_full_sequence.js: -------------------------------------------------------------------------------- 1 | const { toRGBA8888, Decoder } = require('../lib/index'); 2 | const { createCanvas, createImageData } = require('canvas'); 3 | const fs = require('fs'); 4 | const open = require('open'); 5 | const AnsiParser = require('node-ansiparser'); 6 | 7 | // create a sixel decoder instance (sync version, use DecoderAsync in browser main) 8 | const decoder = new Decoder(); 9 | 10 | /** 11 | * Despite the other decode examples we use the normal testfiles here 12 | * with full DCS sequences. For parsing of the escape sequence we can use `node-anisparser`. 13 | * 14 | * Note that `node-ansiparser` works only with strings, thus we have to set 'utf-8' as file encoding 15 | * (and use `decodeString` later on). This will not work with all testfiles, 16 | * some of them have 8bit control characters that get stripped/scrambled with 'utf-8'. 17 | */ 18 | fs.readFile('testfiles/boticelli.six', 'utf-8', (err, data) => { 19 | 20 | // terminal object needed for the sequence parser 21 | // we are only interested in the DCS calls, thus skip the other methods 22 | const terminal = { 23 | // some background color (red to make the effect obvious) 24 | backgroundColor: toRGBA8888(255, 0, 0, 255), 25 | 26 | // some state to determine whether DCS payload should go to sixel image 27 | // the state is needed since the input might contain other non DCS sequences 28 | // that should not be treated as SIXEL data 29 | inSixel: false, 30 | 31 | // inst_H: called whan a DCS sequence starts 32 | inst_H(collected, params, flag) { 33 | // q means incoming SIXEL DCS, thus create new SIXEL image 34 | if (flag === 'q') { 35 | // also eval params of the sequence, P2 is backgroundSelect 36 | // if set to 1 we should set fillColor to 0 (leave transparent) 37 | // else set to background color from the terminal 38 | // hint: try changing the test file or the color to see the effect of this setting 39 | 40 | // init a new image (null for `palette` means to keep the current loaded one) 41 | decoder.init(params[1] === 1 ? 0 : this.backgroundColor, null, 256); 42 | this.inSixel = true; 43 | } 44 | }, 45 | 46 | // inst_P: called for DCS payload chunks 47 | inst_P(chunk) { 48 | if (this.inSixel) { 49 | decoder.decodeString(chunk); 50 | } 51 | }, 52 | 53 | // inst_U: called when DCS sequence finishs 54 | inst_U() { 55 | if (this.inSixel) { 56 | this.inSixel = false; 57 | 58 | // we were actually in a SIXEL DCS sequence 59 | // and have now all image data received, thus 60 | // can continue image handling: 61 | 62 | // transfer bitmap data to ImageData object 63 | const imageData = createImageData(decoder.width, decoder.height); 64 | new Uint32Array(imageData.data.buffer).set(decoder.data32); 65 | 66 | // draw ImageData to canvas 67 | const canvas = createCanvas(decoder.width, decoder.height); 68 | const ctx = canvas.getContext('2d'); 69 | ctx.putImageData(imageData, 0, 0); 70 | 71 | // write to some file and show it 72 | const targetFile = __dirname + '/node_decode_full_sequence_output.png'; 73 | const out = fs.createWriteStream(targetFile); 74 | const stream = canvas.createPNGStream(); 75 | stream.pipe(out); 76 | out.on('finish', () => open(targetFile)); 77 | 78 | // free ressources on sixel decoder 79 | decoder.release(); 80 | } 81 | } 82 | }; 83 | 84 | // create sequence parser and parse the file data 85 | const parser = new AnsiParser(terminal); 86 | parser.parse(data); 87 | }) 88 | -------------------------------------------------------------------------------- /examples/encode.js: -------------------------------------------------------------------------------- 1 | const { decodeAsync, image2sixel } = require('../lib/index'); 2 | const { createCanvas, createImageData } = require('canvas'); 3 | const fs = require('fs'); 4 | const open = require('open'); 5 | 6 | // create some canvas 7 | const width = 204; 8 | const height = 202; 9 | const canvas = createCanvas(width, height); 10 | const ctx = canvas.getContext('2d'); 11 | ctx.fillStyle = 'white'; 12 | ctx.fillRect(0, 0, canvas.width, canvas.height); 13 | 14 | // gradient 15 | const gradient = ctx.createLinearGradient(0, 0, 204, 0); 16 | gradient.addColorStop(0, 'green'); 17 | gradient.addColorStop(.5, 'cyan'); 18 | gradient.addColorStop(1, 'green'); 19 | ctx.fillStyle = gradient; 20 | ctx.fillRect(0, 0, 204, 50); 21 | 22 | // yellow circle 23 | ctx.strokeStyle = 'yellow'; 24 | ctx.beginPath(); 25 | ctx.arc(100, 120, 50, 0, 2 * Math.PI); 26 | ctx.stroke(); 27 | 28 | // line at end - 1 29 | ctx.translate(0.5,0.5); 30 | ctx.strokeStyle = 'red'; 31 | ctx.beginPath(); 32 | ctx.moveTo(0, 200); 33 | ctx.lineTo(204, 200); 34 | ctx.stroke(); 35 | 36 | // some text 37 | ctx.font = '30px Impact'; 38 | ctx.rotate(0.1); 39 | ctx.fillStyle = 'black'; 40 | ctx.fillText('Awesome!', 50, 100); 41 | 42 | // green underline half opaque 43 | const text = ctx.measureText('Awesome!'); 44 | ctx.strokeStyle = 'rgba(0, 255, 0, 0.5)'; 45 | ctx.lineWidth = 2; 46 | ctx.beginPath(); 47 | ctx.lineTo(50, 102); 48 | ctx.lineTo(50 + text.width, 102); 49 | ctx.stroke(); 50 | 51 | // convert to sixel sequence 52 | const sixelData = image2sixel(ctx.getImageData(0, 0, width, height).data, width, height, 256, 1); 53 | 54 | // output SIXEL data to terminal (Terminal must have SIXEL enabled!) 55 | console.log(sixelData); 56 | 57 | 58 | /** 59 | * For comparison we also output the image to a PNG file. 60 | */ 61 | 62 | // note: we strip the first 7 bytes, since they belong to the escape sequence introducer 63 | // (should be handled by a proper sequence parser, see node_example_decode_full_sequence.js) 64 | decodeAsync(sixelData.slice(7), {fillColor:0, memoryLimit: 65536 *20}) 65 | .then(result => { 66 | // transfer bitmap data to ImageData object 67 | const imageData = createImageData(result.width, result.height); 68 | new Uint32Array(imageData.data.buffer).set(result.data32); 69 | 70 | // draw ImageData to canvas 71 | const canvas2 = createCanvas(result.width, result.height); 72 | const ctx2 = canvas2.getContext('2d'); 73 | ctx2.putImageData(imageData, 0, 0); 74 | 75 | // write to some file and show it 76 | const targetFile = __dirname + '/node_encode_output.png'; 77 | const out = fs.createWriteStream(targetFile); 78 | const stream = canvas2.createPNGStream(); 79 | stream.pipe(out); 80 | out.on('finish', () => open(targetFile)); 81 | }); 82 | -------------------------------------------------------------------------------- /img2sixel.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Example script as cmdline converter. 3 | * Call: `node img2sixel.js ` 4 | */ 5 | 6 | 7 | // set to 16 for xterm in VT340 mode 8 | const MAX_PALETTE = 256; 9 | 10 | // 0 - default action (background color) 11 | // 1 - keep previous content 12 | // 2 - set background color 13 | const BACKGROUND_SELECT = 0; 14 | 15 | 16 | const { loadImage, createCanvas } = require('canvas'); 17 | const { introducer, FINALIZER, sixelEncode, image2sixel } = require('./lib/index'); 18 | 19 | 20 | async function processImage(filename, palLimit) { 21 | // load image 22 | let img; 23 | try { 24 | img = await loadImage(filename); 25 | } catch (e) { 26 | console.error(`cannot load image "${filename}"`); 27 | return; 28 | } 29 | const canvas = createCanvas(img.width, img.height); 30 | const ctx = canvas.getContext('2d'); 31 | ctx.drawImage(img, 0, 0); 32 | 33 | // use image2sixel with internal quantizer 34 | const data = ctx.getImageData(0, 0, img.width, img.height).data; 35 | console.log(`${filename}:`); 36 | console.log(image2sixel(data, img.width, img.height, palLimit, BACKGROUND_SELECT)); 37 | 38 | // alternatively use custom quantizer library 39 | // const RgbQuant = require('rgbquant'); 40 | // const q = new RgbQuant({colors: palLimit, dithKern: 'FloydSteinberg', dithSerp: true}); 41 | // q.sample(canvas); 42 | // const palette = q.palette(true); 43 | // const quantizedData = q.reduce(canvas); 44 | // console.log(`${filename}:`); 45 | // console.log([ 46 | // introducer(BACKGROUND_SELECT), 47 | // sixelEncode(quantizedData, img.width, img.height, palette), 48 | // FINALIZER 49 | // ].join('')); 50 | } 51 | 52 | async function main() { 53 | let palLimit = MAX_PALETTE; 54 | for (const arg of process.argv) { 55 | if (arg.startsWith('-p')) { 56 | palLimit = parseInt(arg.slice(2)); 57 | process.argv.splice(process.argv.indexOf(arg), 1); 58 | break; 59 | } 60 | } 61 | for (const filename of process.argv.slice(2)) { 62 | await processImage(filename, palLimit); 63 | } 64 | } 65 | 66 | main(); 67 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Sixel test 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 |

36 | 37 |
38 | 39 |
40 | 41 |

42 | 43 |
44 | 45 |

46 | 47 | 48 | 127 | 128 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sixel", 3 | "version": "0.16.0", 4 | "description": "Sixel image format for node and browser.", 5 | "main": "./lib/index.js", 6 | "scripts": { 7 | "test": "mocha lib/*.test.js", 8 | "tsc": "tsc", 9 | "watch": "tsc -w", 10 | "lint": "tslint 'src/**/*.ts'", 11 | "start": "npm run bundle && http-server", 12 | "prepublish": "npm run build-all", 13 | "coverage": "nyc --reporter=lcov --reporter=text --reporter=html npm test", 14 | "benchmark": "xterm-benchmark $*", 15 | "build-wasm": "bin/install_emscripten.sh && cd wasm && ./build.sh && cd .. && node bin/wrap_wasm.js", 16 | "bundle": "tsc --project tsconfig.esm.json && webpack", 17 | "clean": "rm -rf lib lib-esm dist src/wasm.ts wasm/decoder.wasm wasm/settings.json", 18 | "build-all": "npm run build-wasm && npm run tsc && npm run bundle" 19 | }, 20 | "keywords": [ 21 | "sixel", 22 | "image", 23 | "terminal" 24 | ], 25 | "repository": { 26 | "type": "git", 27 | "url": "https://github.com/jerch/node-sixel.git" 28 | }, 29 | "author": "Joerg Breitbart ", 30 | "license": "MIT", 31 | "devDependencies": { 32 | "@istanbuljs/nyc-config-typescript": "^1.0.2", 33 | "@types/mocha": "^9.1.1", 34 | "@types/node": "^14.17.12", 35 | "canvas": "^2.9.1", 36 | "http-server": "^14.1.0", 37 | "mocha": "^9.2.2", 38 | "node-ansiparser": "^2.2.0", 39 | "nyc": "^15.1.0", 40 | "open": "^8.4.0", 41 | "rgbquant": "^1.1.2", 42 | "source-map-loader": "^3.0.1", 43 | "source-map-support": "^0.5.21", 44 | "ts-loader": "^9.3.0", 45 | "ts-node": "^10.7.0", 46 | "tslint": "^6.1.3", 47 | "typescript": "^4.4.4", 48 | "webpack": "^5.72.0", 49 | "webpack-cli": "^4.9.2", 50 | "xterm-benchmark": "^0.3.1" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /palette.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jerch/node-sixel/3f933dbadcaed5ab90b749c032433b394bd0b289/palette.png -------------------------------------------------------------------------------- /sixel_textcursor.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # enable sixel scrolling 4 | # set this to l for mlterm (swapped meaning) 5 | echo -e "\x1b[?80h" 6 | 7 | clear 8 | 9 | echo "test different widths" 10 | sixels='~ ~~ ~~~ ~~~~ ~~~~~ ~~~~~~ ~~~~~~~ ~~~~~~~~ ~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~~ ~~~~~~~~~~~~ ~~~~~~~~~~~~~' 11 | for sixel in $sixels 12 | do 13 | echo -ne "###\x1bPq#2$sixel\x1b\\xxx" 14 | read 15 | done 16 | 17 | clear 18 | 19 | echo "test different heights - full sixel block" 20 | echo -ne "###\x1bPq#2~~~~~~\x1b\\xxx" 21 | read 22 | echo -ne "###\x1bPq#2~~~~~~-~~~~~~\x1b\\xxx" 23 | read 24 | echo -ne "###\x1bPq#2~~~~~~-~~~~~~-~~~~~~\x1b\\xxx" 25 | read 26 | echo -ne "###\x1bPq#2~~~~~~-~~~~~~-~~~~~~-~~~~~~\x1b\\xxx" 27 | read 28 | echo -ne "###\x1bPq#2~~~~~~-~~~~~~-~~~~~~-~~~~~~-~~~~~~\x1b\\xxx" 29 | read 30 | 31 | clear 32 | 33 | echo "test different heights - adding pixels downwards" 34 | echo -ne "###\x1bPq#2~~~~~~-??????\x1b\\xxx" 35 | read 36 | echo -ne "###\x1bPq#2~~~~~~-@@@@@@\x1b\\xxx" 37 | read 38 | echo -ne "###\x1bPq#2~~~~~~-BBBBBB\x1b\\xxx" 39 | read 40 | echo -ne "###\x1bPq#2~~~~~~-FFFFFF\x1b\\xxx" 41 | read 42 | echo -ne "###\x1bPq#2~~~~~~-NNNNNN\x1b\\xxx" 43 | read 44 | echo -ne "###\x1bPq#2~~~~~~-^^^^^^\x1b\\xxx" 45 | read 46 | echo -ne "###\x1bPq#2~~~~~~-~~~~~~\x1b\\xxx" 47 | read 48 | echo -ne "###\x1bPq#2~~~~~~-~~~~~~-??????\x1b\\xxx" 49 | read 50 | echo -ne "###\x1bPq#2~~~~~~-~~~~~~-@@@@@@\x1b\\xxx" 51 | read 52 | echo -ne "###\x1bPq#2~~~~~~-~~~~~~-BBBBBB\x1b\\xxx" 53 | read 54 | echo -ne "###\x1bPq#2~~~~~~-~~~~~~-FFFFFF\x1b\\xxx" 55 | read 56 | echo -ne "###\x1bPq#2~~~~~~-~~~~~~-NNNNNN\x1b\\xxx" 57 | read 58 | echo -ne "###\x1bPq#2~~~~~~-~~~~~~-^^^^^^\x1b\\xxx" 59 | read 60 | echo -ne "###\x1bPq#2~~~~~~-~~~~~~-~~~~~~\x1b\\xxx" 61 | read 62 | 63 | clear 64 | 65 | echo "right border" 66 | echo -ne "###\x1bPq#2!400~\x1b\\xxx" 67 | read 68 | echo -ne "###\x1bPq#2!450~\x1b\\xxx" 69 | read 70 | echo -ne "###\x1bPq#2!500~\x1b\\xxx" 71 | read 72 | echo -ne "###\x1bPq#2!1500~\x1b\\xxx" 73 | read -------------------------------------------------------------------------------- /src/Colors.test.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2019 Joerg Breitbart. 3 | * @license MIT 4 | */ 5 | 6 | import * as assert from 'assert'; 7 | import { fromRGBA8888, toRGBA8888, red, green, blue, alpha, normalizeHLS, normalizeRGB, nearestColorIndex } from './Colors'; 8 | import { RGBA8888 } from './Types'; 9 | 10 | function almostEqualColor(a: RGBA8888, b: RGBA8888, distance: number = 0) { 11 | try { 12 | assert.strictEqual(Math.abs(red(a) - red(b)) <= distance, true); 13 | assert.strictEqual(Math.abs(green(a) - green(b)) <= distance, true); 14 | assert.strictEqual(Math.abs(blue(a) - blue(b)) <= distance, true); 15 | assert.strictEqual(Math.abs(alpha(a) - alpha(b)) <= distance, true); 16 | } catch (e) { 17 | throw new Error(`mismatch in colors: [${fromRGBA8888(a)}] : [${fromRGBA8888(b)}]`); 18 | } 19 | } 20 | 21 | describe('Colors', () => { 22 | describe('toRGBA888', () => { 23 | it('conversions', () => { 24 | assert.strictEqual(toRGBA8888(0, 0, 0, 0), 0); 25 | assert.strictEqual(toRGBA8888(0, 0, 0, 255), 0xFF000000); 26 | assert.strictEqual(toRGBA8888(0, 0, 255, 0), 0x00FF0000); 27 | assert.strictEqual(toRGBA8888(0, 255, 0, 0), 0x0000FF00); 28 | assert.strictEqual(toRGBA8888(255, 0, 0, 0), 0x000000FF); 29 | }); 30 | it('alpha defaults to 255', () => { 31 | assert.strictEqual(toRGBA8888(0, 0, 0), toRGBA8888(0, 0, 0, 255)); 32 | assert.strictEqual(toRGBA8888(0, 0, 255), toRGBA8888(0, 0, 255, 255)); 33 | assert.strictEqual(toRGBA8888(0, 255, 0), toRGBA8888(0, 255, 0, 255)); 34 | assert.strictEqual(toRGBA8888(255, 0, 0), toRGBA8888(255, 0, 0, 255)); 35 | assert.strictEqual(toRGBA8888(0, 255, 255), toRGBA8888(0, 255, 255, 255)); 36 | assert.strictEqual(toRGBA8888(255, 0, 255), toRGBA8888(255, 0, 255, 255)); 37 | assert.strictEqual(toRGBA8888(255, 255, 0), toRGBA8888(255, 255, 0, 255)); 38 | assert.strictEqual(toRGBA8888(255, 255, 255), toRGBA8888(255, 255, 255, 255)); 39 | }); 40 | it('should only return unsigned', () => { 41 | // test only for r and a here (g/b dont add to significant bit) 42 | for (let r = 0; r <= 0xFF; ++r) { 43 | for (let a = 0; a <= 0xFF; ++a) { 44 | const color = toRGBA8888(r, 0, 0, a); 45 | assert.strictEqual(color >= 0, true); 46 | } 47 | } 48 | }); 49 | it('handled signed channel values', () => { 50 | assert.strictEqual(toRGBA8888(-8, -50, -100, -127), toRGBA8888(-8 >>> 0, -50 >>> 0, -100 >>> 0, -127 >>> 0)); 51 | }); 52 | it('strip channel values to 8 bit (not clamping)', () => { 53 | assert.strictEqual(toRGBA8888(0x1234, 0x5678, 0xabcd, 0xef11), 0x11cd7834); 54 | }); 55 | }); 56 | describe('fromRGBA8888', () => { 57 | it('conversions', () => { 58 | assert.deepStrictEqual(fromRGBA8888(0), [0, 0, 0, 0]); 59 | assert.deepStrictEqual(fromRGBA8888(0x000000FF), [255, 0, 0, 0]); 60 | assert.deepStrictEqual(fromRGBA8888(0x0000FF00), [0, 255, 0, 0]); 61 | assert.deepStrictEqual(fromRGBA8888(0x00FF0000), [0, 0, 255, 0]); 62 | assert.deepStrictEqual(fromRGBA8888(0xFF000000), [0, 0, 0, 255]); 63 | }); 64 | it('should only create unsigned channel values', () => { 65 | assert.deepStrictEqual(fromRGBA8888(-1), [255, 255, 255, 255]); 66 | // 2 complement: -0xedcba988 ==> 0x12345678 (newDigit = 15 - digit; result + 1) 67 | assert.deepStrictEqual(fromRGBA8888(-0xedcba988), [0x78, 0x56, 0x34, 0x12]); 68 | }); 69 | it('strip values to 32bit', () => { 70 | assert.deepStrictEqual(fromRGBA8888(0x1234567890), [0x90, 0x78, 0x56, 0x34]); 71 | }); 72 | }); 73 | describe('channels', () => { 74 | it('red', () => { 75 | assert.strictEqual(red(toRGBA8888(0x12, 0x34, 0x56, 0x78)), 0x12); 76 | }); 77 | it('green', () => { 78 | assert.strictEqual(green(toRGBA8888(0x12, 0x34, 0x56, 0x78)), 0x34); 79 | }); 80 | it('blue', () => { 81 | assert.strictEqual(blue(toRGBA8888(0x12, 0x34, 0x56, 0x78)), 0x56); 82 | }); 83 | it('alpha', () => { 84 | assert.strictEqual(alpha(toRGBA8888(0x12, 0x34, 0x56, 0x78)), 0x78); 85 | }); 86 | }); 87 | it('RGB/HLS VT340 normalization', () => { 88 | // values taken from https://vt100.net/docs/vt3xx-gp/chapter2.html#S2.4 89 | assert.strictEqual(normalizeHLS(0, 0, 0), normalizeRGB(0, 0, 0)); 90 | almostEqualColor(normalizeHLS(0, 50, 60), normalizeRGB(20, 20, 80), 0); 91 | almostEqualColor(normalizeHLS(120, 46, 72), normalizeRGB(80, 13, 13), 2); 92 | almostEqualColor(normalizeHLS(240, 50, 60), normalizeRGB(20, 80, 20), 0); 93 | almostEqualColor(normalizeHLS(60, 50, 60), normalizeRGB(80, 20, 80), 0); 94 | almostEqualColor(normalizeHLS(300, 50, 60), normalizeRGB(20, 80, 80), 0); 95 | almostEqualColor(normalizeHLS(180, 50, 60), normalizeRGB(80, 80, 20), 0); 96 | almostEqualColor(normalizeHLS(0, 53, 0), normalizeRGB(53, 53, 53), 0); 97 | almostEqualColor(normalizeHLS(0, 26, 0), normalizeRGB(26, 26, 26), 0); 98 | almostEqualColor(normalizeHLS(0, 46, 29), normalizeRGB(33, 33, 60), 2); 99 | almostEqualColor(normalizeHLS(120, 43, 39), normalizeRGB(60, 26, 26), 1); 100 | almostEqualColor(normalizeHLS(240, 46, 29), normalizeRGB(33, 60, 33), 2); 101 | almostEqualColor(normalizeHLS(0, 80, 0), normalizeRGB(80, 80, 80), 0); 102 | 103 | // basic HLS tests 104 | almostEqualColor(normalizeHLS(0, 50, 100), toRGBA8888(0, 0, 255), 0); 105 | almostEqualColor(normalizeHLS(120, 50, 100), toRGBA8888(255, 0, 0), 0); 106 | almostEqualColor(normalizeHLS(240, 50, 100), toRGBA8888(0, 255, 0), 0); 107 | almostEqualColor(normalizeHLS(180, 50, 100), toRGBA8888(255, 255, 0), 0); 108 | almostEqualColor(normalizeHLS(300, 50, 100), toRGBA8888(0, 255, 255), 0); 109 | almostEqualColor(normalizeHLS(60, 50, 100), toRGBA8888(255, 0, 255), 0); 110 | }); 111 | it('nearestColorIndex (ED)', () => { 112 | const p: [number, number, number][] = [[0, 0, 0], [50, 50, 0], [100, 50, 50], [100, 100, 100], [150, 100, 50]]; 113 | assert.strictEqual(nearestColorIndex(toRGBA8888(1, 2, 3), p), 0); 114 | assert.strictEqual(nearestColorIndex(toRGBA8888(100, 100, 100), p), 3); 115 | assert.strictEqual(nearestColorIndex(toRGBA8888(170, 100, 50), p), 4); 116 | }); 117 | }); 118 | -------------------------------------------------------------------------------- /src/Colors.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2019 Joerg Breitbart. 3 | * @license MIT 4 | */ 5 | 6 | import { RGBA8888, RGBColor } from './Types'; 7 | 8 | 9 | // FIXME: cleanup this mess, move things either to decoder/encoder, keep only shared things 10 | 11 | 12 | // system endianess 13 | export const BIG_ENDIAN = new Uint8Array(new Uint32Array([0xFF000000]).buffer)[0] === 0xFF; 14 | if (BIG_ENDIAN) { 15 | console.warn('BE platform detected. This version of node-sixel works only on LE properly.'); 16 | } 17 | 18 | // channel values 19 | export function red(n: RGBA8888): number { 20 | return n & 0xFF; 21 | } 22 | 23 | export function green(n: RGBA8888): number { 24 | return (n >>> 8) & 0xFF; 25 | } 26 | 27 | export function blue(n: RGBA8888): number { 28 | return (n >>> 16) & 0xFF; 29 | } 30 | 31 | export function alpha(n: RGBA8888): number { 32 | return (n >>> 24) & 0xFF; 33 | } 34 | 35 | 36 | /** 37 | * Convert RGB channels to native color RGBA8888. 38 | */ 39 | export function toRGBA8888(r: number, g: number, b: number, a: number = 255): RGBA8888 { 40 | return ((a & 0xFF) << 24 | (b & 0xFF) << 16 | (g & 0xFF) << 8 | (r & 0xFF)) >>> 0; // ABGR32 41 | } 42 | 43 | 44 | /** 45 | * Convert native color to [r, g, b, a]. 46 | */ 47 | export function fromRGBA8888(color: RGBA8888): [number, number, number, number] { 48 | return [color & 0xFF, (color >> 8) & 0xFF, (color >> 16) & 0xFF, color >>> 24]; 49 | } 50 | 51 | 52 | /** 53 | * Get index of nearest color in `palette` for `color`. 54 | * Uses euclidean distance without any luminescence correction. 55 | */ 56 | export function nearestColorIndex(color: RGBA8888, palette: RGBColor[]): number { 57 | const r = red(color); 58 | const g = green(color); 59 | const b = blue(color); 60 | 61 | let min = Number.MAX_SAFE_INTEGER; 62 | let idx = -1; 63 | 64 | // use euclidean distance (manhattan gives very poor results) 65 | for (let i = 0; i < palette.length; ++i) { 66 | const dr = r - palette[i][0]; 67 | const dg = g - palette[i][1]; 68 | const db = b - palette[i][2]; 69 | const d = dr * dr + dg * dg + db * db; 70 | if (!d) return i; 71 | if (d < min) { 72 | min = d; 73 | idx = i; 74 | } 75 | } 76 | 77 | return idx; 78 | } 79 | 80 | 81 | // color conversions 82 | // HLS taken from: http://www.niwa.nu/2013/05/math-behind-colorspace-conversions-rgb-hsl 83 | 84 | function clamp(low: number, high: number, value: number): number { 85 | return Math.max(low, Math.min(value, high)); 86 | } 87 | 88 | function h2c(t1: number, t2: number, c: number): number { 89 | if (c < 0) c += 1; 90 | if (c > 1) c -= 1; 91 | return c * 6 < 1 92 | ? t2 + (t1 - t2) * 6 * c 93 | : c * 2 < 1 94 | ? t1 95 | : c * 3 < 2 96 | ? t2 + (t1 - t2) * (4 - c * 6) 97 | : t2; 98 | } 99 | 100 | function HLStoRGB(h: number, l: number, s: number): RGBA8888 { 101 | if (!s) { 102 | const v = Math.round(l * 255); 103 | return toRGBA8888(v, v, v); 104 | } 105 | const t1 = l < 0.5 ? l * (1 + s) : l + s - l * s; 106 | const t2 = 2 * l - t1; 107 | return toRGBA8888( 108 | clamp(0, 255, Math.round(h2c(t1, t2, h + 1 / 3) * 255)), 109 | clamp(0, 255, Math.round(h2c(t1, t2, h) * 255)), 110 | clamp(0, 255, Math.round(h2c(t1, t2, h - 1 / 3) * 255)) 111 | ); 112 | } 113 | 114 | /** 115 | * Normalize SIXEL RGB values (percent based, 0-100) to RGBA8888. 116 | */ 117 | export function normalizeRGB(r: number, g: number, b: number): RGBA8888 { 118 | return (0xFF000000 | Math.round(b / 100 * 255) << 16 | Math.round(g / 100 * 255) << 8 | Math.round(r / 100 * 255)) >>> 0; // ABGR32 119 | } 120 | 121 | 122 | /** 123 | * Normalize SIXEL HLS values to RGBA8888. Applies hue correction of +240°. 124 | */ 125 | export function normalizeHLS(h: number, l: number, s: number): RGBA8888 { 126 | // Note: hue value is turned by 240° in VT340, all values given as fractions 127 | return HLStoRGB((h + 240 % 360) / 360, l / 100, s / 100); 128 | } 129 | 130 | 131 | /** 132 | * default palettes 133 | */ 134 | 135 | // FIXME: move palettes to Decoder.ts 136 | 137 | /** 138 | * 16 predefined color registers of VT340 (values in %): 139 | * ``` 140 | * R G B 141 | * 0 Black 0 0 0 142 | * 1 Blue 20 20 80 143 | * 2 Red 80 13 13 144 | * 3 Green 20 80 20 145 | * 4 Magenta 80 20 80 146 | * 5 Cyan 20 80 80 147 | * 6 Yellow 80 80 20 148 | * 7 Gray 50% 53 53 53 149 | * 8 Gray 25% 26 26 26 150 | * 9 Blue* 33 33 60 151 | * 10 Red* 60 26 26 152 | * 11 Green* 33 60 33 153 | * 12 Magenta* 60 33 60 154 | * 13 Cyan* 33 60 60 155 | * 14 Yellow* 60 60 33 156 | * 15 Gray 75% 80 80 80 157 | * ``` 158 | * (*) less saturated 159 | * 160 | * @see https://vt100.net/docs/vt3xx-gp/chapter2.html#S2.4 161 | */ 162 | export const PALETTE_VT340_COLOR = new Uint32Array([ 163 | normalizeRGB( 0, 0, 0), 164 | normalizeRGB(20, 20, 80), 165 | normalizeRGB(80, 13, 13), 166 | normalizeRGB(20, 80, 20), 167 | normalizeRGB(80, 20, 80), 168 | normalizeRGB(20, 80, 80), 169 | normalizeRGB(80, 80, 20), 170 | normalizeRGB(53, 53, 53), 171 | normalizeRGB(26, 26, 26), 172 | normalizeRGB(33, 33, 60), 173 | normalizeRGB(60, 26, 26), 174 | normalizeRGB(33, 60, 33), 175 | normalizeRGB(60, 33, 60), 176 | normalizeRGB(33, 60, 60), 177 | normalizeRGB(60, 60, 33), 178 | normalizeRGB(80, 80, 80) 179 | ]); 180 | 181 | /** 182 | * 16 predefined monochrome registers of VT340 (values in %): 183 | * ``` 184 | * R G B 185 | * 0 Black 0 0 0 186 | * 1 Gray-2 13 13 13 187 | * 2 Gray-4 26 26 26 188 | * 3 Gray-6 40 40 40 189 | * 4 Gray-1 6 6 6 190 | * 5 Gray-3 20 20 20 191 | * 6 Gray-5 33 33 33 192 | * 7 White 7 46 46 46 193 | * 8 Black 0 0 0 0 194 | * 9 Gray-2 13 13 13 195 | * 10 Gray-4 26 26 26 196 | * 11 Gray-6 40 40 40 197 | * 12 Gray-1 6 6 6 198 | * 13 Gray-3 20 20 20 199 | * 14 Gray-5 33 33 33 200 | * 15 White 7 46 46 46 201 | * ``` 202 | * 203 | * @see https://vt100.net/docs/vt3xx-gp/chapter2.html#S2.4 204 | */ 205 | export const PALETTE_VT340_GREY = new Uint32Array([ 206 | normalizeRGB( 0, 0, 0), 207 | normalizeRGB(13, 13, 13), 208 | normalizeRGB(26, 26, 26), 209 | normalizeRGB(40, 40, 40), 210 | normalizeRGB( 6, 6, 6), 211 | normalizeRGB(20, 20, 20), 212 | normalizeRGB(33, 33, 33), 213 | normalizeRGB(46, 46, 46), 214 | normalizeRGB( 0, 0, 0), 215 | normalizeRGB(13, 13, 13), 216 | normalizeRGB(26, 26, 26), 217 | normalizeRGB(40, 40, 40), 218 | normalizeRGB( 6, 6, 6), 219 | normalizeRGB(20, 20, 20), 220 | normalizeRGB(33, 33, 33), 221 | normalizeRGB(46, 46, 46) 222 | ]); 223 | 224 | /** 225 | * 256 predefined ANSI colors. 226 | * 227 | * @see https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit 228 | */ 229 | export const PALETTE_ANSI_256 = (() => { 230 | // 16 lower colors (taken from xterm) 231 | const p: RGBA8888[] = [ 232 | toRGBA8888(0, 0, 0), 233 | toRGBA8888(205, 0, 0), 234 | toRGBA8888(0, 205, 0), 235 | toRGBA8888(205, 205, 0), 236 | toRGBA8888(0, 0, 238), 237 | toRGBA8888(205, 0, 205), 238 | toRGBA8888(0, 250, 205), 239 | toRGBA8888(229, 229, 229), 240 | toRGBA8888(127, 127, 127), 241 | toRGBA8888(255, 0, 0), 242 | toRGBA8888(0, 255, 0), 243 | toRGBA8888(255, 255, 0), 244 | toRGBA8888(92, 92, 255), 245 | toRGBA8888(255, 0, 255), 246 | toRGBA8888(0, 255, 255), 247 | toRGBA8888(255, 255, 255), 248 | ]; 249 | // colors up to 232 250 | const d = [0, 95, 135, 175, 215, 255]; 251 | for (let r = 0; r < 6; ++r) { 252 | for (let g = 0; g < 6; ++g) { 253 | for (let b = 0; b < 6; ++b) { 254 | p.push(toRGBA8888(d[r], d[g], d[b])); 255 | } 256 | } 257 | } 258 | // grey scale to up 255 259 | for (let v = 8; v <= 238; v += 10) { 260 | p.push(toRGBA8888(v, v, v)); 261 | } 262 | return new Uint32Array(p); 263 | })(); 264 | 265 | /** 266 | * Background: Black by default. 267 | * Foreground: White by default. 268 | * 269 | * Background color is used whenever a fill color is needed and not explicitly set. 270 | * Foreground color is used as default initial sixel color. 271 | */ 272 | export const DEFAULT_BACKGROUND: RGBA8888 = toRGBA8888(0, 0, 0, 255); 273 | export const DEFAULT_FOREGROUND: RGBA8888 = toRGBA8888(255, 255, 255, 255); 274 | -------------------------------------------------------------------------------- /src/Params.test.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021 Joerg Breitbart. 3 | * @license MIT 4 | */ 5 | import * as assert from 'assert'; 6 | import { Params } from './Params'; 7 | 8 | describe('Params', () => { 9 | it('should not overflow', () => { 10 | const p = new Params(); 11 | assert.strictEqual(p.length, 1); 12 | assert.strictEqual(p.params[0], 0); 13 | p.addDigit(1); 14 | p.addDigit(2); 15 | p.addParam(); 16 | p.addDigit(3); 17 | p.addDigit(4); 18 | p.addParam(); 19 | p.addDigit(5); 20 | p.addDigit(6); 21 | p.addParam(); 22 | p.addDigit(7); 23 | p.addDigit(8); 24 | p.addParam(); 25 | p.addDigit(9); 26 | p.addDigit(9); 27 | p.addParam(); 28 | p.addDigit(1); 29 | p.addDigit(1); 30 | p.addParam(); 31 | assert.strictEqual(p.length, 6); 32 | assert.deepStrictEqual(p.params, new Uint32Array([12, 34, 56, 78, 99, 0])); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /src/Params.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021 Joerg Breitbart. 3 | * @license MIT 4 | */ 5 | 6 | 7 | /** 8 | * Params storage. 9 | * Used during parsing to hold up to 6 params of a SIXEL command. 10 | */ 11 | export class Params { 12 | public length = 1; 13 | public params = new Uint32Array(6); 14 | public reset(): void { 15 | this.params[0] = 0; 16 | this.length = 1; 17 | } 18 | public addParam(): void { 19 | if (this.length < 6) { 20 | this.params[this.length++] = 0; 21 | } 22 | } 23 | public addDigit(v: number): void { 24 | if (this.length < 6) { 25 | this.params[this.length - 1] = this.params[this.length - 1] * 10 + v; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Quantizer.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2020 Joerg Breitbart. 3 | * @license MIT 4 | * 5 | * Parts taken from UPNG: 6 | * MIT License, Copyright (c) 2017 Photopea 7 | */ 8 | import { red, green, blue, fromRGBA8888 } from './Colors'; 9 | import { IQuantResult, RGBColor, RGBA8888 } from './Types'; 10 | import { quantize as UPNGQuantize } from './upng'; 11 | 12 | 13 | function clamp8Bit(value: number): number { 14 | return value >= 255 ? 255 : value < 0 ? 0 : value; 15 | } 16 | 17 | 18 | function applyError(value: number, r: number, g: number, b: number): number { 19 | return ((0xFF00 | clamp8Bit(blue(value) + b)) 20 | << 8 | clamp8Bit(green(value) + g)) 21 | << 8 | clamp8Bit(red(value) + r); 22 | } 23 | 24 | 25 | /** 26 | * The internal quantizer currently relies on the kd tree quanization from UPNG. 27 | * 28 | * Planned: 29 | * - SIXEL optimized quantizer without alpha channel and reduced RGB (only 1M colors possible) 30 | * - better/customizable dithering algos 31 | * - separate palette creation from image reduction 32 | * - support for predefined palette (needs reconstruction of kd tree) 33 | * - grayscale / monochrome transformations 34 | * 35 | * FIXME: Dithering should respect image dimensions. 36 | */ 37 | export function reduce( 38 | data: Uint8Array | Uint8ClampedArray, 39 | width: number, 40 | colors: number): IQuantResult 41 | { 42 | const data32 = new Uint32Array(data.buffer); 43 | 44 | // palette creation with kd tree 45 | const KD = UPNGQuantize.getKDtree(data.slice(), colors); 46 | const leafs = KD[1]; 47 | const palette = leafs.map((el: any) => fromRGBA8888(el.est.rgba)); 48 | const cm = new ColorMatcher(palette); 49 | 50 | const indices = new Uint16Array(data32.length); 51 | const len = data32.length; 52 | for (let i = 0; i < len; ++i) { 53 | const v = data32[i]; 54 | const r = red(v); 55 | const g = green(v); 56 | const b = blue(v); 57 | 58 | // boxed matching - compromise between exact match and performance 59 | const idx = cm.nearest(v); 60 | indices[i] = idx; 61 | 62 | // dithering - FIXME: find better algo in terms of output quality and speed 63 | const vp = leafs[idx].est.rgba; 64 | let er = (r - red(vp)) >> 2; 65 | let eg = (g - green(vp)) >> 2; 66 | let eb = (b - blue(vp)) >> 2; 67 | 68 | // FIXME: respect idx overflow / left and right border 69 | data32[i + 1] = applyError(data32[i + 1], er, eg, eb); 70 | data32[i + width] = applyError(data32[i + width], er, eg, eb); 71 | 72 | er >>= 1; 73 | eg >>= 1; 74 | eb >>= 1; 75 | data32[i + width - 1] = applyError(data32[i + width - 1], er, eg, eb); 76 | data32[i + width + 1] = applyError(data32[i + width + 1], er, eg, eb); 77 | } 78 | return { indices, palette: leafs.map((el: any) => el.est.rgba) }; 79 | } 80 | 81 | 82 | /** 83 | * Class to do nearest palette color matching with 16x16x16 boxes. 84 | */ 85 | class ColorMatcher { 86 | private _boxes: { [key: number]: number[] } = {}; 87 | private _boxes2: { [key: number]: number[] } = {}; 88 | 89 | constructor(public palette: RGBColor[], radius: number = 14, radius2: number = 42) { 90 | // limit: search sphere to add palette points from 91 | // limit2: outer search sphere for uncertain area 92 | // the value is chosen to get an error rate > 5% 93 | // while not penalizing runtime too much (inner sphere is good trade off) 94 | const limit = radius * radius * 3; 95 | const limit2 = radius2 * radius2 * 3; 96 | for (let i = 0; i < 4096; ++i) { 97 | const x = i >> 8; 98 | const y = i >> 4 & 15; 99 | const z = i & 15; 100 | this._nearestPoints(i, (x << 4) + 8, (y << 4) + 8, (z << 4) + 8, limit, limit2); 101 | } 102 | } 103 | 104 | private _nearestPoints(box: number, r: number, g: number, b: number, limit: number, limit2: number): void { 105 | let min = Number.MAX_SAFE_INTEGER; 106 | let idx = -1; 107 | const pointIndices: number[] = []; 108 | const pointIndices2: number[] = []; 109 | for (let i = 0; i < this.palette.length; ++i) { 110 | const p_color = this.palette[i]; 111 | const d = this._distance(r, g, b, p_color[0], p_color[1], p_color[2]); 112 | if (d < min) { 113 | min = d; 114 | idx = i; 115 | } 116 | if (d < limit) { 117 | pointIndices.push(i); 118 | } else if (d < limit2) { 119 | pointIndices2.push(i); 120 | } 121 | } 122 | if (pointIndices.length === 0) { 123 | pointIndices.push(idx); 124 | } 125 | this._boxes[box] = pointIndices; 126 | this._boxes2[box] = pointIndices2; 127 | } 128 | 129 | private _distance(r1: number, g1: number, b1: number, r2: number, g2: number, b2: number): number { 130 | const dr = r1 - r2; 131 | const dg = g1 - g2; 132 | const db = b1 - b2; 133 | return dr * dr + dg * dg + db * db; 134 | } 135 | 136 | public nearest(color: RGBA8888): number { 137 | const r = red(color); 138 | const g = green(color); 139 | const b = blue(color); 140 | const box = ((r >> 4) << 8) | ((g >> 4) << 4) | (b >> 4); 141 | const indices = this._boxes[box]; 142 | let min = Number.MAX_SAFE_INTEGER; 143 | let idx = -1; 144 | // inner sphere handling 145 | for (let i = 0; i < indices.length; ++i) { 146 | const p_color = this.palette[indices[i]]; 147 | const d = this._distance(r, g, b, p_color[0], p_color[1], p_color[2]); 148 | if (!d) return indices[i]; 149 | if (d < min) { 150 | min = d; 151 | idx = indices[i]; 152 | } 153 | } 154 | // check for outer sphere if point is within uncertain area (d > 8*8 + 8*8 + 8*8) 155 | if (this._distance(r, g, b, (r & 0xF0) + 8, (g & 0xF0) + 8, (b & 0xF0) + 8) > 192) { 156 | const indices = this._boxes2[box]; 157 | for (let i = 0; i < indices.length; ++i) { 158 | const p_color = this.palette[indices[i]]; 159 | const d = this._distance(r, g, b, p_color[0], p_color[1], p_color[2]); 160 | if (d < min) { 161 | min = d; 162 | idx = indices[i]; 163 | } 164 | } 165 | } 166 | return idx; 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/SixelEncoder.test.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2019 Joerg Breitbart. 3 | * @license MIT 4 | */ 5 | 6 | import * as assert from 'assert'; 7 | import { introducer, FINALIZER, sixelEncode } from './SixelEncoder'; 8 | import { fromRGBA8888, normalizeRGB, toRGBA8888 } from './Colors'; 9 | import { RGBA8888 } from './Types'; 10 | import { Decoder } from './Decoder'; 11 | 12 | describe('encoding', () => { 13 | it('DCS introducer supports P2', () => { 14 | /** 15 | * DEC STD 070 - 1st should set to 0 16 | * Note: 17 | * - we only support P2 18 | * - no 8 bit DCS support, only 7 bit notation 19 | */ 20 | assert.strictEqual(introducer(), '\x1bP0;0;q'); 21 | assert.strictEqual(introducer(0), '\x1bP0;0;q'); 22 | assert.strictEqual(introducer(1), '\x1bP0;1;q'); 23 | assert.strictEqual(introducer(2), '\x1bP0;2;q'); 24 | }); 25 | it('FINALIZER 7bit ST', () => { 26 | assert.strictEqual(FINALIZER, '\x1b\\'); 27 | }); 28 | describe('sixelEncode', () => { 29 | it('empty data, width/height of 0', () => { 30 | let sixels = ''; 31 | assert.doesNotThrow(() => { sixels = sixelEncode(new Uint8Array(0), 1, 1, [0]); }); 32 | assert.strictEqual(sixels, ''); 33 | assert.doesNotThrow(() => { sixels = sixelEncode(new Uint8Array(25), 0, 1, [0]); }); 34 | assert.strictEqual(sixels, ''); 35 | assert.doesNotThrow(() => { sixels = sixelEncode(new Uint8Array(25), 1, 0, [0]); }); 36 | assert.strictEqual(sixels, ''); 37 | }); 38 | it('wrong geometry should throw', () => { 39 | assert.throws(() => { sixelEncode(new Uint8Array(8), 1, 1, [0]); }, /wrong geometry of data/); 40 | }); 41 | it('empty palette should throw', () => { 42 | assert.throws(() => { sixelEncode(new Uint8Array(4), 1, 1, []); }, /palette must not be empty/); 43 | }); 44 | describe('palette handling', () => { 45 | function getPalFromSixel(sixels: string): number[] { 46 | const pal: RGBA8888[] = []; 47 | sixels.split('#') 48 | .map(el => el.split(';')) 49 | .filter(el => el.length === 5) 50 | .forEach(e => { pal[~~e[0]] = normalizeRGB(~~e[2], ~~e[3], ~~e[4]); }); 51 | return pal; 52 | } 53 | it('accepts [r, g, b] and RGBA8888 as palette entries', () => { 54 | const data = new Uint8Array(8); 55 | data.fill(255); 56 | const sixels = sixelEncode(data, 2, 1, [[12, 34, 56], [98, 76, 54]]); 57 | const sixels2 = sixelEncode(data, 2, 1, [toRGBA8888(12, 34, 56), toRGBA8888(98, 76, 54)]); 58 | // compare with values read by decoder 59 | const dec = new Decoder(); 60 | dec.init(0, new Uint32Array(2), 2); 61 | dec.decodeString(sixels + '@'); 62 | assert.deepStrictEqual(new Uint32Array(getPalFromSixel(sixels)), dec.palette); 63 | assert.deepStrictEqual(new Uint32Array(getPalFromSixel(sixels2)), dec.palette); 64 | assert.strictEqual(getPalFromSixel(sixels).length, 2); 65 | }); 66 | it('should filter alpha=0 and doubles from palette', () => { 67 | const data = new Uint8Array(8); 68 | data.fill(255); 69 | const sixels = sixelEncode(data, 2, 1, [[12, 34, 56], [98, 76, 54]]); 70 | const sixels2 = sixelEncode(data, 2, 1, [ 71 | toRGBA8888(55, 66, 77, 0), 72 | toRGBA8888(12, 34, 56), 73 | toRGBA8888(55, 66, 88, 0), 74 | toRGBA8888(98, 76, 54), 75 | toRGBA8888(12, 34, 56) 76 | ]); 77 | // compare with values read by decoder 78 | const dec = new Decoder(); 79 | dec.init(0, new Uint32Array(2), 2); 80 | 81 | dec.decodeString(sixels + '@'); 82 | assert.deepStrictEqual(new Uint32Array(getPalFromSixel(sixels)), dec.palette); 83 | assert.deepStrictEqual(new Uint32Array(getPalFromSixel(sixels2)), dec.palette); 84 | assert.strictEqual(getPalFromSixel(sixels).length, 2); 85 | }); 86 | }); 87 | it('skip raster attributes in output', () => { 88 | const data = new Uint8Array(8); 89 | data.fill(255); 90 | // default - contains raster attributes 91 | const sixels = sixelEncode(data, 2, 1, [[12, 34, 56], [98, 76, 54]]); 92 | assert.strictEqual(sixels.indexOf('"1;1;2;1'), 0); 93 | const sixels2 = sixelEncode(data, 2, 1, [[12, 34, 56], [98, 76, 54]], false); 94 | assert.strictEqual(sixels2.indexOf('"1;1;2;1'), -1); 95 | assert.strictEqual(sixels2, sixels.slice(8)); 96 | }); 97 | }); 98 | describe('encoding tests', () => { 99 | it('5 repeating pixels', () => { 100 | const data = new Uint8Array(20); 101 | data.fill(255); 102 | const sixels = sixelEncode(data, 5, 1, [[0, 0, 0], [255, 255, 255]]); 103 | // "#1" color slot[1], "!5" repeat 5, "@" 1st bit set 104 | assert.strictEqual(sixels.indexOf('#1!5@') !== -1, true); 105 | }); 106 | it('4 repeating pixels', () => { 107 | const data = new Uint8Array(20); 108 | data.fill(255); 109 | // set first pixel to 0 110 | data[0] = 0; data[1] = 0; data[2] = 0; 111 | const sixels = sixelEncode(data, 5, 1, [[0, 0, 0], [255, 255, 255]]); 112 | // "#0" color slot[0], "@" 1st bit set, "$" CR 113 | // "#1" color slot[1], "?" 0 bit set, "!4" repeat 4, "@" 1st bit set, "$" CR 114 | assert.strictEqual(sixels.indexOf('#0@$#1?!4@$') !== -1, true); 115 | }); 116 | it('3 repeating pixels', () => { 117 | const data = new Uint8Array(20); 118 | data.fill(255); 119 | data[0] = 0; data[1] = 0; data[2] = 0; 120 | data[4] = 0; data[5] = 0; data[6] = 0; 121 | const sixels = sixelEncode(data, 5, 1, [[0, 0, 0], [255, 255, 255]]); 122 | // "#0" color slot[0], "@@" 1st bit set, "$" CR 123 | // "#1" color slot[1], "?" 0 bit set, "@@@" 1st bit set, "$" CR 124 | // ==> ! length encoding for >3 125 | assert.strictEqual(sixels.indexOf('#0@@$#1??@@@$') !== -1, true); 126 | }); 127 | it('background pixel skipped', () => { 128 | const data = new Uint8Array(20); 129 | data.fill(255); 130 | data[0] = 0; data[1] = 0; data[2] = 0; 131 | data[4] = 0; data[5] = 0; data[6] = 0; data[7] = 0; // alpha 0 sets pixel transparent 132 | const sixels = sixelEncode(data, 5, 1, [[0, 0, 0], [255, 255, 255]]); 133 | // "#0@$" color[0] one pixel + CR 134 | // "#1??@@@$" color[1] skip 2 pixels + color 3 pixels + CR 135 | assert.strictEqual(sixels.indexOf('#0@$#1??@@@$') !== -1, true); 136 | }); 137 | }); 138 | }); 139 | -------------------------------------------------------------------------------- /src/SixelEncoder.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2019 Joerg Breitbart. 3 | * @license MIT 4 | */ 5 | 6 | import { RGBA8888, RGBColor } from './Types'; 7 | import { toRGBA8888, fromRGBA8888, alpha, nearestColorIndex } from './Colors'; 8 | import { reduce } from './Quantizer'; 9 | 10 | 11 | /** 12 | * Create escape sequence introducer for SIXEL. 13 | * Should be written to the terminal before any SIXEL data. 14 | * 15 | * A SIXEL DSC sequence understands 3 parameters, but only the second one (background select) is supported 16 | * by some terminals. Therefore only this parameter is exposed. 17 | * 18 | * backgroundSelect: 19 | * - 0 device default action (most terminals will apply background color) 20 | * - 1 no action (no change to zero bit value grid positions) 21 | * - 2 set to background color - zero bit value grid positions are set to background color (device dependent). 22 | * 23 | * @see https://www.vt100.net/docs/vt3xx-gp/chapter14.html 24 | * @param backgroundSelect background color setting (default = 0) 25 | */ 26 | export function introducer(backgroundSelect: 0 | 1 | 2 = 0): string { 27 | return `\x1bP0;${backgroundSelect};q`; 28 | } 29 | 30 | 31 | /** 32 | * Finalize SIXEL sequence. Write this, when the SIXEL data stream has ended to restore 33 | * the terminal to normal operation. 34 | */ 35 | export const FINALIZER = '\x1b\\'; 36 | 37 | 38 | /** 39 | * Convert 6 bit code to SIXEL string. 40 | */ 41 | function codeToSixel(code: number, repeat: number): string { 42 | const c = String.fromCharCode(code + 63); 43 | if (repeat > 3) return '!' + repeat + c; 44 | if (repeat === 3) return c + c + c; 45 | if (repeat === 2) return c + c; 46 | return c; 47 | } 48 | 49 | 50 | /** 51 | * Create SIXEL data for a 6 pixel band. 52 | */ 53 | function processBand( 54 | data32: Uint32Array, 55 | start: number, 56 | bandHeight: number, 57 | width: number, 58 | colorMap: Map, 59 | paletteRGB: RGBColor[]): string 60 | { 61 | // temp buffers to hold various color data 62 | // last: last seen SIXEL code per color 63 | // code: current SIXEL code per color 64 | // accu: count rows with equal SIXEL codes per color 65 | // slots: palette color --> idx in usedColorIdx 66 | const last = new Int8Array(paletteRGB.length + 1); 67 | const code = new Uint8Array(paletteRGB.length + 1); 68 | const accu = new Uint16Array(paletteRGB.length + 1); 69 | const slots = new Int16Array(paletteRGB.length + 1); 70 | 71 | last.fill(-1); 72 | accu.fill(1); 73 | slots.fill(-1); 74 | 75 | // array to hold band local color idx 76 | // only those are processed and written to output 77 | // whenever a new color enters here we have to extend the accu/code handling below 78 | const usedColorIdx: number[] = []; 79 | 80 | // storage for SIXELs per color in band 81 | const targets: string[][] = []; 82 | 83 | let oldColor = 0; 84 | let idx = 0; 85 | for (let i = 0; i < width; ++i) { 86 | const p = start + i; 87 | let rowOffset = 0; 88 | code.fill(0, 0, usedColorIdx.length); 89 | for (let row = 0; row < bandHeight; ++row) { 90 | const color = data32[p + rowOffset]; 91 | // skip expensive color to palette matching if we have same color as before 92 | if (color !== oldColor) { 93 | oldColor = color; 94 | idx = alpha(color) ? colorMap.get(color) || 0 : 0; 95 | if (idx === undefined) { 96 | idx = nearestColorIndex(color, paletteRGB) + 1; 97 | colorMap.set(color, idx); 98 | } 99 | // extend accu/code handling to new color 100 | if (slots[idx] === -1) { 101 | targets.push([]); 102 | // if not at start catch up by writing 0s up to i for new color 103 | // (happens during shift below) 104 | if (i) { 105 | last[usedColorIdx.length] = 0; 106 | accu[usedColorIdx.length] = i; 107 | } 108 | slots[idx] = usedColorIdx.length; 109 | usedColorIdx.push(idx); 110 | } 111 | } 112 | // update codes for a row of 6 pixels 113 | code[slots[idx]] |= 1 << row; 114 | rowOffset += width; 115 | } 116 | // code/last/accu shift, updates SIXELs per color in band 117 | for (let j = 0; j < usedColorIdx.length; ++j) { 118 | if (code[j] === last[j]) { 119 | accu[j]++; 120 | } else { 121 | if (~last[j]) { 122 | targets[j].push(codeToSixel(last[j], accu[j])); 123 | } 124 | last[j] = code[j]; 125 | accu[j] = 1; 126 | } 127 | } 128 | 129 | } 130 | // handle remaining SIXELs to EOL 131 | for (let j = 0; j < usedColorIdx.length; ++j) { 132 | if (last[j]) { 133 | targets[j].push(codeToSixel(last[j], accu[j])); 134 | } 135 | } 136 | // write sixel chunk for every color in band 137 | const result: string[] = []; 138 | for (let j = 0; j < usedColorIdx.length; ++j) { 139 | if (!usedColorIdx[j]) continue; // skip background 140 | result.push('#' + (usedColorIdx[j] - 1) + targets[j].join('') + '$'); 141 | } 142 | return result.join(''); 143 | } 144 | 145 | 146 | /** 147 | * sixelEncode - encode pixel data to SIXEL string. 148 | * 149 | * The colors of the image get aligned to the given palette, unmatched colors will be translated 150 | * by euclidean distance. Without proper quantization beforehand this leads to poor output quality, 151 | * thus consider using a quantizer with custom palette creation and dithering. 152 | * For transparency only an alpha value of 0 will be respected as fully transparent, 153 | * other alpha values are set to fully opaque (255). Transparent pixels will be colored by the 154 | * terminal later on depending on the `backgroundSelect` setting of the introducer. 155 | * 156 | * To be in line with the SIXEL spec (DEC STD 070) `palette` should not contain more than 256 colors. 157 | * Note that older devices limit color registers even further (16 on VT340). Furthermore a high 158 | * number of colors will have a penalty on creation time, temporary memory usage and 159 | * the size of the SIXEL data. For simple graphics a rather small palette (16 to 64) might do, 160 | * for complicated pictures higher should work with 128+. 161 | * 162 | * @param data pixel data 163 | * @param width width of the image 164 | * @param height height of the image 165 | * @param palette palette to be applied 166 | * @param rasterAttributes whether to write raster attributes (true) 167 | */ 168 | export function sixelEncode( 169 | data: Uint8ClampedArray | Uint8Array, 170 | width: number, 171 | height: number, 172 | palette: RGBA8888[] | RGBColor[], 173 | rasterAttributes: boolean = true): string 174 | { 175 | // some sanity checks 176 | if (!data.length || !width || !height) { 177 | return ''; 178 | } 179 | if (width * height * 4 !== data.length) { 180 | throw new Error('wrong geometry of data'); 181 | } 182 | if (!palette || !palette.length) { 183 | throw new Error('palette must not be empty'); 184 | } 185 | 186 | // cleanup/prepare palettes 187 | // paletteWithZero: holds background color in slot 0 188 | // paletteRGB: list of [R, G, B] for ED calc 189 | const paletteWithZero: RGBA8888[] = [0]; 190 | const paletteRGB: RGBColor[] = []; 191 | for (let i = 0; i < palette.length; ++i) { 192 | let color = palette[i]; 193 | if (typeof color === 'number') { 194 | if (!alpha(color)) continue; 195 | color = toRGBA8888(...fromRGBA8888(color)); 196 | } else { 197 | color = toRGBA8888(...color); 198 | } 199 | if (!~paletteWithZero.indexOf(color)) { 200 | paletteWithZero.push(color); 201 | paletteRGB.push(fromRGBA8888(color).slice(0, -1) as RGBColor); 202 | } 203 | } 204 | 205 | // SIXEL data storage 206 | const chunks: string[] = []; 207 | 208 | // write raster attributes (includes image dimensions) - " Pan ; Pad ; Ph ; Pv 209 | // note: Pan/Pad are set to dummies (not eval'd by any terminal) 210 | if (rasterAttributes) { 211 | chunks.push(`"1;1;${width};${height}`); 212 | } 213 | 214 | // create palette and write color entries 215 | for (let [idx, [r, g, b]] of paletteRGB.entries()) { 216 | chunks.push(`#${idx};2;${Math.round(r / 255 * 100)};${Math.round(g / 255 * 100)};${Math.round(b / 255 * 100)}`); 217 | } 218 | 219 | // color --> slot 220 | // if color does not match a palette color a suitable slot will be calculated from ED later on 221 | const colorMap = new Map(paletteWithZero.map((el, idx) => [el, idx])); 222 | 223 | // process in bands of 6 pixels 224 | const bands: string[] = []; 225 | const data32 = new Uint32Array(data.buffer); 226 | for (let b = 0; b < height; b += 6) { 227 | bands.push(processBand(data32, b * width, height - b >= 6 ? 6 : height - b, width, colorMap, paletteRGB)); 228 | } 229 | chunks.push(bands.join('-\n')); 230 | return chunks.join(''); 231 | } 232 | 233 | 234 | /** 235 | * Create SIXEL data for a 6 pixel band. 236 | * Same as `processBand`, but for correctly indexed colors. 237 | */ 238 | function processBandIndexed( 239 | indices: Uint16Array, 240 | start: number, 241 | bandHeight: number, 242 | width: number, 243 | last: Int8Array, 244 | code: Uint8Array, 245 | accu: Uint16Array, 246 | slots: Int16Array): string 247 | { 248 | // reset buffers 249 | last.fill(-1); 250 | code.fill(0); 251 | accu.fill(1); 252 | slots.fill(-1); 253 | 254 | // array to hold band local color idx 255 | // only those are processed and written to output 256 | // whenever a new color enters here we have to extend the accu/code handling below 257 | const usedColorIdx: number[] = []; 258 | 259 | // storage for SIXELs per color in band 260 | const targets: string[][] = []; 261 | 262 | for (let i = 0; i < width; ++i) { 263 | const p = start + i; 264 | let rowOffset = 0; 265 | code.fill(0, 0, usedColorIdx.length); 266 | for (let row = 0; row < bandHeight; ++row) { 267 | const idx = indices[p + rowOffset] + 1; // FIXME: handle alpha = 0 case 268 | if (slots[idx] === -1) { 269 | targets.push([]); 270 | // if not at start catch up by writing 0s up to i for new color 271 | // (happens during shift below) 272 | if (i) { 273 | last[usedColorIdx.length] = 0; 274 | accu[usedColorIdx.length] = i; 275 | } 276 | slots[idx] = usedColorIdx.length; 277 | usedColorIdx.push(idx); 278 | } 279 | // update codes for a row of 6 pixels 280 | code[slots[idx]] |= 1 << row; 281 | rowOffset += width; 282 | } 283 | // code/last/accu shift, updates SIXELs per color in band 284 | for (let j = 0; j < usedColorIdx.length; ++j) { 285 | if (code[j] === last[j]) { 286 | accu[j]++; 287 | } else { 288 | if (~last[j]) { 289 | targets[j].push(codeToSixel(last[j], accu[j])); 290 | } 291 | last[j] = code[j]; 292 | accu[j] = 1; 293 | } 294 | } 295 | 296 | } 297 | // handle remaining SIXELs to EOL 298 | for (let j = 0; j < usedColorIdx.length; ++j) { 299 | if (last[j]) { 300 | targets[j].push(codeToSixel(last[j], accu[j])); 301 | } 302 | } 303 | // write sixel chunk for every color in band 304 | const result: string[] = []; 305 | for (let j = 0; j < usedColorIdx.length; ++j) { 306 | if (!usedColorIdx[j]) continue; // skip background 307 | result.push('#' + (usedColorIdx[j] - 1) + targets[j].join('') + '$'); 308 | } 309 | return result.join(''); 310 | } 311 | 312 | /** 313 | * sixelEncodeIndexed - encode indexed image data to SIXEL string. 314 | * Same as `sixelEncode`, but for correctly indexed colors. 315 | */ 316 | export function sixelEncodeIndexed( 317 | indices: Uint16Array, 318 | width: number, 319 | height: number, 320 | palette: RGBA8888[] | RGBColor[], 321 | rasterAttributes: boolean = true): string 322 | { 323 | // some sanity checks 324 | if (!indices.length || !width || !height) { 325 | return ''; 326 | } 327 | if (width * height !== indices.length) { 328 | throw new Error('wrong geometry of data'); 329 | } 330 | if (!palette || !palette.length) { 331 | throw new Error('palette must not be empty'); 332 | } 333 | 334 | // cleanup/prepare palettes 335 | // paletteWithZero: holds background color in slot 0 336 | // paletteRGB: list of [R, G, B] for ED calc 337 | const paletteWithZero: RGBA8888[] = [0]; 338 | const paletteRGB: RGBColor[] = []; 339 | for (let i = 0; i < palette.length; ++i) { 340 | let color = palette[i]; 341 | if (typeof color === 'number') { 342 | if (!alpha(color)) continue; 343 | color = toRGBA8888(...fromRGBA8888(color)); 344 | } else { 345 | color = toRGBA8888(...color); 346 | } 347 | if (!~paletteWithZero.indexOf(color)) { 348 | paletteWithZero.push(color); 349 | paletteRGB.push(fromRGBA8888(color).slice(0, -1) as RGBColor); 350 | } 351 | } 352 | 353 | // SIXEL data storage 354 | const chunks: string[] = []; 355 | 356 | // write raster attributes (includes image dimensions) - " Pan ; Pad ; Ph ; Pv 357 | // note: Pan/Pad are set to dummies (not eval'd by any terminal) 358 | if (rasterAttributes) { 359 | chunks.push(`"1;1;${width};${height}`); 360 | } 361 | 362 | // create palette and write color entries 363 | for (let [idx, [r, g, b]] of paletteRGB.entries()) { 364 | chunks.push(`#${idx};2;${Math.round(r / 255 * 100)};${Math.round(g / 255 * 100)};${Math.round(b / 255 * 100)}`); 365 | } 366 | 367 | // temp buffers to hold various color data on band level 368 | // last: last seen SIXEL code per color 369 | // code: current SIXEL code per color 370 | // accu: count rows with equal SIXEL codes per color 371 | // slots: palette color --> idx in usedColorIdx 372 | const last = new Int8Array(paletteRGB.length + 1); 373 | const code = new Uint8Array(paletteRGB.length + 1); 374 | const accu = new Uint16Array(paletteRGB.length + 1); 375 | const slots = new Int16Array(paletteRGB.length + 1); 376 | 377 | // process in bands of 6 pixels 378 | const bands: string[] = []; 379 | for (let b = 0; b < height; b += 6) { 380 | bands.push(processBandIndexed(indices, b * width, height - b >= 6 ? 6 : height - b, width, 381 | last, code, accu, slots)); 382 | } 383 | chunks.push(bands.join('-\n')); 384 | return chunks.join(''); 385 | } 386 | 387 | 388 | /** 389 | * Convenient function to create a full SIXEL escape sequence for given image data (alpha). 390 | * 391 | * Quantization is done by the internal quantizer, with dithering done on 4 neighboring pixels 392 | * for speed reasons, which works great for real pictures to level out hard color plane borders, 393 | * but might show moiré or striping artefacts on color gradients. 394 | * Currently the dithering is not configurable, resort to custom quantizer 395 | * library in conjunction with `sixelEncode` if you observe dithering issues. 396 | * 397 | * @param data pixel data 398 | * @param width width of the image 399 | * @param height height of the image 400 | * @param maxColors max colors of the created palette 401 | * @param backgroundSelect background select behavior for transparent pixels 402 | */ 403 | export function image2sixel( 404 | data: Uint8Array | Uint8ClampedArray, 405 | width: number, 406 | height: number, 407 | maxColors: number = 256, 408 | backgroundSelect: 0 | 1 | 2 = 0): string 409 | { 410 | // FIXME: sixelEncodeIndexed does not yet handle transparent pixels 411 | // FIXME: dithering in reduce does not yet respect image width/height 412 | const { indices, palette } = reduce(data, width, maxColors); 413 | const sixelData = sixelEncodeIndexed(indices, width, height, palette); 414 | return [introducer(backgroundSelect), sixelData, FINALIZER].join(''); 415 | } 416 | -------------------------------------------------------------------------------- /src/Types.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2019 Joerg Breitbart. 3 | * @license MIT 4 | */ 5 | 6 | 7 | /** 8 | * This type denotes the byte order for 32 bit color values. 9 | * The resulting word order depends on the system endianess: 10 | * - big endian - RGBA32 11 | * - little endian - ABGR32 12 | * 13 | * Use `toRGBA8888` and `fromRGBA8888` to convert the color values 14 | * respecting the system endianess. 15 | * 16 | * Note: BE handling got removed from the library, thus this type 17 | * will always contain ABGR32. 18 | */ 19 | export type RGBA8888 = number; 20 | 21 | /** 22 | * Unsigned typed array supported by `Decoder.decode`. 23 | */ 24 | export type UintTypedArray = Uint8Array | Uint16Array | Uint32Array; 25 | 26 | /** 27 | * RGB color as array of channels (without alpha channel). 28 | */ 29 | export type RGBColor = [number, number, number]; 30 | 31 | /** 32 | * Return value from internal quantizer. 33 | */ 34 | export interface IQuantResult { 35 | /** image data as palette indices (max. 2^16 colors supported) */ 36 | indices: Uint16Array; 37 | /** array with quantized colors */ 38 | palette: number[]; 39 | } 40 | 41 | 42 | /** 43 | * Decoder options. 44 | */ 45 | export interface IDecoderOptions { 46 | /** 47 | * Maximum memory in bytes a decoder instance is allowed to allocate during decoding. 48 | * Exceeding this value will reset the decoder (abort current decoding + release memory) 49 | * and throw an exception. 50 | * The default of 256 MB is chosen rather high as an emergency stop. 51 | * Setting this to 0 will skip the memory checks. 52 | */ 53 | memoryLimit?: number; 54 | /** 55 | * Standard sixel foreground color (default: white). 56 | * This color should have a high contrast to the background fill color. 57 | * The value can be overridden for individual images at `init`. 58 | */ 59 | sixelColor?: RGBA8888; 60 | /** 61 | * Standard background fill color (default: black). 62 | * This color should have a high contrast to the sixel foreground color. 63 | * The value can be overridden for individual images at `init`. 64 | */ 65 | fillColor?: RGBA8888; 66 | /** 67 | * Standard palette to be used by the decoder. 68 | * Default is 16-color palette of VT340 (PALETTE_VT340_COLOR). 69 | */ 70 | palette?: Uint32Array; 71 | /** 72 | * Standard palette size limit. 73 | * Color registers in image data exceeding this value will be mapped back with modulo. 74 | * Default is 256, as suggest by the specification. 75 | * Maximum is the wasm compile time setting PALETTE_SIZE (default: 4096). 76 | */ 77 | paletteLimit?: number; 78 | /** 79 | * Whether to allow truncating of the image to given dimensions from raster attributes. 80 | * This setting only applies to images, that follow the level 2 format. 81 | * Default is true. 82 | */ 83 | truncate?: boolean; 84 | } 85 | 86 | /** 87 | * Return type of decode and decodeAsync. 88 | */ 89 | export interface IDecodeResult { 90 | width: number; 91 | height: number; 92 | data32: Uint32Array; 93 | data8: Uint8ClampedArray; 94 | } 95 | 96 | export interface IDecoderProperties { 97 | width: number; 98 | height: number; 99 | mode: ParseMode; 100 | level: number; 101 | truncate: boolean; 102 | paletteLimit: number; 103 | fillColor: RGBA8888; 104 | memUsage: number; 105 | rasterAttributes: { 106 | numerator: number; 107 | denominator: number; 108 | width: number; 109 | height: number; 110 | }; 111 | } 112 | 113 | 114 | /** 115 | * Internal types. 116 | */ 117 | 118 | 119 | // decoder options used internally 120 | export type IDecoderOptionsInternal = { 121 | [P in keyof IDecoderOptions]-?: IDecoderOptions[P]; 122 | }; 123 | 124 | // type helper for DecoderAsync 125 | export interface InstanceLike extends WebAssembly.Instance { 126 | module?: WebAssembly.Module; 127 | instance?: WebAssembly.Instance; 128 | } 129 | 130 | // parser operation modes 131 | export const enum ParseMode { 132 | M0 = 0, // image processing mode still undecided 133 | M1 = 1, // level 1 image or level 2 + truncate=false 134 | M2 = 2 // level 2 + truncate=true 135 | } 136 | 137 | // wasm decoder export interface 138 | export interface IWasmDecoderExports extends Record { 139 | memory: WebAssembly.Memory; 140 | get_state_address(): number; 141 | get_chunk_address(): number; 142 | get_p0_address(): number; 143 | get_palette_address(): number; 144 | init(sixelColor: number, fillColor: number, paletteLimit: number, truncate: number): void; 145 | decode(start: number, end: number): void; 146 | current_width(): number; 147 | current_height(): number; 148 | } 149 | 150 | // wasm decoder 151 | export interface IWasmDecoder extends WebAssembly.Instance { 152 | exports: IWasmDecoderExports; 153 | } 154 | 155 | 156 | /** 157 | * OLD (to be removed) 158 | */ 159 | 160 | export const enum SixelState { 161 | DATA = 0, 162 | COMPRESSION = 1, 163 | ATTR = 2, 164 | COLOR = 3 165 | } 166 | 167 | export const enum SixelAction { 168 | IGNORE = 0, 169 | DRAW = 1, 170 | CR = 2, 171 | LF = 3, 172 | REPEATED_DRAW = 4, 173 | STORE_PARAM = 5, 174 | SHIFT_PARAM = 6, 175 | APPLY_PARAM = 7 176 | } 177 | -------------------------------------------------------------------------------- /src/bundle_decode.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021 Joerg Breitbart. 3 | * @license MIT 4 | */ 5 | 6 | export { 7 | Decoder, 8 | DecoderAsync, 9 | decode, 10 | decodeAsync, 11 | } from './Decoder'; 12 | export { 13 | toRGBA8888, 14 | fromRGBA8888, 15 | PALETTE_ANSI_256, 16 | PALETTE_VT340_COLOR, 17 | PALETTE_VT340_GREY 18 | } from './Colors'; 19 | export { 20 | IDecodeResult, 21 | IDecoderOptions, 22 | RGBA8888, 23 | RGBColor 24 | } from './Types'; 25 | -------------------------------------------------------------------------------- /src/bundle_encode.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2021 Joerg Breitbart. 3 | * @license MIT 4 | */ 5 | 6 | export { 7 | sixelEncode, 8 | introducer, 9 | FINALIZER, 10 | image2sixel 11 | } from './SixelEncoder'; 12 | export { 13 | toRGBA8888, 14 | fromRGBA8888, 15 | PALETTE_ANSI_256, 16 | PALETTE_VT340_COLOR, 17 | PALETTE_VT340_GREY 18 | } from './Colors'; 19 | export { 20 | RGBA8888, 21 | RGBColor 22 | } from './Types'; 23 | -------------------------------------------------------------------------------- /src/index.benchmark.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2019 Joerg Breitbart. 3 | * @license MIT 4 | */ 5 | 6 | import { RuntimeCase, perfContext, before, ThroughputRuntimeCase } from 'xterm-benchmark'; 7 | import { toRGBA8888, introducer, FINALIZER, sixelEncode } from './index'; 8 | import * as fs from 'fs'; 9 | import { RGBA8888 } from './Types'; 10 | import { Decoder } from './Decoder'; 11 | import { ICaseResult, IPerfCase } from 'xterm-benchmark/lib/interfaces'; 12 | 13 | 14 | // test data: 9-bit palette in 10x10 tiles (512 colors: 8*8*8) - 640x80 -> 6 rows => 640x480 15 | const { SOURCE32, SOURCE8, PALETTE, SIXELSTRING, SIXELBYTES } = (() => { 16 | const channelValues = Array.from(Array(8).keys()).map(v => v * 32); 17 | const palette: RGBA8888[] = []; 18 | for (let r = 0; r < channelValues.length; ++r) { 19 | for (let g = 0; g < channelValues.length; ++g) { 20 | for (let b = 0; b < channelValues.length; ++b) { 21 | palette.push(toRGBA8888(channelValues[r], channelValues[g], channelValues[b])); 22 | } 23 | } 24 | } 25 | const source32 = new Uint32Array(512 * 10 * 10 * 6); 26 | for (let row = 0; row < 6; ++row) { 27 | for (let colorIdx = 0; colorIdx < 512; ++colorIdx) { 28 | const cy = colorIdx % 8; 29 | const cx = Math.floor(colorIdx / 8); 30 | for (let y = 0; y < 10; ++y) { 31 | for (let x = 0; x < 10; ++x) { 32 | source32[row * 640 * 80 + cy * 8 * 8 * 10 * 10 + y * 8 * 8 * 10 + cx * 10 + x] = palette[colorIdx]; 33 | } 34 | } 35 | } 36 | } 37 | const source8 = new Uint8Array(source32.buffer); 38 | const sixelString = sixelEncode(source8, 640, 480, palette); 39 | const bytes = new Uint8Array(sixelString.length); 40 | for (let i = 0; i < sixelString.length; ++i) bytes[i] = sixelString.charCodeAt(i); 41 | return { 42 | SOURCE32: source32, 43 | SOURCE8: source8, 44 | PALETTE: palette, 45 | SIXELSTRING: sixelString, 46 | SIXELBYTES: bytes 47 | }; 48 | })(); 49 | const TARGET = new Uint8ClampedArray(512 * 10 * 10 * 6 * 4); 50 | 51 | 52 | perfContext('testimage', () => { 53 | 54 | perfContext('decode', () => { 55 | const wasmDec = new Decoder(); 56 | new RuntimeCase('decode', () => { 57 | wasmDec.init(); 58 | wasmDec.decode(SIXELBYTES); 59 | }, { repeat: 20 }).showAverageRuntime(); 60 | new RuntimeCase('decodeString', () => { 61 | wasmDec.init(); 62 | wasmDec.decodeString(SIXELSTRING); 63 | }, { repeat: 20 }).showAverageRuntime(); 64 | }); 65 | 66 | perfContext('encode', () => { 67 | new RuntimeCase('sixelEncode', () => { 68 | return sixelEncode(SOURCE8, 640, 480, PALETTE).length; 69 | }, { repeat: 20 }).showAverageRuntime(); 70 | // }, {repeat: 1, fork: true, forkOptions: {execArgv: ['--inspect-brk']}}).showAverageRuntime(); 71 | }); 72 | }); 73 | 74 | 75 | const TEST1 = fs.readFileSync(__dirname + '/../testfiles/test1_clean.sixel'); 76 | const TEST2 = fs.readFileSync(__dirname + '/../testfiles/test2_clean.sixel'); 77 | const SAMPSA = fs.readFileSync(__dirname + '/../testfiles/sampsa_reencoded_clean.six'); 78 | 79 | // const FHD1 = fs.readFileSync(__dirname + '/../testfiles/fhd1_clean.six'); 80 | // const FHD2 = fs.readFileSync(__dirname + '/../testfiles/fhd2_clean.six'); 81 | 82 | // create 1920 x 1080 random noise in 12bit-RGB 83 | // const channelValues = Array.from(Array(16).keys()).map(v => v * 16); 84 | // const palette: RGBA8888[] = []; 85 | // for (let r = 0; r < channelValues.length; ++r) { 86 | // for (let g = 0; g < channelValues.length; ++g) { 87 | // for (let b = 0; b < channelValues.length; ++b) { 88 | // palette.push(toRGBA8888(channelValues[r], channelValues[g], channelValues[b])); 89 | // } 90 | // } 91 | // } 92 | // const pixels = new Uint32Array(2073600); 93 | // for (let i = 0; i < pixels.length; ++i) { 94 | // pixels[i] = palette[Math.floor(Math.random() * 4096)]; 95 | // } 96 | // const NOISE_STRING = sixelEncode(new Uint8Array(pixels.buffer), 1920, 1080, palette); 97 | // fs.writeFileSync('testfiles/fullhd_12bit_noise_clean.six', NOISE_STRING); 98 | const NOISE = fs.readFileSync(__dirname + '/../testfiles/fullhd_12bit_noise_clean.six'); 99 | 100 | 101 | perfContext('decode - testfiles', () => { 102 | let wasmDec: Decoder; 103 | before(() => { 104 | wasmDec = new Decoder(); 105 | }); 106 | new ThroughputRuntimeCase('test1_clean.sixel', () => { 107 | wasmDec.init(); 108 | wasmDec.decode(TEST1); 109 | return { payloadSize: TEST1.length, pixelSize: wasmDec.width * wasmDec.height }; 110 | }, { repeat: 20 }).showAverageRuntime().showAverageThroughput().postAll(sixelStats); 111 | new ThroughputRuntimeCase('test2_clean.sixel', () => { 112 | wasmDec.init(); 113 | wasmDec.decode(TEST2); 114 | return { payloadSize: TEST2.length, pixelSize: wasmDec.width * wasmDec.height }; 115 | }, { repeat: 20 }).showAverageRuntime().showAverageThroughput().postAll(sixelStats); 116 | new ThroughputRuntimeCase('sampsa_reencoded_clean.six', () => { 117 | wasmDec.init(); 118 | wasmDec.decode(SAMPSA); 119 | return { payloadSize: SAMPSA.length, pixelSize: wasmDec.width * wasmDec.height }; 120 | }, { repeat: 20 }).showAverageRuntime().showAverageThroughput().postAll(sixelStats); 121 | new ThroughputRuntimeCase('FullHD 12bit noise', () => { 122 | wasmDec.init(); 123 | wasmDec.decode(NOISE); 124 | return { payloadSize: NOISE.length, pixelSize: wasmDec.width * wasmDec.height }; 125 | }, { repeat: 20 }).showAverageRuntime().showAverageThroughput().postAll(sixelStats); 126 | 127 | 128 | new ThroughputRuntimeCase('640x480 9bit tiles', () => { 129 | wasmDec.init(); 130 | wasmDec.decode(SIXELBYTES); 131 | return { payloadSize: SIXELBYTES.length, pixelSize: 640 * 480 }; 132 | }, { repeat: 20 }).showAverageRuntime().showAverageThroughput().postAll(sixelStats); 133 | 134 | // new ThroughputRuntimeCase('FullHD 1', () => { 135 | // wasmDec.init(); 136 | // wasmDec.decode(FHD1); 137 | // return { payloadSize: FHD1.length, pixelSize: wasmDec.width * wasmDec.height }; 138 | // }, { repeat: 20 }).showAverageRuntime().showAverageThroughput().postAll(sixelStats); 139 | // new ThroughputRuntimeCase('FullHD 2', () => { 140 | // wasmDec.init(); 141 | // wasmDec.decode(FHD2); 142 | // return { payloadSize: FHD2.length, pixelSize: wasmDec.width * wasmDec.height }; 143 | // }, { repeat: 20 }).showAverageRuntime().showAverageThroughput().postAll(sixelStats); 144 | }); 145 | 146 | function sixelStats(results: ICaseResult[], perfCase: IPerfCase) { 147 | return; 148 | let runtime = 0; 149 | let pixels = 0; 150 | for (const r of results) { 151 | runtime += r.runtime[0] * 1000 + r.runtime[1] / 1000000; 152 | pixels += r.returnValue.pixelSize; 153 | } 154 | const fps = results.length / runtime * 1000; 155 | const pps = pixels / runtime * 1000; 156 | const pixelWrite = pixels * 4 / runtime * 1000; 157 | console.log( 158 | `${perfCase.getIndent()} --> image throughput`, 159 | { 160 | FPS: fps.toFixed(2), 161 | PPS: fmtBig(pps), 162 | pixelWrite: fmtBig(pixelWrite) + 'B/s', 163 | } 164 | ); 165 | } 166 | 167 | function fmtBig(v: number): string { 168 | return v > 1000000000 169 | ? (v / 1000000000).toFixed(2) + ' G' 170 | : v > 1000000 171 | ? (v / 1000000).toFixed(2) + ' M' 172 | : v > 1000 173 | ? (v / 1000).toFixed(2) + ' K' 174 | : '' + v; 175 | } 176 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2019, 2021 Joerg Breitbart. 3 | * @license MIT 4 | */ 5 | 6 | export { 7 | Decoder, 8 | DecoderAsync, 9 | decode, 10 | decodeAsync, 11 | } from './Decoder'; 12 | 13 | export { 14 | sixelEncode, 15 | introducer, 16 | FINALIZER, 17 | image2sixel 18 | } from './SixelEncoder'; 19 | 20 | export { 21 | toRGBA8888, 22 | fromRGBA8888, 23 | PALETTE_ANSI_256, 24 | PALETTE_VT340_COLOR, 25 | PALETTE_VT340_GREY, 26 | DEFAULT_BACKGROUND, 27 | DEFAULT_FOREGROUND 28 | } from './Colors'; 29 | 30 | export { 31 | IDecodeResult, 32 | IDecoderOptions, 33 | RGBA8888, 34 | RGBColor, 35 | UintTypedArray 36 | } from './Types'; 37 | -------------------------------------------------------------------------------- /src/upng.ts: -------------------------------------------------------------------------------- 1 | interface IQuantizer { 2 | getKDtree(data: Uint8Array | Uint8ClampedArray, colors: number): any; 3 | } 4 | 5 | export const quantize: IQuantizer = require('../upng').quantize; 6 | -------------------------------------------------------------------------------- /testfiles/cat2.six: -------------------------------------------------------------------------------- 1 | P;1q 2 | #0;1;280;35;60 3 | #1;1;0;0;0 4 | #2;1;120;50;100 5 | #3;1;0;99;0 6 | - 7 | $#1???!424?!8_- 8 | $#1???!373?owwK{C{C}E}A}AyE}C{C{C{KwGwGwGwK{C{E}A}A}A}A}B~@~@~@~?~?}@~@~@~@~B}A 9 | }EsK{GgWwOoOoOOoo!7_ 10 | $#2???!376?_?o?w?w?{?{?w?w?w?w?o?o?o?o?o?o?w?w?{?{?{?{?{?}?}?}?~?}?}?}?}?{?{?w? 11 | o?o?_?_?_?_- 12 | $#1???!368?_owWKNNFCFEBAABB@@BBABABABABEFKNW^O~o~_~?~?~?~?~?~?~?~?~?~?~?~?~?~?~ 13 | ?~?~?~?~?~?~?~?~?~?~?~?~?~?~@~@~@|B~A}A}A}E{C{CsK{Gw 14 | GwGwWoOOoo___ 15 | $#2???!376?B?@?@!9?@?@?@?@?@?B?F?N?^?^?~?~?~?~?~?~?~?~?~?~?~?~?~?~?~?~?~?~?~?~? 16 | ~?~?~?~?~?~?~?}?}?}?{?{?{?{?w?w?w?o?o?o?o?_?_ 17 | $#3???!371?__oo!4w!16{wwoo!4_- 18 | $#1???!368?FNkww!14?!17_??!6@BBABEFCNKNW^W~w~o~o~o~_~_~_~?~?~?~?~_~_~_~_~_~_~_~ 19 | _no~O^O^O^O^O^O^O^WNGNGNGNGNGNGNW^O^O^P\R^Q]QYU]SS{{ 20 | MFB@BB@@BBAA]}o 21 | $#2???!416?@?@?B?F?F?F?F?N?N?^?^?^?^?~?~?~?^?^?^?^?^?^?^?^?N?N?N?N?N?N?N?N?F?F? 22 | F?F?F?F?F?F?F?N?M?K?K?K?G?G 23 | $#3???!371?FF!14~!18^!6}!4{!4woo!4_!90?ww!8{__- 24 | $#1???!367?o}^B@?_oOWKKKCEFbppXXH!4G!8?_``ppPPZZIMMEEEM!4EM!5K\^vvbBBAEEC!4t||! 25 | 4xh!5gww!10OoowG!42?BBAEECKKGGKKCEEBB 26 | $#2???!376?_?o?o?w?K?e?u?v?v?~?~?~?~?]?M?M?C?C!19?G?[?{?w?G?G!7?O?O?O!5?_?_?_?_ 27 | $#3???!369?__}}NNFF!4B!25?!4_oppxhxp!4xppRrrr__!86?@@!8B@@- 28 | $#1???!346?__ckwoOOO[[OOPZ^NMKK[\NNEFDDKKGG?!7_!4?_ooW[KEEBB@@@!27?@BFE{{_?F^^N 29 | !5FfbbrrRRZZLMEAABB!5@ 30 | $#2???!354?_?_?_?_?_?o?_?_?o?w?q?v?~?^?^?^?^?~?^?N?B?@!37?@?B?w?_?w?w?w?[?K?K?C 31 | ???@ 32 | $#3???!393?__oWw{{}}}!22~}!4~}swG- 33 | $#1???!342?CEFDDDCCCS\LNJz~vfFN^^!4?o{{cEABA?@@@!5?@BBEM^zo!21?K]]K!17?N~w__oo[ 34 | [FFPp`?wXHHLLFF!4EAA 35 | $#2???!348?B?B?A!7?W?_?~?f?B?B?@???@!61?F?N?B?_?g?]?F?E?A 36 | $#3???!372?w{{{!4}!5~}{{w__CN~|!19~r``r!15~{~o- 37 | $#1???!354?AEK]zpb}{{___fNLWoo___??ENNE!4?___oWNN?EEKKGWWoo!6_o!7Oo!4_!9?_ow[NF 38 | @?@BECE]]}{LFBA 39 | $#2???!360?K?@?N?@!24?o?w?o?o?_!36?o?}?{?z?`?@?A 40 | $#3???!371?NN^^^~~xoox!4~^^^Nf?o?x@r@vBfNN!6^NnNnNnNnN!4^!9~^NFB- 41 | $#1???!355?_o[{~vFNZp_!9@BBBEAAEAA@@@?@BBEEkk{{[MMBB?@@B!7?C!6?@@@B!4@AFBLLww_? 42 | ?CK]ua!4B@BA 43 | $#2???!391?@?B?B?@!34?A?F?^?z?`?@ 44 | $#3???!362?o?K?}?}?}?}?}?{?x@|@|@}?}?}?{?x?R?B?`?o?{?}?~?~?~?~?~?~?~?}?}?}?}?|? 45 | {?_- 46 | $#1???!354?@@?N^Zro!4?__!24?Kr@!28?oue!5Ccc~~JJJIIIZPRU[W!6Oo!8_ 47 | $#2???!440?@ 48 | $#3???!360?C?N?~?^?~?~?~?~?~?~?~?~?~?~?~?~?r?K?~?~?~?~?~?~?~?~?~?~?~?~?~?~?H?z? 49 | z?z?Z- 50 | $#1???!360?br]MMNXWoo{{__?G???G!6?___oo{{v{{ww!4o!6_!4?!5_oo!4OwxNMK[vf@@ 51 | $#3???!364?@?A?F?B?^?^?~?~?~?~?~?^?^?N?B???B?F?N?N?^?^?^?~?^?^?^?N?N?N?E?@?B- 52 | $#1???!352?_oOWKEAB@!8?{~B!5@`brZNnxx@@!4?x!18~|xxp??KK^~|[scD@!4?@BEKWo_ 53 | $#2???!384?E?}?~?~!21?E?M?r?_- 54 | $#1???!343?OWGKCEAB@@!15?MN@!6?AEKWr||~~!5?^!19~^N~{!4?F~{_@BEKG!8?@@ 55 | $#2???!388?~?~!20?_!4?~?~ 56 | $#3???!412?_- 57 | $#1???!378?GKEB`o~^!6?!18~{__`BBAAEECDD!11CKK!17GWWOOoo!4_ 58 | $#2???!386?_?~?~!20?^?[?{?w@w@w?w?w?w?w?w?o?o?o?o?o?o?o?o?o?o?_?_ 59 | $#3???!412?^?[?{?w?w?w?w?w?w?w?w?o?o?o?o?o?o?o?o?o?o?_?_- 60 | $#1???!379?cuzx~NB@!5?B!15~rp__!6@BB!5A!36?@@@FFK{w 61 | $#2???!386?}?~?~?{!16?M?]?}?}?{?{?|?|?~?~?~?~?~?~?~?~?~?~?~?~?~?~?~?~?~?~?}?w?o 62 | $#3???!410?M?]?}?}?{?{?|?|?~?~?~?~?~?~?~?~?~?~?~?~?~?~?~?~?~?~?}?w?o- 63 | $#1???!376?CEb`}~RWKC!7?@BFnn!4N^^~!6^NNNffvp^^BBB}}YqaA!6CG!8O!5_!6?!4_owWGGKK 64 | EFB 65 | $#2???!384?r?~?~?~?~?{?O?o?o?_???_?_?_?o?O?G?_?{?{@?@?@?B?B?B?F?N?N?N?N?^?^?~?~ 66 | ?~?^?^?F?F?B?@ 67 | $#3???!424?@?@?@?B?B?B?F?N?N?N?N?^?^?~?~?~?^?^?F?F?B?@- 68 | $#1???!375?CEz|NF!5?!6_!4?@@@BBAA!5IJB!6@!6?C^~qAEECCKWo_!14?!8@ 69 | $#2???!380?o?~?~?^?^?^?^?~?}?}?{?|?t?t?s?}?}?}?~?~?~?z 70 | $#3???!425?KK!4w__- 71 | $#1???!369?wwKKCEEABB!9@??!9@BB!4AEECCCEEAAAEECCEEABB@@!8?@BE}{_ 72 | $#2???!402?@?@?@?B?@?@?@?@?B?@ 73 | $#3???!371?oo!4w{{!20}!6{!8w{{!6w!4{}}!6~}}ww- 74 | $#1???!369?bfess[[GGWWOOOoo!5_!32?___???wwG?_{}E??BBM}w 75 | $#3???!371?@@BBbb!4fnnNN!6^!31~^^^~~~FFv~^B@x~{{oo- 76 | $#1???!367?G]^ro!44?AB@???BB???BB@??BB__ow[EABB 77 | $#3???!371?NN!43~|{}~~~{{~~~{{}~~{[^NNBB@@- 78 | $#1???!370?@Bb}}uM{GwGwGwGwWoOoOoOoo!8OWW!10GwwwK{C{C{C{C{C{E}BvN\wo___ 79 | $#2???!376?o?o?o?o?o?_?_?_?_!25?O?o?w?w?w?w?w?{?o 80 | $#3???!373?!4@BB!8F!8N!8nff!10vFF!12B@@- 81 | $#1???!364?_{{}E}B~@~?~?~?~?~?~?~?~?~?No~^!21?f~{~?~?~?~?~?~?~?~?~?~?~@~B}Mww_ 82 | $#2???!368?w?{?{?}?~?~?~?~?~?~?~?~?N!27?~?~?~?~?~?~?~?~?~?~?}?{?o 83 | $#3???!395?!20~WW- 84 | $#1???!362?o}^~?~?~?~?~?~?~?~?~?~?~?~?^_{~F!7?CCM^^MCC!7?F~{~?~?~?~?~?~?~?~?~?~ 85 | ?~?~?~?{BF}{ 86 | $#2???!366?{?~?~?~?~?~?~?~?~?~?~?~?^!29?~?~?~?~?~?~?~?~?~?~?~?~?~?{ 87 | $#3???!393?ww!6~zz!4_zz!6~ww- 88 | $#1???!360?o}^~?~?~?~?~?~?~?~?~?~?~?~?~??~~!24?_~~~?~?~?~?~?~?~?~?~?~?~?~?~?~?w 89 | F~{ 90 | $#2???!364?{?~?~?~?~?~?~?~?~?~?~?~?~!31?~?~?~?~?~?~?~?~?~?~?~?~?~?~?w 91 | $#3???!393?!22~^^- 92 | $#1???!360?~~?~?~?~?~?~?~?~?~?~?~?~?~?~??~~!9?__owwo__!7?~~?~?~?~?~?~?~?~?~?~?~ 93 | ?~?~?~?~?~?`^~o 94 | $#2???!364?~?~?~?~?~?~?~?~?~?~?~?~?~!31?~?~?~?~?~?~?~?~?~?~?~?~?~?~?~?_ 95 | $#3???!393?!8~^^!4F^^!6~- 96 | $#1???!360?b~}~?~?~?~?~?~?~?~?~?~?~?~?~?}@~~!10?@BBB!8?{~~?~?~?~?~?~?~?~?~?~?~? 97 | ~?~?~?~?~?~?N~w 98 | $#2???!364?~?~?~?~?~?~?~?~?~?~?~?~?~?}!27?w?~?~?~?~?~?~?~?~?~?~?~?~?~?~?~?~ 99 | $#3???!393?!10~!4{!8~- 100 | $#1???!360?~~?~?~?~?~?~?~?~?~?~?~?~?~?~?~?~~!8?__owwo__!6?~~~?~?~?~?~?~?~?~?~?~ 101 | ?~?~?~?~?~?~?~?{B~} 102 | $#2???!364?~?~?~?~?~?~?~?~?~?~?~?~?~?~!27?~?~?~?~?~?~?~?~?~?~?~?~?~?~?~?~?~?{ 103 | $#3???!393?!8~^^!4F^^!6~- 104 | $#1???!359?{~~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~~!10_bbb`!8_~~~_~_~_~_~_~_~_~_~_~_~ 105 | _~_~_~_~_~_~_~_`~~ 106 | $#2???!362?W?^?^?^?^?^?^?^?^?^?^?^?^?^?^!27?^?^?^?^?^?^?^?^?^?^?^?^?^?^?^?^?^?^ 107 | $#3???!393?!10^!4[!8^-- 108 | $#3???!597?_OKCCc[??o_?OOo!4?!4Oo??_O!4?_O?_o?OOOo!11?_oGSOOo??_O???_O!21?_okcc 109 | CSK?o_?OOo!5?oO?C!4?!4Oo??_o?OOOo!11?_O?G?CCCK_O???_ 110 | O!5?oO?C??_o?OOOo??_o?OOOo- 111 | $#3???!593?CCEDC?A?@?CA@!6?EC!4DB@?E@A?F?A@CA@???CA@!9?CAFCCC?AXOORAICB@!19?CCE 112 | DCC?A@?CA@!6?CCCED!4?EC!4DB@CA@???CA@!11?FCCC@B@?EDC 113 | C?E@??CCCED???CA@???CA@CA@???CA@??CEA-\ 114 | -------------------------------------------------------------------------------- /testfiles/cat2_clean.six: -------------------------------------------------------------------------------- 1 | #0;1;280;35;60 2 | #1;1;0;0;0 3 | #2;1;120;50;100 4 | #3;1;0;99;0 5 | - 6 | $#1???!424?!8_- 7 | $#1???!373?owwK{C{C}E}A}AyE}C{C{C{KwGwGwGwK{C{E}A}A}A}A}B~@~@~@~?~?}@~@~@~@~B}A 8 | }EsK{GgWwOoOoOOoo!7_ 9 | $#2???!376?_?o?w?w?{?{?w?w?w?w?o?o?o?o?o?o?w?w?{?{?{?{?{?}?}?}?~?}?}?}?}?{?{?w? 10 | o?o?_?_?_?_- 11 | $#1???!368?_owWKNNFCFEBAABB@@BBABABABABEFKNW^O~o~_~?~?~?~?~?~?~?~?~?~?~?~?~?~?~ 12 | ?~?~?~?~?~?~?~?~?~?~?~?~?~?~@~@~@|B~A}A}A}E{C{CsK{Gw 13 | GwGwWoOOoo___ 14 | $#2???!376?B?@?@!9?@?@?@?@?@?B?F?N?^?^?~?~?~?~?~?~?~?~?~?~?~?~?~?~?~?~?~?~?~?~? 15 | ~?~?~?~?~?~?~?}?}?}?{?{?{?{?w?w?w?o?o?o?o?_?_ 16 | $#3???!371?__oo!4w!16{wwoo!4_- 17 | $#1???!368?FNkww!14?!17_??!6@BBABEFCNKNW^W~w~o~o~o~_~_~_~?~?~?~?~_~_~_~_~_~_~_~ 18 | _no~O^O^O^O^O^O^O^WNGNGNGNGNGNGNW^O^O^P\R^Q]QYU]SS{{ 19 | MFB@BB@@BBAA]}o 20 | $#2???!416?@?@?B?F?F?F?F?N?N?^?^?^?^?~?~?~?^?^?^?^?^?^?^?^?N?N?N?N?N?N?N?N?F?F? 21 | F?F?F?F?F?F?F?N?M?K?K?K?G?G 22 | $#3???!371?FF!14~!18^!6}!4{!4woo!4_!90?ww!8{__- 23 | $#1???!367?o}^B@?_oOWKKKCEFbppXXH!4G!8?_``ppPPZZIMMEEEM!4EM!5K\^vvbBBAEEC!4t||! 24 | 4xh!5gww!10OoowG!42?BBAEECKKGGKKCEEBB 25 | $#2???!376?_?o?o?w?K?e?u?v?v?~?~?~?~?]?M?M?C?C!19?G?[?{?w?G?G!7?O?O?O!5?_?_?_?_ 26 | $#3???!369?__}}NNFF!4B!25?!4_oppxhxp!4xppRrrr__!86?@@!8B@@- 27 | $#1???!346?__ckwoOOO[[OOPZ^NMKK[\NNEFDDKKGG?!7_!4?_ooW[KEEBB@@@!27?@BFE{{_?F^^N 28 | !5FfbbrrRRZZLMEAABB!5@ 29 | $#2???!354?_?_?_?_?_?o?_?_?o?w?q?v?~?^?^?^?^?~?^?N?B?@!37?@?B?w?_?w?w?w?[?K?K?C 30 | ???@ 31 | $#3???!393?__oWw{{}}}!22~}!4~}swG- 32 | $#1???!342?CEFDDDCCCS\LNJz~vfFN^^!4?o{{cEABA?@@@!5?@BBEM^zo!21?K]]K!17?N~w__oo[ 33 | [FFPp`?wXHHLLFF!4EAA 34 | $#2???!348?B?B?A!7?W?_?~?f?B?B?@???@!61?F?N?B?_?g?]?F?E?A 35 | $#3???!372?w{{{!4}!5~}{{w__CN~|!19~r``r!15~{~o- 36 | $#1???!354?AEK]zpb}{{___fNLWoo___??ENNE!4?___oWNN?EEKKGWWoo!6_o!7Oo!4_!9?_ow[NF 37 | @?@BECE]]}{LFBA 38 | $#2???!360?K?@?N?@!24?o?w?o?o?_!36?o?}?{?z?`?@?A 39 | $#3???!371?NN^^^~~xoox!4~^^^Nf?o?x@r@vBfNN!6^NnNnNnNnN!4^!9~^NFB- 40 | $#1???!355?_o[{~vFNZp_!9@BBBEAAEAA@@@?@BBEEkk{{[MMBB?@@B!7?C!6?@@@B!4@AFBLLww_? 41 | ?CK]ua!4B@BA 42 | $#2???!391?@?B?B?@!34?A?F?^?z?`?@ 43 | $#3???!362?o?K?}?}?}?}?}?{?x@|@|@}?}?}?{?x?R?B?`?o?{?}?~?~?~?~?~?~?~?}?}?}?}?|? 44 | {?_- 45 | $#1???!354?@@?N^Zro!4?__!24?Kr@!28?oue!5Ccc~~JJJIIIZPRU[W!6Oo!8_ 46 | $#2???!440?@ 47 | $#3???!360?C?N?~?^?~?~?~?~?~?~?~?~?~?~?~?~?r?K?~?~?~?~?~?~?~?~?~?~?~?~?~?~?H?z? 48 | z?z?Z- 49 | $#1???!360?br]MMNXWoo{{__?G???G!6?___oo{{v{{ww!4o!6_!4?!5_oo!4OwxNMK[vf@@ 50 | $#3???!364?@?A?F?B?^?^?~?~?~?~?~?^?^?N?B???B?F?N?N?^?^?^?~?^?^?^?N?N?N?E?@?B- 51 | $#1???!352?_oOWKEAB@!8?{~B!5@`brZNnxx@@!4?x!18~|xxp??KK^~|[scD@!4?@BEKWo_ 52 | $#2???!384?E?}?~?~!21?E?M?r?_- 53 | $#1???!343?OWGKCEAB@@!15?MN@!6?AEKWr||~~!5?^!19~^N~{!4?F~{_@BEKG!8?@@ 54 | $#2???!388?~?~!20?_!4?~?~ 55 | $#3???!412?_- 56 | $#1???!378?GKEB`o~^!6?!18~{__`BBAAEECDD!11CKK!17GWWOOoo!4_ 57 | $#2???!386?_?~?~!20?^?[?{?w@w@w?w?w?w?w?w?o?o?o?o?o?o?o?o?o?o?_?_ 58 | $#3???!412?^?[?{?w?w?w?w?w?w?w?w?o?o?o?o?o?o?o?o?o?o?_?_- 59 | $#1???!379?cuzx~NB@!5?B!15~rp__!6@BB!5A!36?@@@FFK{w 60 | $#2???!386?}?~?~?{!16?M?]?}?}?{?{?|?|?~?~?~?~?~?~?~?~?~?~?~?~?~?~?~?~?~?~?}?w?o 61 | $#3???!410?M?]?}?}?{?{?|?|?~?~?~?~?~?~?~?~?~?~?~?~?~?~?~?~?~?~?}?w?o- 62 | $#1???!376?CEb`}~RWKC!7?@BFnn!4N^^~!6^NNNffvp^^BBB}}YqaA!6CG!8O!5_!6?!4_owWGGKK 63 | EFB 64 | $#2???!384?r?~?~?~?~?{?O?o?o?_???_?_?_?o?O?G?_?{?{@?@?@?B?B?B?F?N?N?N?N?^?^?~?~ 65 | ?~?^?^?F?F?B?@ 66 | $#3???!424?@?@?@?B?B?B?F?N?N?N?N?^?^?~?~?~?^?^?F?F?B?@- 67 | $#1???!375?CEz|NF!5?!6_!4?@@@BBAA!5IJB!6@!6?C^~qAEECCKWo_!14?!8@ 68 | $#2???!380?o?~?~?^?^?^?^?~?}?}?{?|?t?t?s?}?}?}?~?~?~?z 69 | $#3???!425?KK!4w__- 70 | $#1???!369?wwKKCEEABB!9@??!9@BB!4AEECCCEEAAAEECCEEABB@@!8?@BE}{_ 71 | $#2???!402?@?@?@?B?@?@?@?@?B?@ 72 | $#3???!371?oo!4w{{!20}!6{!8w{{!6w!4{}}!6~}}ww- 73 | $#1???!369?bfess[[GGWWOOOoo!5_!32?___???wwG?_{}E??BBM}w 74 | $#3???!371?@@BBbb!4fnnNN!6^!31~^^^~~~FFv~^B@x~{{oo- 75 | $#1???!367?G]^ro!44?AB@???BB???BB@??BB__ow[EABB 76 | $#3???!371?NN!43~|{}~~~{{~~~{{}~~{[^NNBB@@- 77 | $#1???!370?@Bb}}uM{GwGwGwGwWoOoOoOoo!8OWW!10GwwwK{C{C{C{C{C{E}BvN\wo___ 78 | $#2???!376?o?o?o?o?o?_?_?_?_!25?O?o?w?w?w?w?w?{?o 79 | $#3???!373?!4@BB!8F!8N!8nff!10vFF!12B@@- 80 | $#1???!364?_{{}E}B~@~?~?~?~?~?~?~?~?~?No~^!21?f~{~?~?~?~?~?~?~?~?~?~?~@~B}Mww_ 81 | $#2???!368?w?{?{?}?~?~?~?~?~?~?~?~?N!27?~?~?~?~?~?~?~?~?~?~?}?{?o 82 | $#3???!395?!20~WW- 83 | $#1???!362?o}^~?~?~?~?~?~?~?~?~?~?~?~?^_{~F!7?CCM^^MCC!7?F~{~?~?~?~?~?~?~?~?~?~ 84 | ?~?~?~?{BF}{ 85 | $#2???!366?{?~?~?~?~?~?~?~?~?~?~?~?^!29?~?~?~?~?~?~?~?~?~?~?~?~?~?{ 86 | $#3???!393?ww!6~zz!4_zz!6~ww- 87 | $#1???!360?o}^~?~?~?~?~?~?~?~?~?~?~?~?~??~~!24?_~~~?~?~?~?~?~?~?~?~?~?~?~?~?~?w 88 | F~{ 89 | $#2???!364?{?~?~?~?~?~?~?~?~?~?~?~?~!31?~?~?~?~?~?~?~?~?~?~?~?~?~?~?w 90 | $#3???!393?!22~^^- 91 | $#1???!360?~~?~?~?~?~?~?~?~?~?~?~?~?~?~??~~!9?__owwo__!7?~~?~?~?~?~?~?~?~?~?~?~ 92 | ?~?~?~?~?~?`^~o 93 | $#2???!364?~?~?~?~?~?~?~?~?~?~?~?~?~!31?~?~?~?~?~?~?~?~?~?~?~?~?~?~?~?_ 94 | $#3???!393?!8~^^!4F^^!6~- 95 | $#1???!360?b~}~?~?~?~?~?~?~?~?~?~?~?~?~?}@~~!10?@BBB!8?{~~?~?~?~?~?~?~?~?~?~?~? 96 | ~?~?~?~?~?~?N~w 97 | $#2???!364?~?~?~?~?~?~?~?~?~?~?~?~?~?}!27?w?~?~?~?~?~?~?~?~?~?~?~?~?~?~?~?~ 98 | $#3???!393?!10~!4{!8~- 99 | $#1???!360?~~?~?~?~?~?~?~?~?~?~?~?~?~?~?~?~~!8?__owwo__!6?~~~?~?~?~?~?~?~?~?~?~ 100 | ?~?~?~?~?~?~?~?{B~} 101 | $#2???!364?~?~?~?~?~?~?~?~?~?~?~?~?~?~!27?~?~?~?~?~?~?~?~?~?~?~?~?~?~?~?~?~?{ 102 | $#3???!393?!8~^^!4F^^!6~- 103 | $#1???!359?{~~_~_~_~_~_~_~_~_~_~_~_~_~_~_~_~~!10_bbb`!8_~~~_~_~_~_~_~_~_~_~_~_~ 104 | _~_~_~_~_~_~_~_`~~ 105 | $#2???!362?W?^?^?^?^?^?^?^?^?^?^?^?^?^?^!27?^?^?^?^?^?^?^?^?^?^?^?^?^?^?^?^?^?^ 106 | $#3???!393?!10^!4[!8^-- 107 | $#3???!597?_OKCCc[??o_?OOo!4?!4Oo??_O!4?_O?_o?OOOo!11?_oGSOOo??_O???_O!21?_okcc 108 | CSK?o_?OOo!5?oO?C!4?!4Oo??_o?OOOo!11?_O?G?CCCK_O???_ 109 | O!5?oO?C??_o?OOOo??_o?OOOo- 110 | $#3???!593?CCEDC?A?@?CA@!6?EC!4DB@?E@A?F?A@CA@???CA@!9?CAFCCC?AXOORAICB@!19?CCE 111 | DCC?A@?CA@!6?CCCED!4?EC!4DB@CA@???CA@!11?FCCC@B@?EDC 112 | C?E@??CCCED???CA@???CA@CA@???CA@??CEA- -------------------------------------------------------------------------------- /testfiles/gnuplot.six: -------------------------------------------------------------------------------- 1 | Pq#0;2;100;100;100 2 | #0~!319@T!7@?!310@~$ 3 | #1!15?_!4O???Os!4?O_?_O??_OOO_???C{!18?OO{OO??_OOO_??o_OO_??oO_O_???Os!4?o_OO_???OOO_???C{!11?OO{OO??_OOO_??_!4O??OO{OO!487?$ 4 | #5!328?N!311?$ 5 | - 6 | #0~!319?T!260?!5O???C}!10?!22O!16?~$ 7 | #1!15?CDDDA???CFC???CA@AC??BDDD@???CFC!19?BCA??BDDD@??F!6?F?F?F???CFC???F???F??ADDDF???CFC!12?BCA??BDDD@??CDDDA!4?BCA!487?$ 8 | #5!224?!4_??w?__!4?___???_???_!9?__w__???_g!5?___!4?!4_???___!4?___!4?Gw!5?___!9?!5A!315?$ 9 | - 10 | #0~!319?T!268?ABA!47?~$ 11 | #5!223?HIIIC??N@??N??FGGGF??FGEGF!11?FGC???GNG???FGGGD??HIIIC??FGGGD??CIIIN???GNG???FIIIB!328?$ 12 | - 13 | #0~!319?T!267?}PHD}!8?G?G?G?G?G?G?G?G?G?G?G!17?~$ 14 | #1!631?G!8?$ 15 | - 16 | #0~!319?T!268?@@@!47?~$ 17 | #1!590?_!49?$ 18 | - 19 | #0~!319?T!318?~$ 20 | #1!589?`~_!9?!22C!6?CC^CC!6?$ 21 | - 22 | #0~!319?T!187?_!5?_!5?__!5?__!15?_!24?__!68?~$ 23 | #1!629?_???_!6?$ 24 | #2!588?_OOO_!47?$ 25 | - 26 | #0~!319?T!185?C~C@???a}_!4?_~_!4?_~_???[iiiK??[aaS~!9?}Saa[??[aaa[???_~_???]___}??[aaS}??[aaa[??}CAA{??ciiiQ???ZZ!26?~$ 27 | #1!629?GDADG!6?$ 28 | #2!588?[AOOP!8?AAA!4?AAA??!10A!17?$ 29 | - 30 | #0~!319?T!233?B!21?AAA@??@AAA@!52?~$ 31 | #1!629?O_o_O!6?$ 32 | #3!588?OGGGo!47?$ 33 | - 34 | #0~!319?T!318?~$ 35 | #1!629?DBFBD!6?$ 36 | #3!429?_w}!33~}w_!120?CGHHE!8?!22@!17?$ 37 | - 38 | #0~!319?T!318?~$ 39 | #1!629?wGgGw!6?$ 40 | #3!425?_o{!41~{o_!168?$ 41 | #4!588?_OG{!9?!22_!17?$ 42 | - 43 | #0~!319?T!318?~$ 44 | #1!629?BAAAB!6?$ 45 | #2!463?_!33o_!142?$ 46 | #3!422?_w}!38~^!8NMG!166?$ 47 | #4!588?@@@F@!47?$ 48 | - 49 | #0~!319?T!318?~$ 50 | #1!629?OgSgO!6?$ 51 | #2!460?ow}!35~}wo!139?$ 52 | #3!418?_o{!39~NF@!177?$ 53 | #5!588?MIIIq!8?!22O!17?$ 54 | - 55 | #0~!319?T!318?~$ 56 | #1!631?@!8?$ 57 | #2!456?_w{!43~{w_!135?$ 58 | #3!416?G}!38~^FB!181?$ 59 | #5!588?@AAA@!47?$ 60 | - 61 | #0~!319?T!318?~$ 62 | #1!629?O[Y[O!6?$ 63 | #2!453?ow}!49~}wo!132?$ 64 | #3!418?BN^!32~NF@!184?$ 65 | #6!588?{IHHo!8?!22G!17?$ 66 | - 67 | #0~!319?T!318?~$ 68 | #2!449?_w{!57~{w_!128?$ 69 | #3!421?@F^!25~^FB!188?$ 70 | #6!589?@@@!48?$ 71 | #7!588?!5_!47?$ 72 | - 73 | #0~!319?T!318?~$ 74 | #1!629?CC^CC!6?$ 75 | #2!448?AN!61~NA!127?$ 76 | #3!425?BN!21~|o!190?$ 77 | #7!588?oGCA@!8?!22C!17?$ 78 | - 79 | #0~!319?T!318?~$ 80 | #1!629?_???_!6?$ 81 | #2!451?BF^!53~^FB!130?$ 82 | #3!428?@F^!20~{w_!186?$ 83 | #8!588?_OOO_!47?$ 84 | - 85 | #0~!319?T!318?~$ 86 | #1!629?GDADG!6?$ 87 | #2!21?LRPRLPM!4?LRPRLPM!415?@FN!47~NF@!133?$ 88 | #3!431?!23@!186?$ 89 | #8!588?LQQQL!8?!22A!17?$ 90 | - 91 | #0~!319?T!318?~$ 92 | #1!629?O_o_O!6?$ 93 | #2!21?[a}_[!6?[a}_[!421?BN^!39~^NB!137?$ 94 | #9!588?oGGGo!47?$ 95 | - 96 | #0~!319?T!318?~$ 97 | #1!629?DBFBD!6?$ 98 | #2!19?CCsKCKs!4?CCsKCKs!424?@F!35^F@!140?$ 99 | #9!589?HHDB!8?!22@!17?$ 100 | - 101 | #0~!319?T!318?~$ 102 | #1!629?wGgGw!6?$ 103 | #2!22?@@@!8?@@@!604?$ 104 | #10!582?G{!4?wCcSw!8?!22_!17?$ 105 | - 106 | #0~!319?T!318?~$ 107 | #1!629?BAAAB!6?$ 108 | #2!19?o?_OOO_!4?o?_OOO_!603?$ 109 | #10!582?CFC???BDCCB!47?$ 110 | - 111 | #0~!319?T!318?~$ 112 | #1!629?OgSgO!6?$ 113 | #2!19?FCF?_CB!4?FCF?_CB!603?$ 114 | #11!582?C}!5?C}!10?!22O!17?$ 115 | - 116 | #0~!319?T!318?~$ 117 | #1!631?@!8?$ 118 | #2!19?@BDHN@@!4?@BDHN@@!603?$ 119 | #11!582?ABA!4?ABA!48?$ 120 | - 121 | #0~!319?T!318?~$ 122 | #1!629?O[Y[O!6?$ 123 | #2!22?^!8?CC^CC!604?$ 124 | #12!582?A~!4?qHHHE!8?!22G!17?$ 125 | - 126 | #0~!319?T!318?~$ 127 | #12!582?@@@???!5@!47?$ 128 | #13!583?_!5?___!48?$ 129 | - 130 | #0~!319?T!318?~$ 131 | #1!629?CC^CC!6?$ 132 | #2!10?_??_!7?!4C{Cw!4?!4C{Cw!601?$ 133 | #13!582?`~_???P_ccZ!8?!22C!17?$ 134 | - 135 | #0~!319?T?C{!4?_OOO_???_wcG??OO{OO!11?Os???o???o??_!4O??OO{OO???Os!5?_wcG???Os!4?_OOO_??_OO_{!222?~$ 136 | #1!629?_???_!6?$ 137 | #2!8?AANAAA@!6?pHHHo!6?pHHHo!603?$ 138 | #14!582?_o!6?_o!48?$ 139 | - 140 | #0~!319?T?CFC???BDDD@!4?F!6?BCA!9?GOON???BCCAF??CDDDA!4?BCA???CFC!5?F!5?CFC???BDDD@??BCCAF!222?~$ 141 | #1!629?GDADG!6?$ 142 | #2!10?PICIP!4?AAABABA!4?AAABABA!603?$ 143 | #14!582?O^O???EDC^C!8?!22A!17?$ 144 | - 145 | #0~!275?oGGGO??ogggo??wOGGo??GG}GG??wOGGO??ogggo??OO|OO??oGGO}!9?GG}GG??ogggo??GO_OG??GG}GG!274?~$ 146 | #1!629?O_o_O!6?$ 147 | #2!10?[a}_[!4?!7_!4?!7_!603?$ 148 | #15!582?Ow!4?wgggG!47?- 149 | #0~!275?@AAA@??@AAA???B???B!4?@A@??B!6?@AAA!5?T!4?@AA@B!11?@A@??@AAA???A@?@A!4?@A@!274?~$ 150 | #1!629?DBFBD!6?$ 151 | #2!8?OO{OOSG!6?EHGHE!6?EHGHE!603?$ 152 | #15!582?GNG???CGGGF!8?!22@!17?- 153 | #0~!215?{GCCG???C|!4?wCCg{??~GCCw??CC~Cc!11?C|???{??_{??GSSSc??CC~Cc???C|!5?G}HA???C|!4?wSSSW??wCCg~?T!318?~$ 154 | #1!582?G{!4?ogcc!9?!22_!6?wGgGw!6?$ 155 | #2!10?@!10?MP^OM!6?MP^OM!603?$ 156 | - 157 | #0~!215?@!7?@@@???ADDCB??@???@!5?@!10?ACCB!4?@@?@??!4@!6?@!4?@@@!5?@!5?@@@!4?@@@!4?@@?@?T!318?~$ 158 | #1!582?CFC???BCCCB!36?BAAAB!6?$ 159 | #2!8?OOOoOoO!4?GG}GGIC!4?GG}GGIC!603?$ 160 | - 161 | #0~!319?T!318?~$ 162 | #1!629?OgSgO!6?$ 163 | #2!10?BccCB!6?wC{C{!6?wC{C{!545?C}!4?AaQIE!8?!22O!17?$ 164 | - 165 | #0~!319?T!318?~$ 166 | #1!631?@!8?$ 167 | #2!10?FGNGF!4?__w__hO!4?__w__hO!545?ABA???B!51?$ 168 | - 169 | #0~!319?T!318?~$ 170 | #1!629?O[Y[O!6?$ 171 | #2!10?UXOOO!6?bOOO_!6?bOOO_!603?$ 172 | #3!582?A~!4?uHHHu!8?!22G!17?$ 173 | #4!253?_!20?_!21?_!17?_!36?_!34?_!8?_!4?_!4?_!234?$ 174 | - 175 | #0~!319?T!318?~$ 176 | #2!8?GG}GGIC!6?BcCCB!6?BcCCB!603?$ 177 | #3!582?@@@!4?@@@!48?$ 178 | #4!251?AA^aQ??[iiiK??ciiiQ??AA^aQ!9?[aaa[???C~C@!9?[aaaS??~CAA{??Oiii{??}CAAC??Oiii{??[aaaS??AA^aQ??[iiiK??}CAAC!9?]_W_]???a}_???[aaS~??AA^aQ??~CAA{???ZZ!168?_!5?___!48?$ 179 | - 180 | #0~!319?T!318?~$ 181 | #1!629?CC^CC!6?$ 182 | #2!10?WcCCC!6?JKGGG!6?JKGGG!603?$ 183 | #4!582?`~_???BccSN!8?!22C!17?$ 184 | - 185 | #0~_?_?_?_!7?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_???_?_!5?_?_???_?_?_???_?_?_???_!5?_?_???_?_!5?_?_!5?_?_?_?_S_???_?_!5?_?_???_?_?_???_?_?_???_!5?_?_???_?_!5?_?_!5?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_???_???_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_???_???_?_?~$ 186 | #1!629?_???_!6?$ 187 | #2!9?_`x``@!625?$ 188 | #4!250?~@H|!4@HdddX@@HDddX@@`PH|@@@\TTTd@@phdd@@@DDdTL@@XdddX@@Xdddx@@xDdTx@@@H|!4@HdddX@@HDddX@@`PH|@@@\TTTd@@phdd@@@DDdTL@@XdddX@@Xdddx@@xDdTx@~!249?$ 189 | #5!581?_OOO_??_OOO_!47?$ 190 | - 191 | #0~!319?D!318?~$ 192 | #1!629?GDADG!6?$ 193 | #2!10?_Ro?_!625?$ 194 | #4!250?^OSVSOOOV!4SOOQSSSROOPPPVPOOQSSSROORSSSROOUP!5ORSSSROOOSSQPOORTSSROOOSVSOOOV!4SOOQSSSROOPPPVPOOQSSSROORSSSROOUP!5ORSSSROOOSSQPOORTSSRO^!249?$ 195 | #5!581?[QQQP??NSQPN!8?!22A!17?$ 196 | - 197 | #0~!319?T!318?~$ 198 | #1!629?O_o_O!6?$ 199 | #2!10?BcFcB!625?$ 200 | #6!581?OGGGo???Ow!49?$ 201 | - 202 | #0~!319?T!318?~$ 203 | #1!629?DBFBD!6?$ 204 | #2!10?FGGGF!625?$ 205 | #6!581?MHHHG???GNG!9?!22@!17?$ 206 | - 207 | #0~!319?T!318?~$ 208 | #1!629?wGgGw!6?$ 209 | #7!581?GcccW??GcccW!8?!22_!17?$ 210 | - 211 | #0~!319?T!318?~$ 212 | #1!629?BAAAB!6?$ 213 | #2!8?AAYeaeY!625?$ 214 | #7!581?F!4C??F!4C!47?$ 215 | - 216 | #0~!319?T!318?~$ 217 | #1!629?OgSgO!6?$ 218 | #2!10?wC{?w!625?$ 219 | #8!581?cQQQK??CAQQk!8?!22O!17?$ 220 | - 221 | #0~!319?T!318?~$ 222 | #1!631?@!8?$ 223 | #2!8?__w``hO!625?$ 224 | #8!581?B!4A??@AAA@!47?$ 225 | - 226 | #0~!319?T!318?~$ 227 | #1!629?O[Y[O!6?$ 228 | #2!10?bOoOo!625?$ 229 | #9!581?qHHHE??WSQ~O!8?!22G!17?$ 230 | - 231 | #0~!319?T!318?~$ 232 | #2!10?b?BcB!625?$ 233 | #9!581?!5@!5?@!48?$ 234 | #10!582?___???!5_!47?$ 235 | - 236 | #0~!319?T!318?~$ 237 | #1!629?CC^CC!6?$ 238 | #2!8?AANAAA@!625?$ 239 | #10!581?xcccb??Raaa[!8?!22C!17?$ 240 | - 241 | #0~!319?T!318?~$ 242 | #1!629?_???_!6?$ 243 | #2!10?MPPPM!625?$ 244 | #11!581?_OOO_???_OO!48?$ 245 | - 246 | #0~!319?T!318?~$ 247 | #1!629?GDADG!6?$ 248 | #2!10?kq___!625?$ 249 | #11!581?[QQQP??NQQQK!8?!22A!17?$ 250 | - 251 | #0~!319?T!318?~$ 252 | #1!629?O_o_O!6?$ 253 | #12!581?OGGGo??GGGgW!47?$ 254 | - 255 | #0~!319?T!318?~$ 256 | #1!629?DBFBD!6?$ 257 | #12!581?MHHHG??KA@!10?!22@!17?$ 258 | - 259 | #0~!319?T!318?~$ 260 | #1!629?wGgGw!6?$ 261 | #13!581?GcccW??WcccW!8?!22_!17?$ 262 | - 263 | #0~!319?T!318?~$ 264 | #1!239?w}w!387?BAAAB!6?$ 265 | #13!581?F!4C??BCCCB!47?$ 266 | - 267 | #0~!319?T!318?~$ 268 | #1!220?GO_!15?EF~FE!14?_OoG!368?OgSgO!6?$ 269 | #14!581?cQQQK??KQQQ{!8?!22O!17?$ 270 | - 271 | #0~!49?A}!5?Gy!4?wOGGo??ogggo??w?_?w???Gy!4?oGGO}??GG}GG??}OGGo!210?T!318?~$ 272 | #1!223?@ACGO_!11?~!11?_OIDHE@!372?@!8?$ 273 | #14!581?B!4A???AA@!48?$ 274 | - 275 | #0~!49?ABA!4?ABA???B???B??@AAA???@A@A@???ABA???@AA@B!4?@A@??B???B!210?T!318?~$ 276 | #1!229?@ACGO_!5?~!5?_OGCA@!377?O[Y[O!6?$ 277 | #15!581?a@HHu??}PHD}!8?!22G!17?- 278 | #0~!319?T!318?~$ 279 | #1!212?__!4OGw!15_`acgo~ogca`!15_gg!4o__!313?___!5?_!49?$ 280 | #15!582?@@@!4?@@@!48?- 281 | #0~!45?!70{!12?C{!4?o???o!9?ogcc!168?T!318?~$ 282 | #1!214?!4@AB!14?_OGCA@~BDYcSg!14?AA!4@!314?P_ccZ???`~_!9?!22C!6?CC^CC!6?$ 283 | - 284 | #0~!45?!70B!12?CFC???BCBCB!9?BCCCB!167?T!318?~$ 285 | #1!228?_OGCA@!6?~!6?@ACGO_!376?_???_!6?$ 286 | #2!581?_OOO_??_OOO_!47?$ 287 | - 288 | #0~!319?T!318?~$ 289 | #1!221?_OkQSI@!12?~!12?@ACGO_!370?GDADG!6?$ 290 | #2!581?GOQQL??[QQQP!8?!22A!17?$ 291 | - 292 | #0~!46?!69w!12?Gw!4?_???_!9?wgggG!167?T!318?~$ 293 | #1!220?A@@!15?KsFsK!16?@A!368?O_o_O!6?$ 294 | #3!581?OGGGo??OGGGo!47?$ 295 | - 296 | #0~!46?!69B!12?GNG???FGEGF!9?CGGGF!167?T!318?~$ 297 | #1!239?BKB!387?DBFBD!6?$ 298 | #3!581?CGHHE??CGHHE!8?!22@!17?$ 299 | - 300 | #0~!319?T!318?~$ 301 | #1!629?wGgGw!6?$ 302 | #4!581?GCccW??_OG{!9?!22_!17?$ 303 | - 304 | #0~!46?!68_!13?Oo!20?_o!168?T!128?o!6?o!34?_O_!4?O!5?Oo!5?Oo!126?~$ 305 | #1!629?BAAAB!6?$ 306 | #4!581?ACCCB??@@@F@!47?$ 307 | - 308 | #0~!46?!68F!13?O^O???NOKON!9?EDC^C!167?T!112?~IPPM??GTTT]??@@NPH??@@NPH??MTTTE??^A@@A??^A@@]!10?A^A!4?P^O!4?O^O!4?O^O!125?~$ 309 | #1!629?OgSgO!6?$ 310 | #5!581?CAQQk??MIIIq!8?!22O!17?$ 311 | - 312 | #0~!319?T!15?oGGgo!20?Ow!21?OGGGo!19?OGGGo!20?`Ow!20?wgggG!19?_OGG!20?GGGgW!19?oGGGo!19?oGGGo!82?~$ 313 | #1!631?@!8?$ 314 | #5!581?@AAA@??@AAA@!47?$ 315 | - 316 | #0~!127?__!19?___!168?t!15_fIHGF???!17_GNG!4?!16_mHHHG???!16_cGHHE???!16_bAANA???!16_cGGGF???!16_fHHHE???!16_kA@!5?!16_eHHHE???!17_HHDB!82?~$ 317 | #1!629?O[Y[O!6?$ 318 | #6!581?a@HHu??{IHHo!8?!22G!17?$ 319 | - 320 | #0~!47?!67F!13?_~_???]_W_]!9?P_ccZ!167?~!15?~!7?~CA@ACgOgCA@ACgO~!7?~TaTGTaTGTaTGTaT~!7?!17~!7?~_??@ACGO_??@ACG~!7?~CA@??_OGCA@??_O~!7?~?BKo?BKo?BKo?BK~!7?~B?oKB?oKB?oKB?o~!7?~!15?~!7?~CA@ACgOgCA@ACgO~!86?~$ 321 | #6!582?@@@!4?@@@!48?$ 322 | #7!582?___???!5_!47?$ 323 | - 324 | #0~!319?~!15?~!7?~PICIP_?_PICIP_?~!7?~TGTaTGTaTGTaTGT~!7?!17~!7?~?@ACGO_??@ACGO_~!7?~OGCA@??_OGCA@??~!7?~BKo?BKo?BKo?BKo~!7?~KB?oKB?oKB?oKB?~!7?~!15?~!7?~PICIP_?_PICIP_?~!86?~$ 325 | #1!629?CC^CC!6?$ 326 | #7!581?P_ccZ??oGCA@!8?!22C!17?$ 327 | - 328 | #0~!319?~!15?~!7?~CgOgCA@ACgOgCA@~!7?~TaTGTaTGTaTGTaT~!7?!17~!7?~ACGO_??@ACGO_??~!7?~?_OGCA@??_OGCA@~!7?~Ko?BKo?BKo?BKo?~!7?~oKB?oKB?oKB?oKB~!7?~!15?~!7?~CgOgCA@ACgOgCA@~!86?~$ 329 | #1!629?_???_!6?$ 330 | #8!581?_OOO_??_OOO_!47?$ 331 | - 332 | #0~!47?!66K!14?@~!4?{?o?{!9?qHHHE!167?~!15?~!7?~P_?_PICIP_?_PIC~!7?~TGTaTGTaTGTaTGT~!7?!17~!7?~GO_??@ACGO_??@A~!7?~@??_OGCA@??_OGC~!7?~o?BKo?BKo?BKo?B~!7?~?oKB?oKB?oKB?oK~!7?~!15?~!7?~P_?_PICIP_?_PIC~!86?~$ 333 | #1!629?GDADG!6?$ 334 | #8!581?GOQQL??LQQQL!8?!22A!17?$ 335 | - 336 | #0~!127?@@@!4?@?@!10?!5@!167?~!15?~!7?~CA@ACgOgCA@ACgO~!7?~TaTGTaTGTaTGTaT~!7?!17~!7?~_??@ACGO_??@ACG~!7?~CA@??_OGCA@??_O~!7?~?BKo?BKo?BKo?BK~!7?~B?oKB?oKB?oKB?o~!7?~!15?~!7?~CA@ACgOgCA@ACgO~!86?~$ 337 | #1!629?O_o_O!6?$ 338 | #9!581?OGGGo??oGGGo!47?$ 339 | - 340 | #0~!319?~!15?~!7?~PICIP_?_PICIP_?~!7?~TGTaTGTaTGTaTGT~!7?!17~!7?~?@ACGO_??@ACGO_~!7?~OGCA@??_OGCA@??~!7?~BKo?BKo?BKo?BKo~!7?~KB?oKB?oKB?oKB?~!7?~!15?~!7?~PICIP_?_PICIP_?~!86?~$ 341 | #1!629?DBFBD!6?$ 342 | #9!581?CGHHE???HHDB!8?!22@!17?$ 343 | - 344 | #0~!47?!65O!15?A}!4?w?_?w!10?C}!169?~!15?~!7?~CgOgCA@ACgOgCA@~!7?~TaTGTaTGTaTGTaT~!7?!17~!7?~ACGO_??@ACGO_??~!7?~?_OGCA@??_OGCA@~!7?~Ko?BKo?BKo?BKo?~!7?~oKB?oKB?oKB?oKB~!7?~!15?~!7?~CgOgCA@ACgOgCA@~!86?~$ 345 | #1!629?wGgGw!6?$ 346 | #10!581?_OG{???wCcSw!8?!22_!17?$ 347 | - 348 | #0~!127?ABA???@A@A@!10?ABA!168?~!15?~!7?~P_?_PICIP_?_PIC~!7?~TGTaTGTaTGTaTGT~!7?!17~!7?~GO_??@ACGO_??@A~!7?~@??_OGCA@??_OGC~!7?~o?BKo?BKo?BKo?B~!7?~?oKB?oKB?oKB?oK~!7?~!15?~!7?~P_?_PICIP_?_PIC~!86?~$ 349 | #1!629?BAAAB!6?$ 350 | #10!581?@@@F@??BDCCB!47?$ 351 | - 352 | #0~!319?~!15?~!7?~CA@ACgOgCA@ACgO~!7?~TaTGTaTGTaTGTaT~!7?!17~!7?~_??@ACGO_??@ACG~!7?~CA@??_OGCA@??_O~!7?~?BKo?BKo?BKo?BK~!7?~B?oKB?oKB?oKB?o~!7?~!15?~!7?~CA@ACgOgCA@ACgO~!86?~$ 353 | - 354 | #0~!319_~!15_~!7_~picip___picip__~!7_~tgtatgtatgtatgt~!7_!17~!7_~_`acgo___`acgo_~!7_~ogca`___ogca`__~!7_~bko_bko_bko_bko~!7_~kb_okb_okb_okb_~!7_~!15_~!7_~picip___picip__~!86_~$ 355 | - 356 | \ 357 | -------------------------------------------------------------------------------- /testfiles/gnuplot_clean.six: -------------------------------------------------------------------------------- 1 | #0;2;100;100;100 2 | #0~!319@T!7@?!310@~$ 3 | #1!15?_!4O???Os!4?O_?_O??_OOO_???C{!18?OO{OO??_OOO_??o_OO_??oO_O_???Os!4?o_OO_???OOO_???C{!11?OO{OO??_OOO_??_!4O??OO{OO!487?$ 4 | #5!328?N!311?$ 5 | - 6 | #0~!319?T!260?!5O???C}!10?!22O!16?~$ 7 | #1!15?CDDDA???CFC???CA@AC??BDDD@???CFC!19?BCA??BDDD@??F!6?F?F?F???CFC???F???F??ADDDF???CFC!12?BCA??BDDD@??CDDDA!4?BCA!487?$ 8 | #5!224?!4_??w?__!4?___???_???_!9?__w__???_g!5?___!4?!4_???___!4?___!4?Gw!5?___!9?!5A!315?$ 9 | - 10 | #0~!319?T!268?ABA!47?~$ 11 | #5!223?HIIIC??N@??N??FGGGF??FGEGF!11?FGC???GNG???FGGGD??HIIIC??FGGGD??CIIIN???GNG???FIIIB!328?$ 12 | - 13 | #0~!319?T!267?}PHD}!8?G?G?G?G?G?G?G?G?G?G?G!17?~$ 14 | #1!631?G!8?$ 15 | - 16 | #0~!319?T!268?@@@!47?~$ 17 | #1!590?_!49?$ 18 | - 19 | #0~!319?T!318?~$ 20 | #1!589?`~_!9?!22C!6?CC^CC!6?$ 21 | - 22 | #0~!319?T!187?_!5?_!5?__!5?__!15?_!24?__!68?~$ 23 | #1!629?_???_!6?$ 24 | #2!588?_OOO_!47?$ 25 | - 26 | #0~!319?T!185?C~C@???a}_!4?_~_!4?_~_???[iiiK??[aaS~!9?}Saa[??[aaa[???_~_???]___}??[aaS}??[aaa[??}CAA{??ciiiQ???ZZ!26?~$ 27 | #1!629?GDADG!6?$ 28 | #2!588?[AOOP!8?AAA!4?AAA??!10A!17?$ 29 | - 30 | #0~!319?T!233?B!21?AAA@??@AAA@!52?~$ 31 | #1!629?O_o_O!6?$ 32 | #3!588?OGGGo!47?$ 33 | - 34 | #0~!319?T!318?~$ 35 | #1!629?DBFBD!6?$ 36 | #3!429?_w}!33~}w_!120?CGHHE!8?!22@!17?$ 37 | - 38 | #0~!319?T!318?~$ 39 | #1!629?wGgGw!6?$ 40 | #3!425?_o{!41~{o_!168?$ 41 | #4!588?_OG{!9?!22_!17?$ 42 | - 43 | #0~!319?T!318?~$ 44 | #1!629?BAAAB!6?$ 45 | #2!463?_!33o_!142?$ 46 | #3!422?_w}!38~^!8NMG!166?$ 47 | #4!588?@@@F@!47?$ 48 | - 49 | #0~!319?T!318?~$ 50 | #1!629?OgSgO!6?$ 51 | #2!460?ow}!35~}wo!139?$ 52 | #3!418?_o{!39~NF@!177?$ 53 | #5!588?MIIIq!8?!22O!17?$ 54 | - 55 | #0~!319?T!318?~$ 56 | #1!631?@!8?$ 57 | #2!456?_w{!43~{w_!135?$ 58 | #3!416?G}!38~^FB!181?$ 59 | #5!588?@AAA@!47?$ 60 | - 61 | #0~!319?T!318?~$ 62 | #1!629?O[Y[O!6?$ 63 | #2!453?ow}!49~}wo!132?$ 64 | #3!418?BN^!32~NF@!184?$ 65 | #6!588?{IHHo!8?!22G!17?$ 66 | - 67 | #0~!319?T!318?~$ 68 | #2!449?_w{!57~{w_!128?$ 69 | #3!421?@F^!25~^FB!188?$ 70 | #6!589?@@@!48?$ 71 | #7!588?!5_!47?$ 72 | - 73 | #0~!319?T!318?~$ 74 | #1!629?CC^CC!6?$ 75 | #2!448?AN!61~NA!127?$ 76 | #3!425?BN!21~|o!190?$ 77 | #7!588?oGCA@!8?!22C!17?$ 78 | - 79 | #0~!319?T!318?~$ 80 | #1!629?_???_!6?$ 81 | #2!451?BF^!53~^FB!130?$ 82 | #3!428?@F^!20~{w_!186?$ 83 | #8!588?_OOO_!47?$ 84 | - 85 | #0~!319?T!318?~$ 86 | #1!629?GDADG!6?$ 87 | #2!21?LRPRLPM!4?LRPRLPM!415?@FN!47~NF@!133?$ 88 | #3!431?!23@!186?$ 89 | #8!588?LQQQL!8?!22A!17?$ 90 | - 91 | #0~!319?T!318?~$ 92 | #1!629?O_o_O!6?$ 93 | #2!21?[a}_[!6?[a}_[!421?BN^!39~^NB!137?$ 94 | #9!588?oGGGo!47?$ 95 | - 96 | #0~!319?T!318?~$ 97 | #1!629?DBFBD!6?$ 98 | #2!19?CCsKCKs!4?CCsKCKs!424?@F!35^F@!140?$ 99 | #9!589?HHDB!8?!22@!17?$ 100 | - 101 | #0~!319?T!318?~$ 102 | #1!629?wGgGw!6?$ 103 | #2!22?@@@!8?@@@!604?$ 104 | #10!582?G{!4?wCcSw!8?!22_!17?$ 105 | - 106 | #0~!319?T!318?~$ 107 | #1!629?BAAAB!6?$ 108 | #2!19?o?_OOO_!4?o?_OOO_!603?$ 109 | #10!582?CFC???BDCCB!47?$ 110 | - 111 | #0~!319?T!318?~$ 112 | #1!629?OgSgO!6?$ 113 | #2!19?FCF?_CB!4?FCF?_CB!603?$ 114 | #11!582?C}!5?C}!10?!22O!17?$ 115 | - 116 | #0~!319?T!318?~$ 117 | #1!631?@!8?$ 118 | #2!19?@BDHN@@!4?@BDHN@@!603?$ 119 | #11!582?ABA!4?ABA!48?$ 120 | - 121 | #0~!319?T!318?~$ 122 | #1!629?O[Y[O!6?$ 123 | #2!22?^!8?CC^CC!604?$ 124 | #12!582?A~!4?qHHHE!8?!22G!17?$ 125 | - 126 | #0~!319?T!318?~$ 127 | #12!582?@@@???!5@!47?$ 128 | #13!583?_!5?___!48?$ 129 | - 130 | #0~!319?T!318?~$ 131 | #1!629?CC^CC!6?$ 132 | #2!10?_??_!7?!4C{Cw!4?!4C{Cw!601?$ 133 | #13!582?`~_???P_ccZ!8?!22C!17?$ 134 | - 135 | #0~!319?T?C{!4?_OOO_???_wcG??OO{OO!11?Os???o???o??_!4O??OO{OO???Os!5?_wcG???Os!4?_OOO_??_OO_{!222?~$ 136 | #1!629?_???_!6?$ 137 | #2!8?AANAAA@!6?pHHHo!6?pHHHo!603?$ 138 | #14!582?_o!6?_o!48?$ 139 | - 140 | #0~!319?T?CFC???BDDD@!4?F!6?BCA!9?GOON???BCCAF??CDDDA!4?BCA???CFC!5?F!5?CFC???BDDD@??BCCAF!222?~$ 141 | #1!629?GDADG!6?$ 142 | #2!10?PICIP!4?AAABABA!4?AAABABA!603?$ 143 | #14!582?O^O???EDC^C!8?!22A!17?$ 144 | - 145 | #0~!275?oGGGO??ogggo??wOGGo??GG}GG??wOGGO??ogggo??OO|OO??oGGO}!9?GG}GG??ogggo??GO_OG??GG}GG!274?~$ 146 | #1!629?O_o_O!6?$ 147 | #2!10?[a}_[!4?!7_!4?!7_!603?$ 148 | #15!582?Ow!4?wgggG!47?- 149 | #0~!275?@AAA@??@AAA???B???B!4?@A@??B!6?@AAA!5?T!4?@AA@B!11?@A@??@AAA???A@?@A!4?@A@!274?~$ 150 | #1!629?DBFBD!6?$ 151 | #2!8?OO{OOSG!6?EHGHE!6?EHGHE!603?$ 152 | #15!582?GNG???CGGGF!8?!22@!17?- 153 | #0~!215?{GCCG???C|!4?wCCg{??~GCCw??CC~Cc!11?C|???{??_{??GSSSc??CC~Cc???C|!5?G}HA???C|!4?wSSSW??wCCg~?T!318?~$ 154 | #1!582?G{!4?ogcc!9?!22_!6?wGgGw!6?$ 155 | #2!10?@!10?MP^OM!6?MP^OM!603?$ 156 | - 157 | #0~!215?@!7?@@@???ADDCB??@???@!5?@!10?ACCB!4?@@?@??!4@!6?@!4?@@@!5?@!5?@@@!4?@@@!4?@@?@?T!318?~$ 158 | #1!582?CFC???BCCCB!36?BAAAB!6?$ 159 | #2!8?OOOoOoO!4?GG}GGIC!4?GG}GGIC!603?$ 160 | - 161 | #0~!319?T!318?~$ 162 | #1!629?OgSgO!6?$ 163 | #2!10?BccCB!6?wC{C{!6?wC{C{!545?C}!4?AaQIE!8?!22O!17?$ 164 | - 165 | #0~!319?T!318?~$ 166 | #1!631?@!8?$ 167 | #2!10?FGNGF!4?__w__hO!4?__w__hO!545?ABA???B!51?$ 168 | - 169 | #0~!319?T!318?~$ 170 | #1!629?O[Y[O!6?$ 171 | #2!10?UXOOO!6?bOOO_!6?bOOO_!603?$ 172 | #3!582?A~!4?uHHHu!8?!22G!17?$ 173 | #4!253?_!20?_!21?_!17?_!36?_!34?_!8?_!4?_!4?_!234?$ 174 | - 175 | #0~!319?T!318?~$ 176 | #2!8?GG}GGIC!6?BcCCB!6?BcCCB!603?$ 177 | #3!582?@@@!4?@@@!48?$ 178 | #4!251?AA^aQ??[iiiK??ciiiQ??AA^aQ!9?[aaa[???C~C@!9?[aaaS??~CAA{??Oiii{??}CAAC??Oiii{??[aaaS??AA^aQ??[iiiK??}CAAC!9?]_W_]???a}_???[aaS~??AA^aQ??~CAA{???ZZ!168?_!5?___!48?$ 179 | - 180 | #0~!319?T!318?~$ 181 | #1!629?CC^CC!6?$ 182 | #2!10?WcCCC!6?JKGGG!6?JKGGG!603?$ 183 | #4!582?`~_???BccSN!8?!22C!17?$ 184 | - 185 | #0~_?_?_?_!7?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_???_?_!5?_?_???_?_?_???_?_?_???_!5?_?_???_?_!5?_?_!5?_?_?_?_S_???_?_!5?_?_???_?_?_???_?_?_???_!5?_?_???_?_!5?_?_!5?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_???_???_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_???_???_?_?~$ 186 | #1!629?_???_!6?$ 187 | #2!9?_`x``@!625?$ 188 | #4!250?~@H|!4@HdddX@@HDddX@@`PH|@@@\TTTd@@phdd@@@DDdTL@@XdddX@@Xdddx@@xDdTx@@@H|!4@HdddX@@HDddX@@`PH|@@@\TTTd@@phdd@@@DDdTL@@XdddX@@Xdddx@@xDdTx@~!249?$ 189 | #5!581?_OOO_??_OOO_!47?$ 190 | - 191 | #0~!319?D!318?~$ 192 | #1!629?GDADG!6?$ 193 | #2!10?_Ro?_!625?$ 194 | #4!250?^OSVSOOOV!4SOOQSSSROOPPPVPOOQSSSROORSSSROOUP!5ORSSSROOOSSQPOORTSSROOOSVSOOOV!4SOOQSSSROOPPPVPOOQSSSROORSSSROOUP!5ORSSSROOOSSQPOORTSSRO^!249?$ 195 | #5!581?[QQQP??NSQPN!8?!22A!17?$ 196 | - 197 | #0~!319?T!318?~$ 198 | #1!629?O_o_O!6?$ 199 | #2!10?BcFcB!625?$ 200 | #6!581?OGGGo???Ow!49?$ 201 | - 202 | #0~!319?T!318?~$ 203 | #1!629?DBFBD!6?$ 204 | #2!10?FGGGF!625?$ 205 | #6!581?MHHHG???GNG!9?!22@!17?$ 206 | - 207 | #0~!319?T!318?~$ 208 | #1!629?wGgGw!6?$ 209 | #7!581?GcccW??GcccW!8?!22_!17?$ 210 | - 211 | #0~!319?T!318?~$ 212 | #1!629?BAAAB!6?$ 213 | #2!8?AAYeaeY!625?$ 214 | #7!581?F!4C??F!4C!47?$ 215 | - 216 | #0~!319?T!318?~$ 217 | #1!629?OgSgO!6?$ 218 | #2!10?wC{?w!625?$ 219 | #8!581?cQQQK??CAQQk!8?!22O!17?$ 220 | - 221 | #0~!319?T!318?~$ 222 | #1!631?@!8?$ 223 | #2!8?__w``hO!625?$ 224 | #8!581?B!4A??@AAA@!47?$ 225 | - 226 | #0~!319?T!318?~$ 227 | #1!629?O[Y[O!6?$ 228 | #2!10?bOoOo!625?$ 229 | #9!581?qHHHE??WSQ~O!8?!22G!17?$ 230 | - 231 | #0~!319?T!318?~$ 232 | #2!10?b?BcB!625?$ 233 | #9!581?!5@!5?@!48?$ 234 | #10!582?___???!5_!47?$ 235 | - 236 | #0~!319?T!318?~$ 237 | #1!629?CC^CC!6?$ 238 | #2!8?AANAAA@!625?$ 239 | #10!581?xcccb??Raaa[!8?!22C!17?$ 240 | - 241 | #0~!319?T!318?~$ 242 | #1!629?_???_!6?$ 243 | #2!10?MPPPM!625?$ 244 | #11!581?_OOO_???_OO!48?$ 245 | - 246 | #0~!319?T!318?~$ 247 | #1!629?GDADG!6?$ 248 | #2!10?kq___!625?$ 249 | #11!581?[QQQP??NQQQK!8?!22A!17?$ 250 | - 251 | #0~!319?T!318?~$ 252 | #1!629?O_o_O!6?$ 253 | #12!581?OGGGo??GGGgW!47?$ 254 | - 255 | #0~!319?T!318?~$ 256 | #1!629?DBFBD!6?$ 257 | #12!581?MHHHG??KA@!10?!22@!17?$ 258 | - 259 | #0~!319?T!318?~$ 260 | #1!629?wGgGw!6?$ 261 | #13!581?GcccW??WcccW!8?!22_!17?$ 262 | - 263 | #0~!319?T!318?~$ 264 | #1!239?w}w!387?BAAAB!6?$ 265 | #13!581?F!4C??BCCCB!47?$ 266 | - 267 | #0~!319?T!318?~$ 268 | #1!220?GO_!15?EF~FE!14?_OoG!368?OgSgO!6?$ 269 | #14!581?cQQQK??KQQQ{!8?!22O!17?$ 270 | - 271 | #0~!49?A}!5?Gy!4?wOGGo??ogggo??w?_?w???Gy!4?oGGO}??GG}GG??}OGGo!210?T!318?~$ 272 | #1!223?@ACGO_!11?~!11?_OIDHE@!372?@!8?$ 273 | #14!581?B!4A???AA@!48?$ 274 | - 275 | #0~!49?ABA!4?ABA???B???B??@AAA???@A@A@???ABA???@AA@B!4?@A@??B???B!210?T!318?~$ 276 | #1!229?@ACGO_!5?~!5?_OGCA@!377?O[Y[O!6?$ 277 | #15!581?a@HHu??}PHD}!8?!22G!17?- 278 | #0~!319?T!318?~$ 279 | #1!212?__!4OGw!15_`acgo~ogca`!15_gg!4o__!313?___!5?_!49?$ 280 | #15!582?@@@!4?@@@!48?- 281 | #0~!45?!70{!12?C{!4?o???o!9?ogcc!168?T!318?~$ 282 | #1!214?!4@AB!14?_OGCA@~BDYcSg!14?AA!4@!314?P_ccZ???`~_!9?!22C!6?CC^CC!6?$ 283 | - 284 | #0~!45?!70B!12?CFC???BCBCB!9?BCCCB!167?T!318?~$ 285 | #1!228?_OGCA@!6?~!6?@ACGO_!376?_???_!6?$ 286 | #2!581?_OOO_??_OOO_!47?$ 287 | - 288 | #0~!319?T!318?~$ 289 | #1!221?_OkQSI@!12?~!12?@ACGO_!370?GDADG!6?$ 290 | #2!581?GOQQL??[QQQP!8?!22A!17?$ 291 | - 292 | #0~!46?!69w!12?Gw!4?_???_!9?wgggG!167?T!318?~$ 293 | #1!220?A@@!15?KsFsK!16?@A!368?O_o_O!6?$ 294 | #3!581?OGGGo??OGGGo!47?$ 295 | - 296 | #0~!46?!69B!12?GNG???FGEGF!9?CGGGF!167?T!318?~$ 297 | #1!239?BKB!387?DBFBD!6?$ 298 | #3!581?CGHHE??CGHHE!8?!22@!17?$ 299 | - 300 | #0~!319?T!318?~$ 301 | #1!629?wGgGw!6?$ 302 | #4!581?GCccW??_OG{!9?!22_!17?$ 303 | - 304 | #0~!46?!68_!13?Oo!20?_o!168?T!128?o!6?o!34?_O_!4?O!5?Oo!5?Oo!126?~$ 305 | #1!629?BAAAB!6?$ 306 | #4!581?ACCCB??@@@F@!47?$ 307 | - 308 | #0~!46?!68F!13?O^O???NOKON!9?EDC^C!167?T!112?~IPPM??GTTT]??@@NPH??@@NPH??MTTTE??^A@@A??^A@@]!10?A^A!4?P^O!4?O^O!4?O^O!125?~$ 309 | #1!629?OgSgO!6?$ 310 | #5!581?CAQQk??MIIIq!8?!22O!17?$ 311 | - 312 | #0~!319?T!15?oGGgo!20?Ow!21?OGGGo!19?OGGGo!20?`Ow!20?wgggG!19?_OGG!20?GGGgW!19?oGGGo!19?oGGGo!82?~$ 313 | #1!631?@!8?$ 314 | #5!581?@AAA@??@AAA@!47?$ 315 | - 316 | #0~!127?__!19?___!168?t!15_fIHGF???!17_GNG!4?!16_mHHHG???!16_cGHHE???!16_bAANA???!16_cGGGF???!16_fHHHE???!16_kA@!5?!16_eHHHE???!17_HHDB!82?~$ 317 | #1!629?O[Y[O!6?$ 318 | #6!581?a@HHu??{IHHo!8?!22G!17?$ 319 | - 320 | #0~!47?!67F!13?_~_???]_W_]!9?P_ccZ!167?~!15?~!7?~CA@ACgOgCA@ACgO~!7?~TaTGTaTGTaTGTaT~!7?!17~!7?~_??@ACGO_??@ACG~!7?~CA@??_OGCA@??_O~!7?~?BKo?BKo?BKo?BK~!7?~B?oKB?oKB?oKB?o~!7?~!15?~!7?~CA@ACgOgCA@ACgO~!86?~$ 321 | #6!582?@@@!4?@@@!48?$ 322 | #7!582?___???!5_!47?$ 323 | - 324 | #0~!319?~!15?~!7?~PICIP_?_PICIP_?~!7?~TGTaTGTaTGTaTGT~!7?!17~!7?~?@ACGO_??@ACGO_~!7?~OGCA@??_OGCA@??~!7?~BKo?BKo?BKo?BKo~!7?~KB?oKB?oKB?oKB?~!7?~!15?~!7?~PICIP_?_PICIP_?~!86?~$ 325 | #1!629?CC^CC!6?$ 326 | #7!581?P_ccZ??oGCA@!8?!22C!17?$ 327 | - 328 | #0~!319?~!15?~!7?~CgOgCA@ACgOgCA@~!7?~TaTGTaTGTaTGTaT~!7?!17~!7?~ACGO_??@ACGO_??~!7?~?_OGCA@??_OGCA@~!7?~Ko?BKo?BKo?BKo?~!7?~oKB?oKB?oKB?oKB~!7?~!15?~!7?~CgOgCA@ACgOgCA@~!86?~$ 329 | #1!629?_???_!6?$ 330 | #8!581?_OOO_??_OOO_!47?$ 331 | - 332 | #0~!47?!66K!14?@~!4?{?o?{!9?qHHHE!167?~!15?~!7?~P_?_PICIP_?_PIC~!7?~TGTaTGTaTGTaTGT~!7?!17~!7?~GO_??@ACGO_??@A~!7?~@??_OGCA@??_OGC~!7?~o?BKo?BKo?BKo?B~!7?~?oKB?oKB?oKB?oK~!7?~!15?~!7?~P_?_PICIP_?_PIC~!86?~$ 333 | #1!629?GDADG!6?$ 334 | #8!581?GOQQL??LQQQL!8?!22A!17?$ 335 | - 336 | #0~!127?@@@!4?@?@!10?!5@!167?~!15?~!7?~CA@ACgOgCA@ACgO~!7?~TaTGTaTGTaTGTaT~!7?!17~!7?~_??@ACGO_??@ACG~!7?~CA@??_OGCA@??_O~!7?~?BKo?BKo?BKo?BK~!7?~B?oKB?oKB?oKB?o~!7?~!15?~!7?~CA@ACgOgCA@ACgO~!86?~$ 337 | #1!629?O_o_O!6?$ 338 | #9!581?OGGGo??oGGGo!47?$ 339 | - 340 | #0~!319?~!15?~!7?~PICIP_?_PICIP_?~!7?~TGTaTGTaTGTaTGT~!7?!17~!7?~?@ACGO_??@ACGO_~!7?~OGCA@??_OGCA@??~!7?~BKo?BKo?BKo?BKo~!7?~KB?oKB?oKB?oKB?~!7?~!15?~!7?~PICIP_?_PICIP_?~!86?~$ 341 | #1!629?DBFBD!6?$ 342 | #9!581?CGHHE???HHDB!8?!22@!17?$ 343 | - 344 | #0~!47?!65O!15?A}!4?w?_?w!10?C}!169?~!15?~!7?~CgOgCA@ACgOgCA@~!7?~TaTGTaTGTaTGTaT~!7?!17~!7?~ACGO_??@ACGO_??~!7?~?_OGCA@??_OGCA@~!7?~Ko?BKo?BKo?BKo?~!7?~oKB?oKB?oKB?oKB~!7?~!15?~!7?~CgOgCA@ACgOgCA@~!86?~$ 345 | #1!629?wGgGw!6?$ 346 | #10!581?_OG{???wCcSw!8?!22_!17?$ 347 | - 348 | #0~!127?ABA???@A@A@!10?ABA!168?~!15?~!7?~P_?_PICIP_?_PIC~!7?~TGTaTGTaTGTaTGT~!7?!17~!7?~GO_??@ACGO_??@A~!7?~@??_OGCA@??_OGC~!7?~o?BKo?BKo?BKo?B~!7?~?oKB?oKB?oKB?oK~!7?~!15?~!7?~P_?_PICIP_?_PIC~!86?~$ 349 | #1!629?BAAAB!6?$ 350 | #10!581?@@@F@??BDCCB!47?$ 351 | - 352 | #0~!319?~!15?~!7?~CA@ACgOgCA@ACgO~!7?~TaTGTaTGTaTGTaT~!7?!17~!7?~_??@ACGO_??@ACG~!7?~CA@??_OGCA@??_O~!7?~?BKo?BKo?BKo?BK~!7?~B?oKB?oKB?oKB?o~!7?~!15?~!7?~CA@ACgOgCA@ACgO~!86?~$ 353 | - 354 | #0~!319_~!15_~!7_~picip___picip__~!7_~tgtatgtatgtatgt~!7_!17~!7_~_`acgo___`acgo_~!7_~ogca`___ogca`__~!7_~bko_bko_bko_bko~!7_~kb_okb_okb_okb_~!7_~!15_~!7_~picip___picip__~!86_~$ 355 | - 356 | -------------------------------------------------------------------------------- /testfiles/leonardo.six: -------------------------------------------------------------------------------- 1 | [?80hP0;0;0;q"1;2;461;464 2 | #15;2;100;100;100!247~!2^!212~$#8;2;75;75;75!463?$-#15!241~^NFb@o{!213~$#8!463?$-#15!236~^NFB@!3?o}!215~$#8!239?ow{]B 3 | !219?$-#15!231~^NFB!7?w!218~$#8!234?_w{}!3~B!221?$-#15!226~^NFB!10?_}!219~$#8!228?_ow{}!6~F!223?$-#15!222~^FB@!13?{!221~$#8!224? 4 | _w{}!9~F!225?$-#15!218~NFB!16?_!223~$#8!219?_ow{!12~^@!226?$-#15!213~^NB@!19?_!224~$#8!215?_o{}!15~^!228?$-#15!209~^NB@!23?!225~ 5 | $#8!211?_o{}!19~!229?$-#15!205~^NB@!26?w!225~$#8!207?_o{}!22~@!229?$-#15!201~^NB@!25?_ow!2{!226~$#8!203?_o{}!21~^NFB!231?$-#15 6 | !198~NB@!24?!2o!2{!232~$#8!200?o{}!20~^NFB!236?$-#15!194~^F@!22?_ow{}!237~$#8!196?_w}!19~NFB@!241?$-#15!191~NB!21?_ow}!243~ 7 | $#8!193?o{!17~^NFB@!246?$-#15!178~^!3N!7FB!19?_ow{}!29~!9^!106~!2^!2NFbrz!95~$#8!190?ow}!14~^NFB@!251?$-#15!169~^N!2F 8 | B!2@!29?w{}!19~^!2N!2F!2B!3@!19?!4@!2B!2FN^!78~!2^!2N!3F!2B!2@!6?_w!98~$#8!187?ow}!12~^NB@!141? 9 | !3_!2o!3w!2{!2}^F@!101?$#2;2;0;50;0!171?!2_ow!2{!2}!3~!3^NB@!42?_!3o!4w!7{!5}!3{!4w!3o 10 | !2_$-#15!164~^FB@!38?@F^!10~^!4NFB!42?@FN!12~!2^!5N!4^!12~!4^!20~!2^!3N!2F!2B!3@!16?_o}!100~$#2!166? 11 | _ow}!5~NFB!2@!46?w}!35~!2}{wo$#8!185?w}!10~NFB@!132?!3_!2o!3w!2{!2}!12~N@!104?$#12;2;0;0;100!199?ow{ 12 | !2}{o!15?!3_$#3;2;50;50;0!176?_OgSiSI$#1;2;50;0;0!177?_OgSiCA$-#15!161~NB!46?!2B!2FN^~V!2B!53?B^!2~ 13 | ^N!2FB@!22?ow{}!3{!2xz!2~!2^!2N!3F!2B!3@!28?o{!103~$#2!163?o{}^Nfrx{M@!23?_!3o!5w!3o!2_!17?!4@ 14 | !3B!3F!6N!6^!11~!8^S$#8!184?!8~NFB@!123?!3_!2o!3w!2{!2}!23~^F!107?$#12!196?KMF!3B!4@!14?G^!2~fec 15 | KG!2W!5o!3_!40?_o!2w!2{!6}!6{w!3{KC$#3!174?gSiTiTiT$#1!173?_OiTiTiTi$-#15!158~^B!55?@!11?W!2{wo_!41?!2@!23? 16 | Ow{]^!2N!2F!3B!2@!41?w!106~$#12!218?M!2^!3}~b!2@!4?@FN!2^!8~!8}!3]!3M!3]!10}{!3?W{wp!3@|!2{ 17 | !3|!3xprbB!3@$#8!183?A!2~^NFB!117?_!2o!3w!2{!2}!35~F@!109?$#2!161?o}B?o}!2~!2^@!24?w_BN!12~}][K$#3!172? 18 | _TiTiTiTiT!6?_OgSiS$#1!172?OiTiTiTiTi!7?_OgTi$-#15!158~!8?G!62?!5@!14?G!2[!2{!2]MA@!21?!2_!70?_{!108~$#8!184? 19 | @!107?!3_!2o!3w!2{!2}!44~!2^B!112?$#12!222?!2@!2B!9?!3CF!4N!3^!3@!9?{!3}!5~r!2zZ!2XKc!2? 20 | HNFB!2?q!2owxZ!2^!2N!2F!2B@$#2!160?{!2~o_!30?{~^!2?w!5~^NFbxw{}!4~^][!2KG!2g!13_$#3!172?ATiTiTiTiTgO 21 | _OiTiTiTi@$#1!172?TiTiTiTiTiO_OgSiTiTiTA$-#15!158~_!117?WKN!2FA!54?!3_!2o!3w!3{!2}{}!110~$#2!160?@!4~@?!4}w!21? 22 | _bxw{}!4~NB?w}!6~!2|{!3}!24~!4}!4{w!2o_!3?!14o_$#8!278?!3_!2o!2w!3{!2}!42~!3^!2N!3F!3B!3@ 23 | !117?$#12!254?AE!9FDC!4E!2FB!10?!2@$#3!173?@ITiTiTiTiTiTiTiTID$#1!173?ATiTiTiTiTiTiTiTiTA$-#15!159~}!2wo_!156?!3_!2o 24 | !3w!3{!2}!128~$#2!162?@BF!3?BN^!2~{wo!2_!8?_!2ow{}!11~!3?!8~!2^!41~}!2{!3^!2N!2F!3B@$#8!264? 25 | !2_!3o!3w{!3}!40~!3^!2N!3F!3B!2@!134?$#3!176?A@ADIDIDIDADA$#1!176?@ADADIDIDIDA@$-#15!164~{wo!7_!137?GW!2{ 26 | !2}!144~$#2!174?!2@GW{}~Nn^N!18~}o?BN^!2~r!5_!2`rv!2f!2n!3N!6^!11~!3^!3N!2F!2B!2@$#8!250?!2_!3o 27 | !2w!2{!3}!39~!2^!3N!3F!2B!3@!149?$-#15!174~}wo_!116?GW[!5{!3w!3o_!4?@N!146~$#8!234?!3_!3o!2w!3{ 28 | !2}!36~^!6N!2F!3B!3@!165?$#2!178?@FN?N]?N!7~^!2Nn!2N^!4~!2^]KG?P!2bF!3N!2^!11~!3]MK!2C!6@$#12 29 | !302?!4@!2B!2FM[o$-#15!179~]!2[{!4W!2G!109?@B!2N!8~}{wo?N!145~$#8!219?!3_!2o!3w!3{!2}!33~!4^!3N!2F 30 | !4B!3@!182?$#2!184?@?@!3B@`{}]!4^]!2{xr!8?B~!3]MK?@?!6@!42?!3_!3o!3w!3{}!3mG$#1!205?GS!2?iS!77?@ 31 | ITiTiTiSgO_$#3!205?Si!2?Sg!78?@ITiTiSgO_$-#15!174~NB@!126?@!3^!7~!2}!145~$#8!211?w!2{!3}!27~!4^!5N!2F!4B!3@ 32 | !201?$#2!177?_w!2KC!2?!2CKM?_arx{!2}Aw{}!4~!46?!4_!2?!2o!3w!3{!3}fFN^!12~!3n!2N]$#1!206?T_?a@!80? 33 | ITiTiTiTiTI$#3!206?i!2?P?@!80?ATiTiTiSG$-#15!170~NB@!62?O?O!70?@B!2FN!148~$#2!172?ow!2{}!2~!3{o@yBw?^!7~EF!6~_!34? 34 | GK!2[{!3}!8~}obN!10~}]!2N!2Ffv!2zx|!7~!2}{w!2o_$#8!211?@!14~!3^!3N!4F!3B!3@!221?$#1!206?TiOAO!81? 35 | @A?IPiPgSiTiTiSgO_$#3!206?IT_@_!82?@?@!2?GOgSiSiSgO_$-#15!169~@!47?O?O?O?S?S?S?T?T?T?T?T?T?S?S?O!68?@FN!144~$#2!171?w!5~N 36 | !2FfN^}{}~}oF!6~?~^NF!4~o!38?@B!2F^!13~^NFBbpow{!2}!23~}{wo$#9;2;100;0;0!217?_OgOgSgSiSiTiTiTiTiTiTiTiSgOg 37 | O$#1!207?iTg@gO!85?@ADITiTiTiTiTiSgO$#3!207?TiOAO_?_!86?@ITiTiTiTiSgO$#11;2;100;100;0!218?_?g?g?i?i?i?i?i?i?i?i?i?i?g?_$#8 38 | !213?B!3F!2B!4@!240?$-#15!148~^NF!3B!2@!2B!2FN!6FN@!48?@?T?T?T?T?T?T?T?T?T?T?T?T?T?T?T?O!68?@F^!140~$#2!170?o!6~ 39 | !2o!2~o?!2~!2^!2~}WF!4~o?E!2o!5~w!43?@BF^!3~^NB@?_oyz\!2Nnfv!29~}wo$#9!218?TiTiTiTiTiTiTiTiTiTiTiTiTiTiT 40 | iSgO$#1!151?_OgOGOG!7?_!42?DiOATiTi!88?DITiTiTiTiTiTiO_$#3!152?_OGOG!7?_!43?AT_@ITiS!89?@ITiTiTiTiSg$#11!218?i?i?i?i?i? 41 | i?i?i?i?i?i?i?i?i?i?i?_$#8!463?$-#15!145~^B!70?S?T?T?T?T?T?T?T?T?T?T?T?T?T?T?T?T?T?O!5?GC!60?B!139~$#2!170?!11~{_bpo?p{]Nv!3~ 42 | N`}!2~F^!6~w!46?@!4?_rz\KMev!36~}w$#9!217?iTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiSG$#1!147?_!2?SiT!2?iT!2?iT!2?iTi 43 | O_!42?@I!2?DA!91?ATiTiTiTiTiTiS$#3!147?O!2?iTi!2?Si!2?Ti!2?TiTg!44?C?@I@!92?ATiTiTiTiTi$#11!218?i?i?i?i?i?i?i?i?i 44 | ?i?i?i?i?i?i?i?i?i$#8!463?$-#15!145~o_!16?!4_o_!42?C?S?S?T?T?T?T?T?T?T?T?T?T?T?T?T?T?T?T?T?D!4?O!65?_!138~$#2!170?F!5~^ 45 | N~^!6~w@?}!5~?Fr!2B}ob!3~!2^F!48?KerX[Km!2fvr!3zx!2|{!32~$#9!211?GSgSiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTI@$#1 46 | !147?AO?DITiO_@!2?ADAC?@ADA!140?CiTiTiTiTiTID$#3!147?@!2?ATiSgOaC?@AD?@ADA@!141?DiTiTiTiTID$#11!212?G?g?i?i?i?i?i?i?i?i?i?i 47 | ?i?i?i?i?i?i?i?i$#8!463?$-#15!148~}!10{}!9~{o_!43?T?T?T?T?T?T?T?T?T?T?T?T?T?T?T?T?T?T?O!5?@!62?C]!139~$#2!172?@BFB?^?cp@N 48 | !5~{xr`_OR!2NFEAB!3@!51?o!2w!2{}!46~$#9!214?@ATiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTG$#11!216?i?i?i?i?i?i?i?i?i?i?i?i? 49 | i?i?i?i?i?i$#12!196?!3_!2o!2wW[KMebrxw_$#1!309?gPA@aPaPgO_$#3!309?O_@a@aPgO_$#8!463?$-#15!172~^A!3?O!39?@?T?T?T?T?T?T?T? 50 | T?T?T?T?T?T?T?D?@!72?@N!137~$#2!180?@A!2?@!2B!3@!59?ow{!2}!38~^frN!9~N$#12!184?_!3o!2w!2[K!2mfvrzX\Kkm}!8~ 51 | }[$#9!176?O_?_!36?@aTiTiTiTiTiTiTiTiTiTiTiTiTiTiDA@$#11!176?_?_!39?i?i?i?i?i?i?i?i?i?i?i?i?i?i?I?A$#1!308?OI?iTiTiTiTiTi$#3!308? 52 | gD?SiTiTiTiTiO$#8!463?$-#15!171~B!3?T?T?T?O!35?@?D?T?T?T?T?T?T?T?T?T?T?T?D!80?!137~$#2!209?!2_!2ow!2{o_!28?o{!39~^Nf 53 | r|Mfp{!5~^FB$#12!184?BD!2L!2mev!2rz!2x|!2[]nfvrx\[N!2FB@$#9!174?OiTiTiT_!36?@ADITiTiTiTiTiTiTiTiTiTiTI@$#11!174?g 54 | ?i?i?i?_!35?A?I?i?i?i?i?i?i?i?i?i?i?i$#1!305?_SiD_TiTiTiTiTiTiT$#3!304?_OiTA?gTiTiTiTiTiTG$#8!463?$-#15!170~N!4?T?T?T?T?T?S?O 55 | !37?@?P?@?@?@?@?@?@?@!82?!137~$#2!197?!3_!2o!2w{!3}!8~^!2~}{wo_!20?w}!40~!2}nBP{}!2^NF!2bpw{$#9!174?TiTi 56 | TiTiTiTgO_!36?@A@?Pa@a@a@a@a@a@A$#12!188?!2B!3J!2LDKMEF!2B!2@$#1!304?TiT?SiTiTiTiTiTiTi@$#3!304?iTI?_TiTiTiTiTiTiT$#11 57 | !174?i?i?i?i?i?i?g?_!39?A?a?a?a?a?a?A$#8!463?$-#15!171~!4?T?T?T?T?T?T?T?T!41?@?T?T?T?T!82?o}!137~$#2!193?E~}w?!2NF!14~r? 58 | !9~}{wo!13?]^Nf!2v!20~!2^!4~|@!6~NFB`!4?pw{!2}!5~N$#9!174?TiTiTiTiTiTiTiTiS_!38?@ADITiTiTi$#1!303?_TAOiTiTi 59 | TiTiTiTiTA$#3!303?Oi@_SiTiTiTiTiTiTI@$#11!174?i?i?i?i?i?i?i?i?g!41?I?i?i?i$#8!463?$-#15!171~{!3?T?T?D?P?T?D?T?T?T!43?@?@?@!79? 60 | _w!140~$#2!195?N~}of?@!14~o@F^!11~}w!7?oWKerzx!22~N?!3~NBWNfr!2x{!2}~B!3?_!9~N?w$#9!174?@iTiDaPgTiTIDiTiTiO!41? 61 | @A@?@$#1!303?AOiTiTiTiTiTiTID$#3!303?D?OiTiTiTiTiTiTA$#11!174?A?i?I?a?g?I?i?i?i?g!43?A$#8!463?$-#15!173~w_!2?S?T?D?O?T?T?T?T?O 62 | !76?C!49?@!140~$#2!196?!5~o@N^!13~{o_@FN^!6~F!9?EN!22~^NFB!2_rxw{}!9~F!3?w!8~!2@$#1!300?Oi?ATiTiTiTiTIDA@$#9 63 | !176?@GSiDAPgSiTiTiTiT_$#3!300?gT!2?iTiTiTiTIDA@$#11!178?i?I?_?i?i?i?i?i$#12!232?_{N?{?}w_$#8!463?$-#15!175~}w_!3?T?T?T?T?T?T? 64 | T!126?_!140~$#2!197?!5~}o!2?!15~!2}{wo`@!2B!14?BN^!10~!3vrzx|!2{!2}!15~N@?_w}!8~v_$#12!230?_{~^?}~?B!2~}w 65 | o$#9!178?@?SiTiTiTiTiTiTi$#11!178?A?g?i?i?i?i?i?i$#3!299?TIDIC!3?@?@$#1!299?ITID?C!3?@$#6;2;0;50;50!304?_O_OG?GCaTA@ 66 | G$#8!463?$-#15!176~FB!3?@?D?T?T?T?T?T!124?w{!141~$#2!197?B^!5~{_@^!4~B!14~F!20?@BFN^!25~^NFB?OWMN!4F!3B!2@$#12!229? 67 | {!3~?_!3~?!6~}{wo_$#9!181?ADITITITiTiTiT_$#6!179?O_?_!105?_?_O_OGOgC_SaPIPGDiCaPiPgSID$#11!182?A?I?I?i?i?i?i$#8!463?$-#15!176~ 68 | {!16?@?@!116?!2_o{}!144~$#2!199?BN!5~}{obfN?@^~BN!8~B!26?@BF^!8~L@F!3~^N!2FB@$#12!195?!2_!31?!4~F?!4~?N!11~ 69 | }{o_$#6!179?PATaDiCiSgSgSg?G!83?gOgSiTiTiTITiCiTaPiPgTgSiSITIDA@$#8!463?$#9!192?@?@$-#15!175~^!22?{wo!109?o}!149~$#12!191?_{}!3~ 70 | !30?}!4~!2?!4~_?!8~!2^!5~}{o$#2!202?BFN^!6~}!2{xyo`N^!4~B!32?BN~fF@!2?C!2B@$#6!178?_CaCiCgDGTiTA@!76? 71 | _O_OgSiTiTiTiPgTgSiTITIDIDADA@A@?@$#3!298?_?_OGOGSgSA$#1!299?_?_OGOGSG$#8!463?$-#15!169~!2^N!2FB!22?O^!2~w!3o!103?o}!151~ 72 | $#12!191?N!4~!11?_!19?!5~!2?!5~?B!4~HB~}o@^!6~^$#2!206?@B!2FN^!8~}{|zJ!36?B!20?!3_!3o!2w[C$#6!177?CIT 73 | ITITiTiTi!73?_OgSiTiTiTiTiTITIDIDADA@A!2?@$#3!292?_OiTaPgSiSiTiSA$#1!293?gSIDaPgSiSiTI$#8!463?$-#15!150~!3^!4N!3F!6B!2@ 74 | !5?O?T?T?T?S?S?S?S?S!14?@!100?_{!154~$#12!192?@BN^~!4}{!3[WGJFA!14?o!2?!5~!2?!5~!2?!4~y?@^~!2?!2^NFB@$#2 75 | !165?!2_!2o!2wK!42?@!2BFN!4~F!45?_!3o!2w!3{}!2{}!5~^F@$#9!173?gSiTiTiTiSiSiSgSGC$#3!290?gSaPgSiTiTiTiD$#1 76 | !289?_OIDaOiTiTiTiTA$#6!187?@?@!2?_!18?OG!45?_OgTADIDI@AD?@A@$#11!174?i?i?i?i?i?i?g?g?G$#8!463?$-#15!135~!2^!2N!2F!3B 77 | !3@!10?!2A!14?T?T?T?T?T?T?D?@!9?O!15?C?C?O!35?O?O?T?S!42?ow}!156~$#2!143?!3_!3o!4w{[!2K!3_ocscd!2lx!2z 78 | ~_!48?@!2B!39?o}{s!4o_!2`!2B!2FN!2^!3~^NB@$#12!200?@!23?Fwo!3~^F_w!5~o?!4^N!2KCB$#9!173?iTiTiTiTiTiTI@ 79 | !10?_!13?A?ACIS?O_!31?_O_OgOgSgS$#3!286?gCaPgSiTiTiTiDA@$#1!285?_OGD_SiTiTiTiTA@$#11!174?i?i?i?i?i?I?A!11?_!13?A?A?G?_!33? 80 | _?g?g?g?G$#6!186?_OgSiTID!5?CG!2?SgSgSgOgO_$#8!463?$-#15!121~!2^!3NF!3B!2@!41?@?T?T?T?T?@!13?@?S?O!17?@?C?O?O?O?S 81 | ?O?O?O?O?O?O?O?S?S?S?T?T?T?T?@!36?!2_ow{!160~$#2!127?!3_!2o!2w!3{!3}!11~!2rx!2{!8|!8~!88?o{!9~!3x!3w 82 | !4?@$#9!174?TiTiTiTiD!15?DiSG!18?@ASgO_OgOgOgOgOgOgOgOgOgOgSgSiTiTiTiTiD$#6!184?gTiTiT_?iD!2?_!4?@A!2?P_?aTiTiTiTiSg 83 | O$#11!174?i?i?i?i?I!15?I?g!19?A?g?g?_?g?g?g?g?g?g?_?g?g?g?i?i?i?i?i?I$#3!284?A@GTITiTiTIDA@$#1!286?CITITiTIDA@$#8!463?$#12!226? 84 | @$-#15!110~!2^N!2F!2B!2@!56?T?T?T?@!17?@!3?S!19?T?T?T?T?T?T?T?T?T?T?T?T?T?T?T?T?D!38?!2A!2F!2N!2^!159~$#2!115? 85 | !2_!2ow!3{!2}!47~w!85?o}!21~!2}!2{w_?GO!2o!2_$#9!174?DiTiTiT!19?D?SiO!17?PiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTi 86 | TI@$#6!183?TiTiTiTiTiTiTiS!7?@ITiTiTiTiTiTiT$#11!174?i?i?i?i!19?A?g?g!17?a?i?i?i?i?i?i?i?i?i?i?i?i?i?i?i?i?A$#8!463?$-#15!101~ 87 | !2^N!2FB!2@!66?T?T?T?T?O!15?G?S?T?S!15?S?T?T?T?T?T?T?T?T?T?T?T?T?T?T?D?@!49?@BF!2N^!152~$#2!106?_!2ow!2{!2}!59~ 88 | _!79?_ow}!31~}w`BN!3~!2}{!2wo!2_$#9!175?iTiTiTiS_!13?_O!2?iTiTG!15?gTiTiTiTiTiTiTiTiTiTiTiTiTiTiTIDA$#6!183?@ADI 89 | TiTiTiTiTI@!9?@iTiTiTiTiTI@$#11!176?i?i?i?g!15?_!3?i?i!15?_?i?i?i?i?i?i?i?i?i?i?i?i?i?i?i?A$#8!463?$-#15!94~^NF!2B@!77? 90 | T?T?T?T?S?O!3?O?O?S?T!3?T?T?@!9?O?S?T?T?T?T?T?T?T?T?T?T?T?T?T?T?D?@!59?!2@BFN^!146~$#2!97?_!2ow!2{}!70~{!73?_o 91 | w{}!38~f_p!11~}{wo!2_$#9!176?DiTiTiTiTiSgO_?_OgSiTiT!2?iTiT!8?_O_OgSiTiTiTiTiTiTiTiTiTiTiTiTiTIDA@$#11!176?A?i?i?i?i?g? 92 | g?_?_?g?i?i!3?i?I!9?_?g?i?i?i?i?i?i?i?i?i?i?i?i?i?i?I?A$#6!189?@A@A@!12?_TITIDIDA@$#8!463?$-#15!80~!8^!2NFB@!86?D?T?T 93 | ?T?T?T?T?T?T?T!3?T?P?S?S?S?T?T?T?T?T?T?T?T?T?T?T?T?T?T?T?D?@!71?@BFN!142~$#2!90?!2_ow{}!27~!2B!51~{o!65?_ow!2}!63~ 94 | }{w_$#9!178?@ITiTiTiTiTiTiTiTiTiT_?iTiOgSiSiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTIDA@$#11!178?A?i?i?i?i?i?i?i?i?i?i!3?i?g?g?i?i?i?i?i 95 | ?i?i?i?i?i?i?i?i?i?i?i?I?A$#8!463?$-#15!71~^!2NFB!2@!103?@?D?T?T?T?T?T?T?T?T?@?T?T?T?T?T?T?T?T?T?T?T?T?T?T?D?D?@!84?@BF^!137~ 96 | $#2!86?K!2}!31~Nfp{!35~^!13~N^!4~{wo_!52?!2_ow!2{}!64~n!7N!2^{wo_$#9!181?ADITiTiTiTiTiTiTiTiO?TiTiTiTiTiTiTi 97 | TiTiTiTiTiTiTiTIDA@A@$#11!182?A?I?i?i?i?i?i?i?i?_?I?i?i?i?i?i?i?i?i?i?i?i?i?i?I?I?A$#12!77?o!2w!4{_$#8!463?$-#15!67~^B@!117? 98 | @?D?T?T?T?T?T?O?D?T?T?T?T?T?T?T?D?D?D?@?@!97?BFN^!132~$#2!87?@BN^!16~^!3~^Fp|^nvx{}!28~!2^!2NVvzx\[]Nnvrw[^!2N!2F 99 | BHKE!3F!3B@`_!3o!2_!36?!2_!3o!2_M]!80~!3}{!2x!2qeGO$#9!186?@ADIDITiTiTiTiTg?ATiTiTiTiTiTiTITIDIDA@A@$#12!71? 100 | o{!2}w_BN^!5~}wo$#11!188?A?I?I?i?i?i?i!3?i?i?i?i?i?i?I?I?I?A?A$#8!463?$-#15!65~NFA!127?@?@?@?@!3?@?@?@?@!118?@!3B!2F 101 | !3N!4FN^!116~$#2!91?@BF!2N^!7~Nfp[Nfpoz|}!26~!2zx!2|!25{!7}~^!2N~^NF@JN!2FA!31?@FJF!2N^!3~}{wq_pbfN!2^ 102 | !76~H!2RfMK[wo_O_$#12!68?_?F^!6~}wobFN^!4~}{wo_!245?!2_$#1!189?_OgSgSgSgSiSiSiSgSiSiSiSiSiSiSiSiSgO_?_$#9!196?@?@?@!3? 103 | @A@?@?@$#3!196?_?_O_O_O_OgOgO!4?gOgOgO_$#8!463?$#11!204?A?A$-#15!63~B!117?!2_!3o!2w!2{}!2{!2w!3o!2_!9?_!25? 104 | !5_!107?!2@!2B!2F!2N!106~$#2!98?@BFJ\}~|{}!27~!3^!2N!2nf!2vr!2z!23~^!2NF!2B@!55?!2@!3BF!3N!2] 105 | [y|zv~vn!2N!2^!66~}{xzQC!2@B?@$#12!66?w!3~{wpbFN!2^!5~}!2{!2xrbf!2N!2^}{wo_!230?!3owWKC_p!2~^NE?!3_ 106 | o_$#1!186?CA?A@A@ADIDITITiTiTiTiTITiTiTiTiTiTiTiTiTiTiSgSgSGS$#3!195?@A@ADADIDIDIDA!4?TITiTiTiTiTiSiSgOgOG$#8!463?$-#15!63~o!64? 107 | !2_o!3w!6o!3_!23?!3_!3o!2w!2{!2}!27~!3}!3~!2}{!14w!2{!3}!9~!3}{w!3o!2_!104?@B!103~$#2!105? 108 | @B!2FN^!12~!3^!3FB!2@!2?!3A!6F!3N!11^!4N!3F!2B!3@!79?@!3B@BFEM!2]{!4|x!4z!51~^NF@$#12!66?B 109 | ^!7~!2}{wxp!2r!2fF!2N!3^~!3}{w!2x!2r!2eKGWO!2_!214?_o{}~^NFbpw{}~^NBpw}!6~^NF_o{$#1!206?@!5?@ADADADA 110 | DADADADA@A@?@$#3!217?@?@?@?@$#8!463?$-#15!65~E!53?!2_!2ow!2{}!18~!9}!5{!4}!91~}!3{!3w!2o!2_!93?!2@!3B 111 | !4F!4N!6^!84~$#2!69?o!2_!41?!2@!3B!2@!141?!2@!2B!3F!2N!3^!13~n!2^!4~nN^!21~NB@!37?!4_$#12!69? 112 | B!2F!2N!3^!11~!3}!6{!3xz!2rvf!2e!2cghg!207?_w{~^NBpw{}!4~^F`w}!6~NF`o{}!2~^B$#8!463?$-#15!63~N!49?!2_ 113 | o{!2}!150~!2}!4{!3w!3o!4_!98?!3@!3B!2F!2N!2^!66~$#2!67?w!6~!2}!4{!4w!7o!15_o!3O!2G!165? 114 | !3@!3B!5F!2MKXZRFCKJRFN^~n!2N!2^!7~NB!37?o{!10~!7}!7{!4w!3o!2_$#12!79?!5@!4B!18F!3B!3@!206? 115 | w~^F`w}!7~N@o}!6~NBo{}!4~NB$#8!463?$-#15!62~F!47?]!178~!4}!4{!4w!3o!3_!89?@BFN^!60~$#2!66?}!17~F!2~^Nfx!2{ 116 | !13~|w!196?@A!4?HXZRB!35?_!2w!3x!2rv!2fN!2^!3~N^!22~!2}!2{wo_$#12!317?]Bo{!9~F?w!6~^B_{!5~^B$#8!463? 117 | $-#15!62~!49?N!198~!3}!89?@BFN^!54~$#2!65?!18~V?rx}^Nbx|!15~o!238?_w}!13~!2}!2|zu{xrfN^!4~!2N^!17~}{wo_$#12!315? 118 | ABw}!9~F?w!6~^Bo}!4~NB@$#8!463?$-#15!61~!23?G!27?N!200~{w!2o_!89?@BN^!50~$#2!64?!18~@!2?frw]^!19~!234?_o!2{p 119 | bFN^!22~}|zvN~!2}{xrN^!18~}wo$#12!317?B!2F!5NFB`w!6~N@o}!2~^F@$#8!463?$-#15!61~!52?!206~!6}{o!4_!80?@BF^!46~$#2 120 | !64?!17~N@?]Fp{}!21~!232?MN^!5~}!2{xrf!2N^!20~}!6~}xf^!19~}wo_$#12!327?@!4BFBo{~NFB@$#8!463?$-#15!61~!52?!219~}!5{ 121 | }!3~!2}{!2wo!2_!66?BN^!42~$#2!64?!16~o}!2?o!11~^B!12~!237?@!2B!2FN!2^!3~}{!2x!2r!2f!2N!2^!20~ 122 | {r!13~!3rvfnN^]{o_$#8!463?$-#15!61~!50?o!239~}!2{w!2_!62?@F^!39~$#2!64?B!2~^N!12~B!2?N~^F!2~^b!3~N_}!9~^N 123 | @!245?!2@BFN^!4~^!3~!4}!3|!3zr!2v~!2n!23~!4fnM[\zq_$#8!463?$-#15!61~}!45?_o{!248~}w!2o_!4?_!54?!2@!2B 124 | !2F!3N!2^!28~$#2!63?{?Fp[Nn!11~w~w?@W~^bw!2~^b{~Nb!4~^FB@!254?@BFN[PF^{xfN!2~rF^!3~N!33~}][\zqc_$#8!463?$-#15!61~ 125 | B!41?_o{}!256~}!2{wo!2_!5?O!57?@BN^!24~$#2!63?`?B@c}!16~{?`CN^nz}^bx}^NB@!268?B!2FKHBFM[rfM[pbfN^!2~!2^!25~!3} 126 | {xzrekPbM]!2{o$#8!463?$-#15!61~!38?ow{!269~!2}!2{!2w!3o!7_a!2op!5o!42?BF!2N!2^!17~$#2!62?wG?w}!18~!2} 127 | ~rw{}pOEF@!283?@!3?!2@!8?@B!2FN^!27~}{y{z!2~w_$#8!463?$-#15!61~!34?o{}!300~}{wo_!44?!3@B!2FN^!6~$#2!62?@w~z 128 | !17~Nf!7~!309?!2@BF^~fN^!30~!4}!2{!3w!2o_$#8!463?$-#15!60~N!32?o}!309~}{wo_!46?@!5~$#2!62?}!18~N?}!7~@!315? 129 | @BEHRf^!3~!3Frx^!2Nnf!25~M$#8!463?$-#15!59~F!32?}!317~}{w!2o_!39?o!5~$#2!61?!20~S?!7~F!324?@!4?CEBJHL[}!19~ 130 | !2?^N@$#8!463?$-#15!57~N!33?}!322~F@!38?}!6~$#2!58?_}!22~?F!5~!327?_ox!2{}!26~{o$#8!463?$-#15!55~^@!34?!319~^FB!41? 131 | @BN^!3~$#2!57?{!18~^!5~w?N!4~!325?W!4^!21~!2^!9~{wo$#8!463?$-#15!51~^NFB!36?BN!307~!2^!2N!2F!2B@!49?b!2~ 132 | $#2!53?_ow}!16~Nbw{!5~B_!5~!317?!3_owx!2w!2xzre{|!14~|c?!2A?GN!12~}$#8!463?$-#15!43~^!2NFB!2@!44?BN^!301~@ 133 | !27?o!2w!25?_ow{!3~$#2!46?_ow!2{}!20~p_F!2N!2^Nbo{!3~^N@o!2{o_!307?{!3}!12~^N^!3~^NF!2B!2@B^!3~`? 134 | [}!12~^NFB@$#8!463?$-#15!15~!2^!5N!9^!2N!2F!5B!2@!55?@N!299~o_!19?!3_!2ow!3~@!22?_w}!7~$#2!32?!2_!4o 135 | !3_gGYque{|!28~!2}!7~!2}!6~}w!305?F!8N^!2N!2FB@GC!2?!2@!8?!2G!2N^!3W!3B!2R!5B@$#8!463?$-#15!11~ 136 | ^F@!85?p!301~!3}{}{!9}!13~!2}!3{!11w!3{!2}!11~$#2!14?w!2}!7~!2}!2{!2w}{xz!9~}!52~N$#8!463?$-#15!5~ 137 | F!2B@!83?_!2o!2w}!363~$#2!7?!2w!2{o_?F!2N^!4~!5^!4N!15~n!2N^@B!17^!18~!2^!2NFB!2@$#8!463?$-#15!4~ 138 | wo!12_!2o!4w!3o!19_!2o!2w{!11}~!3}!2{w!4{!3w!11{!3}!371~$#2!6?@!2F!5N!2MEC!12?!2@B!2F!8N 139 | !2FB$#8!463?$-#15!461b$#8!463?$-\ -------------------------------------------------------------------------------- /testfiles/leonardo_clean.six: -------------------------------------------------------------------------------- 1 | "1;2;461;464 2 | #15;2;100;100;100!247~!2^!212~$#8;2;75;75;75!463?$-#15!241~^NFb@o{!213~$#8!463?$-#15!236~^NFB@!3?o}!215~$#8!239?ow{]B 3 | !219?$-#15!231~^NFB!7?w!218~$#8!234?_w{}!3~B!221?$-#15!226~^NFB!10?_}!219~$#8!228?_ow{}!6~F!223?$-#15!222~^FB@!13?{!221~$#8!224? 4 | _w{}!9~F!225?$-#15!218~NFB!16?_!223~$#8!219?_ow{!12~^@!226?$-#15!213~^NB@!19?_!224~$#8!215?_o{}!15~^!228?$-#15!209~^NB@!23?!225~ 5 | $#8!211?_o{}!19~!229?$-#15!205~^NB@!26?w!225~$#8!207?_o{}!22~@!229?$-#15!201~^NB@!25?_ow!2{!226~$#8!203?_o{}!21~^NFB!231?$-#15 6 | !198~NB@!24?!2o!2{!232~$#8!200?o{}!20~^NFB!236?$-#15!194~^F@!22?_ow{}!237~$#8!196?_w}!19~NFB@!241?$-#15!191~NB!21?_ow}!243~ 7 | $#8!193?o{!17~^NFB@!246?$-#15!178~^!3N!7FB!19?_ow{}!29~!9^!106~!2^!2NFbrz!95~$#8!190?ow}!14~^NFB@!251?$-#15!169~^N!2F 8 | B!2@!29?w{}!19~^!2N!2F!2B!3@!19?!4@!2B!2FN^!78~!2^!2N!3F!2B!2@!6?_w!98~$#8!187?ow}!12~^NB@!141? 9 | !3_!2o!3w!2{!2}^F@!101?$#2;2;0;50;0!171?!2_ow!2{!2}!3~!3^NB@!42?_!3o!4w!7{!5}!3{!4w!3o 10 | !2_$-#15!164~^FB@!38?@F^!10~^!4NFB!42?@FN!12~!2^!5N!4^!12~!4^!20~!2^!3N!2F!2B!3@!16?_o}!100~$#2!166? 11 | _ow}!5~NFB!2@!46?w}!35~!2}{wo$#8!185?w}!10~NFB@!132?!3_!2o!3w!2{!2}!12~N@!104?$#12;2;0;0;100!199?ow{ 12 | !2}{o!15?!3_$#3;2;50;50;0!176?_OgSiSI$#1;2;50;0;0!177?_OgSiCA$-#15!161~NB!46?!2B!2FN^~V!2B!53?B^!2~ 13 | ^N!2FB@!22?ow{}!3{!2xz!2~!2^!2N!3F!2B!3@!28?o{!103~$#2!163?o{}^Nfrx{M@!23?_!3o!5w!3o!2_!17?!4@ 14 | !3B!3F!6N!6^!11~!8^S$#8!184?!8~NFB@!123?!3_!2o!3w!2{!2}!23~^F!107?$#12!196?KMF!3B!4@!14?G^!2~fec 15 | KG!2W!5o!3_!40?_o!2w!2{!6}!6{w!3{KC$#3!174?gSiTiTiT$#1!173?_OiTiTiTi$-#15!158~^B!55?@!11?W!2{wo_!41?!2@!23? 16 | Ow{]^!2N!2F!3B!2@!41?w!106~$#12!218?M!2^!3}~b!2@!4?@FN!2^!8~!8}!3]!3M!3]!10}{!3?W{wp!3@|!2{ 17 | !3|!3xprbB!3@$#8!183?A!2~^NFB!117?_!2o!3w!2{!2}!35~F@!109?$#2!161?o}B?o}!2~!2^@!24?w_BN!12~}][K$#3!172? 18 | _TiTiTiTiT!6?_OgSiS$#1!172?OiTiTiTiTi!7?_OgTi$-#15!158~!8?G!62?!5@!14?G!2[!2{!2]MA@!21?!2_!70?_{!108~$#8!184? 19 | @!107?!3_!2o!3w!2{!2}!44~!2^B!112?$#12!222?!2@!2B!9?!3CF!4N!3^!3@!9?{!3}!5~r!2zZ!2XKc!2? 20 | HNFB!2?q!2owxZ!2^!2N!2F!2B@$#2!160?{!2~o_!30?{~^!2?w!5~^NFbxw{}!4~^][!2KG!2g!13_$#3!172?ATiTiTiTiTgO 21 | _OiTiTiTi@$#1!172?TiTiTiTiTiO_OgSiTiTiTA$-#15!158~_!117?WKN!2FA!54?!3_!2o!3w!3{!2}{}!110~$#2!160?@!4~@?!4}w!21? 22 | _bxw{}!4~NB?w}!6~!2|{!3}!24~!4}!4{w!2o_!3?!14o_$#8!278?!3_!2o!2w!3{!2}!42~!3^!2N!3F!3B!3@ 23 | !117?$#12!254?AE!9FDC!4E!2FB!10?!2@$#3!173?@ITiTiTiTiTiTiTiTID$#1!173?ATiTiTiTiTiTiTiTiTA$-#15!159~}!2wo_!156?!3_!2o 24 | !3w!3{!2}!128~$#2!162?@BF!3?BN^!2~{wo!2_!8?_!2ow{}!11~!3?!8~!2^!41~}!2{!3^!2N!2F!3B@$#8!264? 25 | !2_!3o!3w{!3}!40~!3^!2N!3F!3B!2@!134?$#3!176?A@ADIDIDIDADA$#1!176?@ADADIDIDIDA@$-#15!164~{wo!7_!137?GW!2{ 26 | !2}!144~$#2!174?!2@GW{}~Nn^N!18~}o?BN^!2~r!5_!2`rv!2f!2n!3N!6^!11~!3^!3N!2F!2B!2@$#8!250?!2_!3o 27 | !2w!2{!3}!39~!2^!3N!3F!2B!3@!149?$-#15!174~}wo_!116?GW[!5{!3w!3o_!4?@N!146~$#8!234?!3_!3o!2w!3{ 28 | !2}!36~^!6N!2F!3B!3@!165?$#2!178?@FN?N]?N!7~^!2Nn!2N^!4~!2^]KG?P!2bF!3N!2^!11~!3]MK!2C!6@$#12 29 | !302?!4@!2B!2FM[o$-#15!179~]!2[{!4W!2G!109?@B!2N!8~}{wo?N!145~$#8!219?!3_!2o!3w!3{!2}!33~!4^!3N!2F 30 | !4B!3@!182?$#2!184?@?@!3B@`{}]!4^]!2{xr!8?B~!3]MK?@?!6@!42?!3_!3o!3w!3{}!3mG$#1!205?GS!2?iS!77?@ 31 | ITiTiTiSgO_$#3!205?Si!2?Sg!78?@ITiTiSgO_$-#15!174~NB@!126?@!3^!7~!2}!145~$#8!211?w!2{!3}!27~!4^!5N!2F!4B!3@ 32 | !201?$#2!177?_w!2KC!2?!2CKM?_arx{!2}Aw{}!4~!46?!4_!2?!2o!3w!3{!3}fFN^!12~!3n!2N]$#1!206?T_?a@!80? 33 | ITiTiTiTiTI$#3!206?i!2?P?@!80?ATiTiTiSG$-#15!170~NB@!62?O?O!70?@B!2FN!148~$#2!172?ow!2{}!2~!3{o@yBw?^!7~EF!6~_!34? 34 | GK!2[{!3}!8~}obN!10~}]!2N!2Ffv!2zx|!7~!2}{w!2o_$#8!211?@!14~!3^!3N!4F!3B!3@!221?$#1!206?TiOAO!81? 35 | @A?IPiPgSiTiTiSgO_$#3!206?IT_@_!82?@?@!2?GOgSiSiSgO_$-#15!169~@!47?O?O?O?S?S?S?T?T?T?T?T?T?S?S?O!68?@FN!144~$#2!171?w!5~N 36 | !2FfN^}{}~}oF!6~?~^NF!4~o!38?@B!2F^!13~^NFBbpow{!2}!23~}{wo$#9;2;100;0;0!217?_OgOgSgSiSiTiTiTiTiTiTiTiSgOg 37 | O$#1!207?iTg@gO!85?@ADITiTiTiTiTiSgO$#3!207?TiOAO_?_!86?@ITiTiTiTiSgO$#11;2;100;100;0!218?_?g?g?i?i?i?i?i?i?i?i?i?i?g?_$#8 38 | !213?B!3F!2B!4@!240?$-#15!148~^NF!3B!2@!2B!2FN!6FN@!48?@?T?T?T?T?T?T?T?T?T?T?T?T?T?T?T?O!68?@F^!140~$#2!170?o!6~ 39 | !2o!2~o?!2~!2^!2~}WF!4~o?E!2o!5~w!43?@BF^!3~^NB@?_oyz\!2Nnfv!29~}wo$#9!218?TiTiTiTiTiTiTiTiTiTiTiTiTiTiT 40 | iSgO$#1!151?_OgOGOG!7?_!42?DiOATiTi!88?DITiTiTiTiTiTiO_$#3!152?_OGOG!7?_!43?AT_@ITiS!89?@ITiTiTiTiSg$#11!218?i?i?i?i?i? 41 | i?i?i?i?i?i?i?i?i?i?i?_$#8!463?$-#15!145~^B!70?S?T?T?T?T?T?T?T?T?T?T?T?T?T?T?T?T?T?O!5?GC!60?B!139~$#2!170?!11~{_bpo?p{]Nv!3~ 42 | N`}!2~F^!6~w!46?@!4?_rz\KMev!36~}w$#9!217?iTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiSG$#1!147?_!2?SiT!2?iT!2?iT!2?iTi 43 | O_!42?@I!2?DA!91?ATiTiTiTiTiTiS$#3!147?O!2?iTi!2?Si!2?Ti!2?TiTg!44?C?@I@!92?ATiTiTiTiTi$#11!218?i?i?i?i?i?i?i?i?i 44 | ?i?i?i?i?i?i?i?i?i$#8!463?$-#15!145~o_!16?!4_o_!42?C?S?S?T?T?T?T?T?T?T?T?T?T?T?T?T?T?T?T?T?D!4?O!65?_!138~$#2!170?F!5~^ 45 | N~^!6~w@?}!5~?Fr!2B}ob!3~!2^F!48?KerX[Km!2fvr!3zx!2|{!32~$#9!211?GSgSiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTI@$#1 46 | !147?AO?DITiO_@!2?ADAC?@ADA!140?CiTiTiTiTiTID$#3!147?@!2?ATiSgOaC?@AD?@ADA@!141?DiTiTiTiTID$#11!212?G?g?i?i?i?i?i?i?i?i?i?i 47 | ?i?i?i?i?i?i?i?i$#8!463?$-#15!148~}!10{}!9~{o_!43?T?T?T?T?T?T?T?T?T?T?T?T?T?T?T?T?T?T?O!5?@!62?C]!139~$#2!172?@BFB?^?cp@N 48 | !5~{xr`_OR!2NFEAB!3@!51?o!2w!2{}!46~$#9!214?@ATiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTG$#11!216?i?i?i?i?i?i?i?i?i?i?i?i? 49 | i?i?i?i?i?i$#12!196?!3_!2o!2wW[KMebrxw_$#1!309?gPA@aPaPgO_$#3!309?O_@a@aPgO_$#8!463?$-#15!172~^A!3?O!39?@?T?T?T?T?T?T?T? 50 | T?T?T?T?T?T?T?D?@!72?@N!137~$#2!180?@A!2?@!2B!3@!59?ow{!2}!38~^frN!9~N$#12!184?_!3o!2w!2[K!2mfvrzX\Kkm}!8~ 51 | }[$#9!176?O_?_!36?@aTiTiTiTiTiTiTiTiTiTiTiTiTiTiDA@$#11!176?_?_!39?i?i?i?i?i?i?i?i?i?i?i?i?i?i?I?A$#1!308?OI?iTiTiTiTiTi$#3!308? 52 | gD?SiTiTiTiTiO$#8!463?$-#15!171~B!3?T?T?T?O!35?@?D?T?T?T?T?T?T?T?T?T?T?T?D!80?!137~$#2!209?!2_!2ow!2{o_!28?o{!39~^Nf 53 | r|Mfp{!5~^FB$#12!184?BD!2L!2mev!2rz!2x|!2[]nfvrx\[N!2FB@$#9!174?OiTiTiT_!36?@ADITiTiTiTiTiTiTiTiTiTiTI@$#11!174?g 54 | ?i?i?i?_!35?A?I?i?i?i?i?i?i?i?i?i?i?i$#1!305?_SiD_TiTiTiTiTiTiT$#3!304?_OiTA?gTiTiTiTiTiTG$#8!463?$-#15!170~N!4?T?T?T?T?T?S?O 55 | !37?@?P?@?@?@?@?@?@?@!82?!137~$#2!197?!3_!2o!2w{!3}!8~^!2~}{wo_!20?w}!40~!2}nBP{}!2^NF!2bpw{$#9!174?TiTi 56 | TiTiTiTgO_!36?@A@?Pa@a@a@a@a@a@A$#12!188?!2B!3J!2LDKMEF!2B!2@$#1!304?TiT?SiTiTiTiTiTiTi@$#3!304?iTI?_TiTiTiTiTiTiT$#11 57 | !174?i?i?i?i?i?i?g?_!39?A?a?a?a?a?a?A$#8!463?$-#15!171~!4?T?T?T?T?T?T?T?T!41?@?T?T?T?T!82?o}!137~$#2!193?E~}w?!2NF!14~r? 58 | !9~}{wo!13?]^Nf!2v!20~!2^!4~|@!6~NFB`!4?pw{!2}!5~N$#9!174?TiTiTiTiTiTiTiTiS_!38?@ADITiTiTi$#1!303?_TAOiTiTi 59 | TiTiTiTiTA$#3!303?Oi@_SiTiTiTiTiTiTI@$#11!174?i?i?i?i?i?i?i?i?g!41?I?i?i?i$#8!463?$-#15!171~{!3?T?T?D?P?T?D?T?T?T!43?@?@?@!79? 60 | _w!140~$#2!195?N~}of?@!14~o@F^!11~}w!7?oWKerzx!22~N?!3~NBWNfr!2x{!2}~B!3?_!9~N?w$#9!174?@iTiDaPgTiTIDiTiTiO!41? 61 | @A@?@$#1!303?AOiTiTiTiTiTiTID$#3!303?D?OiTiTiTiTiTiTA$#11!174?A?i?I?a?g?I?i?i?i?g!43?A$#8!463?$-#15!173~w_!2?S?T?D?O?T?T?T?T?O 62 | !76?C!49?@!140~$#2!196?!5~o@N^!13~{o_@FN^!6~F!9?EN!22~^NFB!2_rxw{}!9~F!3?w!8~!2@$#1!300?Oi?ATiTiTiTiTIDA@$#9 63 | !176?@GSiDAPgSiTiTiTiT_$#3!300?gT!2?iTiTiTiTIDA@$#11!178?i?I?_?i?i?i?i?i$#12!232?_{N?{?}w_$#8!463?$-#15!175~}w_!3?T?T?T?T?T?T? 64 | T!126?_!140~$#2!197?!5~}o!2?!15~!2}{wo`@!2B!14?BN^!10~!3vrzx|!2{!2}!15~N@?_w}!8~v_$#12!230?_{~^?}~?B!2~}w 65 | o$#9!178?@?SiTiTiTiTiTiTi$#11!178?A?g?i?i?i?i?i?i$#3!299?TIDIC!3?@?@$#1!299?ITID?C!3?@$#6;2;0;50;50!304?_O_OG?GCaTA@ 66 | G$#8!463?$-#15!176~FB!3?@?D?T?T?T?T?T!124?w{!141~$#2!197?B^!5~{_@^!4~B!14~F!20?@BFN^!25~^NFB?OWMN!4F!3B!2@$#12!229? 67 | {!3~?_!3~?!6~}{wo_$#9!181?ADITITITiTiTiT_$#6!179?O_?_!105?_?_O_OGOgC_SaPIPGDiCaPiPgSID$#11!182?A?I?I?i?i?i?i$#8!463?$-#15!176~ 68 | {!16?@?@!116?!2_o{}!144~$#2!199?BN!5~}{obfN?@^~BN!8~B!26?@BF^!8~L@F!3~^N!2FB@$#12!195?!2_!31?!4~F?!4~?N!11~ 69 | }{o_$#6!179?PATaDiCiSgSgSg?G!83?gOgSiTiTiTITiCiTaPiPgTgSiSITIDA@$#8!463?$#9!192?@?@$-#15!175~^!22?{wo!109?o}!149~$#12!191?_{}!3~ 70 | !30?}!4~!2?!4~_?!8~!2^!5~}{o$#2!202?BFN^!6~}!2{xyo`N^!4~B!32?BN~fF@!2?C!2B@$#6!178?_CaCiCgDGTiTA@!76? 71 | _O_OgSiTiTiTiPgTgSiTITIDIDADA@A@?@$#3!298?_?_OGOGSgSA$#1!299?_?_OGOGSG$#8!463?$-#15!169~!2^N!2FB!22?O^!2~w!3o!103?o}!151~ 72 | $#12!191?N!4~!11?_!19?!5~!2?!5~?B!4~HB~}o@^!6~^$#2!206?@B!2FN^!8~}{|zJ!36?B!20?!3_!3o!2w[C$#6!177?CIT 73 | ITITiTiTi!73?_OgSiTiTiTiTiTITIDIDADA@A!2?@$#3!292?_OiTaPgSiSiTiSA$#1!293?gSIDaPgSiSiTI$#8!463?$-#15!150~!3^!4N!3F!6B!2@ 74 | !5?O?T?T?T?S?S?S?S?S!14?@!100?_{!154~$#12!192?@BN^~!4}{!3[WGJFA!14?o!2?!5~!2?!5~!2?!4~y?@^~!2?!2^NFB@$#2 75 | !165?!2_!2o!2wK!42?@!2BFN!4~F!45?_!3o!2w!3{}!2{}!5~^F@$#9!173?gSiTiTiTiSiSiSgSGC$#3!290?gSaPgSiTiTiTiD$#1 76 | !289?_OIDaOiTiTiTiTA$#6!187?@?@!2?_!18?OG!45?_OgTADIDI@AD?@A@$#11!174?i?i?i?i?i?i?g?g?G$#8!463?$-#15!135~!2^!2N!2F!3B 77 | !3@!10?!2A!14?T?T?T?T?T?T?D?@!9?O!15?C?C?O!35?O?O?T?S!42?ow}!156~$#2!143?!3_!3o!4w{[!2K!3_ocscd!2lx!2z 78 | ~_!48?@!2B!39?o}{s!4o_!2`!2B!2FN!2^!3~^NB@$#12!200?@!23?Fwo!3~^F_w!5~o?!4^N!2KCB$#9!173?iTiTiTiTiTiTI@ 79 | !10?_!13?A?ACIS?O_!31?_O_OgOgSgS$#3!286?gCaPgSiTiTiTiDA@$#1!285?_OGD_SiTiTiTiTA@$#11!174?i?i?i?i?i?I?A!11?_!13?A?A?G?_!33? 80 | _?g?g?g?G$#6!186?_OgSiTID!5?CG!2?SgSgSgOgO_$#8!463?$-#15!121~!2^!3NF!3B!2@!41?@?T?T?T?T?@!13?@?S?O!17?@?C?O?O?O?S 81 | ?O?O?O?O?O?O?O?S?S?S?T?T?T?T?@!36?!2_ow{!160~$#2!127?!3_!2o!2w!3{!3}!11~!2rx!2{!8|!8~!88?o{!9~!3x!3w 82 | !4?@$#9!174?TiTiTiTiD!15?DiSG!18?@ASgO_OgOgOgOgOgOgOgOgOgOgSgSiTiTiTiTiD$#6!184?gTiTiT_?iD!2?_!4?@A!2?P_?aTiTiTiTiSg 83 | O$#11!174?i?i?i?i?I!15?I?g!19?A?g?g?_?g?g?g?g?g?g?_?g?g?g?i?i?i?i?i?I$#3!284?A@GTITiTiTIDA@$#1!286?CITITiTIDA@$#8!463?$#12!226? 84 | @$-#15!110~!2^N!2F!2B!2@!56?T?T?T?@!17?@!3?S!19?T?T?T?T?T?T?T?T?T?T?T?T?T?T?T?T?D!38?!2A!2F!2N!2^!159~$#2!115? 85 | !2_!2ow!3{!2}!47~w!85?o}!21~!2}!2{w_?GO!2o!2_$#9!174?DiTiTiT!19?D?SiO!17?PiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTi 86 | TI@$#6!183?TiTiTiTiTiTiTiS!7?@ITiTiTiTiTiTiT$#11!174?i?i?i?i!19?A?g?g!17?a?i?i?i?i?i?i?i?i?i?i?i?i?i?i?i?i?A$#8!463?$-#15!101~ 87 | !2^N!2FB!2@!66?T?T?T?T?O!15?G?S?T?S!15?S?T?T?T?T?T?T?T?T?T?T?T?T?T?T?D?@!49?@BF!2N^!152~$#2!106?_!2ow!2{!2}!59~ 88 | _!79?_ow}!31~}w`BN!3~!2}{!2wo!2_$#9!175?iTiTiTiS_!13?_O!2?iTiTG!15?gTiTiTiTiTiTiTiTiTiTiTiTiTiTiTIDA$#6!183?@ADI 89 | TiTiTiTiTI@!9?@iTiTiTiTiTI@$#11!176?i?i?i?g!15?_!3?i?i!15?_?i?i?i?i?i?i?i?i?i?i?i?i?i?i?i?A$#8!463?$-#15!94~^NF!2B@!77? 90 | T?T?T?T?S?O!3?O?O?S?T!3?T?T?@!9?O?S?T?T?T?T?T?T?T?T?T?T?T?T?T?T?D?@!59?!2@BFN^!146~$#2!97?_!2ow!2{}!70~{!73?_o 91 | w{}!38~f_p!11~}{wo!2_$#9!176?DiTiTiTiTiSgO_?_OgSiTiT!2?iTiT!8?_O_OgSiTiTiTiTiTiTiTiTiTiTiTiTiTIDA@$#11!176?A?i?i?i?i?g? 92 | g?_?_?g?i?i!3?i?I!9?_?g?i?i?i?i?i?i?i?i?i?i?i?i?i?i?I?A$#6!189?@A@A@!12?_TITIDIDA@$#8!463?$-#15!80~!8^!2NFB@!86?D?T?T 93 | ?T?T?T?T?T?T?T!3?T?P?S?S?S?T?T?T?T?T?T?T?T?T?T?T?T?T?T?T?D?@!71?@BFN!142~$#2!90?!2_ow{}!27~!2B!51~{o!65?_ow!2}!63~ 94 | }{w_$#9!178?@ITiTiTiTiTiTiTiTiTiT_?iTiOgSiSiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTIDA@$#11!178?A?i?i?i?i?i?i?i?i?i?i!3?i?g?g?i?i?i?i?i 95 | ?i?i?i?i?i?i?i?i?i?i?i?I?A$#8!463?$-#15!71~^!2NFB!2@!103?@?D?T?T?T?T?T?T?T?T?@?T?T?T?T?T?T?T?T?T?T?T?T?T?T?D?D?@!84?@BF^!137~ 96 | $#2!86?K!2}!31~Nfp{!35~^!13~N^!4~{wo_!52?!2_ow!2{}!64~n!7N!2^{wo_$#9!181?ADITiTiTiTiTiTiTiTiO?TiTiTiTiTiTiTi 97 | TiTiTiTiTiTiTiTIDA@A@$#11!182?A?I?i?i?i?i?i?i?i?_?I?i?i?i?i?i?i?i?i?i?i?i?i?i?I?I?A$#12!77?o!2w!4{_$#8!463?$-#15!67~^B@!117? 98 | @?D?T?T?T?T?T?O?D?T?T?T?T?T?T?T?D?D?D?@?@!97?BFN^!132~$#2!87?@BN^!16~^!3~^Fp|^nvx{}!28~!2^!2NVvzx\[]Nnvrw[^!2N!2F 99 | BHKE!3F!3B@`_!3o!2_!36?!2_!3o!2_M]!80~!3}{!2x!2qeGO$#9!186?@ADIDITiTiTiTiTg?ATiTiTiTiTiTiTITIDIDA@A@$#12!71? 100 | o{!2}w_BN^!5~}wo$#11!188?A?I?I?i?i?i?i!3?i?i?i?i?i?i?I?I?I?A?A$#8!463?$-#15!65~NFA!127?@?@?@?@!3?@?@?@?@!118?@!3B!2F 101 | !3N!4FN^!116~$#2!91?@BF!2N^!7~Nfp[Nfpoz|}!26~!2zx!2|!25{!7}~^!2N~^NF@JN!2FA!31?@FJF!2N^!3~}{wq_pbfN!2^ 102 | !76~H!2RfMK[wo_O_$#12!68?_?F^!6~}wobFN^!4~}{wo_!245?!2_$#1!189?_OgSgSgSgSiSiSiSgSiSiSiSiSiSiSiSiSgO_?_$#9!196?@?@?@!3? 103 | @A@?@?@$#3!196?_?_O_O_O_OgOgO!4?gOgOgO_$#8!463?$#11!204?A?A$-#15!63~B!117?!2_!3o!2w!2{}!2{!2w!3o!2_!9?_!25? 104 | !5_!107?!2@!2B!2F!2N!106~$#2!98?@BFJ\}~|{}!27~!3^!2N!2nf!2vr!2z!23~^!2NF!2B@!55?!2@!3BF!3N!2] 105 | [y|zv~vn!2N!2^!66~}{xzQC!2@B?@$#12!66?w!3~{wpbFN!2^!5~}!2{!2xrbf!2N!2^}{wo_!230?!3owWKC_p!2~^NE?!3_ 106 | o_$#1!186?CA?A@A@ADIDITITiTiTiTiTITiTiTiTiTiTiTiTiTiTiSgSgSGS$#3!195?@A@ADADIDIDIDA!4?TITiTiTiTiTiSiSgOgOG$#8!463?$-#15!63~o!64? 107 | !2_o!3w!6o!3_!23?!3_!3o!2w!2{!2}!27~!3}!3~!2}{!14w!2{!3}!9~!3}{w!3o!2_!104?@B!103~$#2!105? 108 | @B!2FN^!12~!3^!3FB!2@!2?!3A!6F!3N!11^!4N!3F!2B!3@!79?@!3B@BFEM!2]{!4|x!4z!51~^NF@$#12!66?B 109 | ^!7~!2}{wxp!2r!2fF!2N!3^~!3}{w!2x!2r!2eKGWO!2_!214?_o{}~^NFbpw{}~^NBpw}!6~^NF_o{$#1!206?@!5?@ADADADA 110 | DADADADA@A@?@$#3!217?@?@?@?@$#8!463?$-#15!65~E!53?!2_!2ow!2{}!18~!9}!5{!4}!91~}!3{!3w!2o!2_!93?!2@!3B 111 | !4F!4N!6^!84~$#2!69?o!2_!41?!2@!3B!2@!141?!2@!2B!3F!2N!3^!13~n!2^!4~nN^!21~NB@!37?!4_$#12!69? 112 | B!2F!2N!3^!11~!3}!6{!3xz!2rvf!2e!2cghg!207?_w{~^NBpw{}!4~^F`w}!6~NF`o{}!2~^B$#8!463?$-#15!63~N!49?!2_ 113 | o{!2}!150~!2}!4{!3w!3o!4_!98?!3@!3B!2F!2N!2^!66~$#2!67?w!6~!2}!4{!4w!7o!15_o!3O!2G!165? 114 | !3@!3B!5F!2MKXZRFCKJRFN^~n!2N!2^!7~NB!37?o{!10~!7}!7{!4w!3o!2_$#12!79?!5@!4B!18F!3B!3@!206? 115 | w~^F`w}!7~N@o}!6~NBo{}!4~NB$#8!463?$-#15!62~F!47?]!178~!4}!4{!4w!3o!3_!89?@BFN^!60~$#2!66?}!17~F!2~^Nfx!2{ 116 | !13~|w!196?@A!4?HXZRB!35?_!2w!3x!2rv!2fN!2^!3~N^!22~!2}!2{wo_$#12!317?]Bo{!9~F?w!6~^B_{!5~^B$#8!463? 117 | $-#15!62~!49?N!198~!3}!89?@BFN^!54~$#2!65?!18~V?rx}^Nbx|!15~o!238?_w}!13~!2}!2|zu{xrfN^!4~!2N^!17~}{wo_$#12!315? 118 | ABw}!9~F?w!6~^Bo}!4~NB@$#8!463?$-#15!61~!23?G!27?N!200~{w!2o_!89?@BN^!50~$#2!64?!18~@!2?frw]^!19~!234?_o!2{p 119 | bFN^!22~}|zvN~!2}{xrN^!18~}wo$#12!317?B!2F!5NFB`w!6~N@o}!2~^F@$#8!463?$-#15!61~!52?!206~!6}{o!4_!80?@BF^!46~$#2 120 | !64?!17~N@?]Fp{}!21~!232?MN^!5~}!2{xrf!2N^!20~}!6~}xf^!19~}wo_$#12!327?@!4BFBo{~NFB@$#8!463?$-#15!61~!52?!219~}!5{ 121 | }!3~!2}{!2wo!2_!66?BN^!42~$#2!64?!16~o}!2?o!11~^B!12~!237?@!2B!2FN!2^!3~}{!2x!2r!2f!2N!2^!20~ 122 | {r!13~!3rvfnN^]{o_$#8!463?$-#15!61~!50?o!239~}!2{w!2_!62?@F^!39~$#2!64?B!2~^N!12~B!2?N~^F!2~^b!3~N_}!9~^N 123 | @!245?!2@BFN^!4~^!3~!4}!3|!3zr!2v~!2n!23~!4fnM[\zq_$#8!463?$-#15!61~}!45?_o{!248~}w!2o_!4?_!54?!2@!2B 124 | !2F!3N!2^!28~$#2!63?{?Fp[Nn!11~w~w?@W~^bw!2~^b{~Nb!4~^FB@!254?@BFN[PF^{xfN!2~rF^!3~N!33~}][\zqc_$#8!463?$-#15!61~ 125 | B!41?_o{}!256~}!2{wo!2_!5?O!57?@BN^!24~$#2!63?`?B@c}!16~{?`CN^nz}^bx}^NB@!268?B!2FKHBFM[rfM[pbfN^!2~!2^!25~!3} 126 | {xzrekPbM]!2{o$#8!463?$-#15!61~!38?ow{!269~!2}!2{!2w!3o!7_a!2op!5o!42?BF!2N!2^!17~$#2!62?wG?w}!18~!2} 127 | ~rw{}pOEF@!283?@!3?!2@!8?@B!2FN^!27~}{y{z!2~w_$#8!463?$-#15!61~!34?o{}!300~}{wo_!44?!3@B!2FN^!6~$#2!62?@w~z 128 | !17~Nf!7~!309?!2@BF^~fN^!30~!4}!2{!3w!2o_$#8!463?$-#15!60~N!32?o}!309~}{wo_!46?@!5~$#2!62?}!18~N?}!7~@!315? 129 | @BEHRf^!3~!3Frx^!2Nnf!25~M$#8!463?$-#15!59~F!32?}!317~}{w!2o_!39?o!5~$#2!61?!20~S?!7~F!324?@!4?CEBJHL[}!19~ 130 | !2?^N@$#8!463?$-#15!57~N!33?}!322~F@!38?}!6~$#2!58?_}!22~?F!5~!327?_ox!2{}!26~{o$#8!463?$-#15!55~^@!34?!319~^FB!41? 131 | @BN^!3~$#2!57?{!18~^!5~w?N!4~!325?W!4^!21~!2^!9~{wo$#8!463?$-#15!51~^NFB!36?BN!307~!2^!2N!2F!2B@!49?b!2~ 132 | $#2!53?_ow}!16~Nbw{!5~B_!5~!317?!3_owx!2w!2xzre{|!14~|c?!2A?GN!12~}$#8!463?$-#15!43~^!2NFB!2@!44?BN^!301~@ 133 | !27?o!2w!25?_ow{!3~$#2!46?_ow!2{}!20~p_F!2N!2^Nbo{!3~^N@o!2{o_!307?{!3}!12~^N^!3~^NF!2B!2@B^!3~`? 134 | [}!12~^NFB@$#8!463?$-#15!15~!2^!5N!9^!2N!2F!5B!2@!55?@N!299~o_!19?!3_!2ow!3~@!22?_w}!7~$#2!32?!2_!4o 135 | !3_gGYque{|!28~!2}!7~!2}!6~}w!305?F!8N^!2N!2FB@GC!2?!2@!8?!2G!2N^!3W!3B!2R!5B@$#8!463?$-#15!11~ 136 | ^F@!85?p!301~!3}{}{!9}!13~!2}!3{!11w!3{!2}!11~$#2!14?w!2}!7~!2}!2{!2w}{xz!9~}!52~N$#8!463?$-#15!5~ 137 | F!2B@!83?_!2o!2w}!363~$#2!7?!2w!2{o_?F!2N^!4~!5^!4N!15~n!2N^@B!17^!18~!2^!2NFB!2@$#8!463?$-#15!4~ 138 | wo!12_!2o!4w!3o!19_!2o!2w{!11}~!3}!2{w!4{!3w!11{!3}!371~$#2!6?@!2F!5N!2MEC!12?!2@B!2F!8N 139 | !2FB$#8!463?$-#15!461b$#8!463?$- -------------------------------------------------------------------------------- /testfiles/oriole.six: -------------------------------------------------------------------------------- 1 | This is a text test! 2 | P0;0;0;q"1;2;640;442$-#31;2;100;100;100!640~$-#31 3 | !640~$-#31!640~$-#31!640~$-#31!640~$-#31!640~$-#31!640~$-#31!640~$-#31!640~$-#31!640~$-#31!640~$-#31!402~!16N!5^!217~$-#31!381~!3^!4N!2Ff!2b 4 | r!2p!2x!2w!4{!12~!5}!3{wo`BFN!210~$-#31!362~!3^!3N!2Ff!2b!2r!2px!2w!3{!3}!4~!3^!4N!3F!3B 5 | !5@!2?!7@!8?!2@B!3?F!208~$#17;2;50;0;0!391?_?_O_OgOgSgSiSiSiTiSiSiSiSiTiTiTiTiSgS_$#19;2;50;50;0!390? 6 | _?_O_O_OgOgSgSiSiSiTiSiSiSiTiTiTiTiSiSgO$-#31!252~!11N!79~!3^!3N!2F!2f!2br!2px!2w!4{!3}!3~!3^!3N!4F!3B 7 | !4@!45?F!207~$#17!371?_?_O_OgOgSgSiSiSiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiS$#19!255?_!3?_!110?_?_O_OgOgOgSgSiSiTiTiT 8 | iTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTg$-#31!239~!7^N^!3NFMK!5?!2KM!2N^!56~!3^!4N!2FB!3br!2p!2x!2w!3{ 9 | !3}~!3^!4N!3F!5B!3@!67?!207~$#17!349?_?_O_O_OgSgSgSiSiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTi 10 | TiTiTI$#19!257?A!90?_?_O_O_OgOgSgSgSiSiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiD$#25;2;100;0;0!253? 11 | @B!2FD!3@$-#31!224~^N!2F!3B!3@!33?!2@!2B!2F!2N^!28~!3^!3NF!2f!2r!2p!2xw!4{!3}!5~!3^!2N 12 | !3F!3B!3@!86?_!207~$#17!333?_?_OgOgSgSiSiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiT 13 | iTiTiT$#19!332?_?_O_OgOgSgSiSiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTi$-#31!221~^B!17? 14 | !2o_!35?@BF!6~!2^!4N!3f!2r!2px!2w!2{!3}!4~!5^!2N!3F!3B!3@!102?_w!208~$#17!313?_?_?_OgOgSgSiSiT 15 | iTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTI@$#19!312?_?_?_O_OgOgSgSiS 16 | iTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTA$-#31!220~@!19?B!2@!39? 17 | @xw!2{!4}!3~!3^!3N!3F!3B!3@!116?_ow{}!210~$#19!294?_?_O_OgOgSgSiSiTiTItItItItItItItItItItItItItItItItItItITiTiT 18 | iTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTITiTIDA@$#17!295?_?_O_OgOgSgSiSiTitItItItItItItItItItItItItIt 19 | ItItItItItiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTITID$-#31!218~N!64?N!2F!3B!3@!118?!3_ 20 | !5ow{!3}!217~$#19!261?_?G?a?G?_!13?OgOgSgSiSiTiTiTiTiTiTiTiTiTiTtItItItItItItItItItItItItItItItiTiTi!2TiTiTiTiTiTiTiTiTi 21 | TiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTITIDIDIDADA@$#17!283?_OgSgSiSiTiTiTiTiTiTiTiTiTiTiItItItItItItItItItItItItItItItITiTiT!2i 22 | TiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTIDIDIDADADA@$#25!261?W{u}[{!2o?!3_$-#31!193~^!3N!3F!8B!7F!2N 23 | ^!167?!4_!4o!4w!4{!4}!235~$#19!261?G?a?G?a?G?a?G!9?TiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiUhUh 24 | UhTiTiTiTiTiTiTiTiTiTiTiTiTiTiTITITIDIDIDA@A@A@A@?@?@?@$#17!283?iTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiThUh 25 | UhUiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTITIDIDIDADA@A@A@?@?@?@$#25!260?FV^\~v~\~v~\~V$-#31!192~}[]KME!2C!24?!3GW!6Oo!3_!122?!4_ 26 | !4o!5w!4{!4}!259~$#19!283?tItItItItItItItItItITiTitItIDIDIDIDITITITItITiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTIDIDIDADADA@A@A@?@ 27 | $#17!283?ItItItItItItItItItItiTiTItITIDIDIDIDITITITItiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTIDIDADADADA@A@?@$#25!242?!2_!21?!7@$-#31 28 | !187~^NB!2@!45?@!105?!2_!4o!3w!3{!3}!282~$#25!238?!2@!2BFDFDNLNF^\~t~\~v~\~s}[}u}[}s}[}u}[}u}[}u{[{owW!3oO!3o 29 | O!2owWwOWG$#19!239?A!3?A?A?A?G?a?I?a?G?a?I?a?G?a?I?a?G?a?G?a?G?_@G@a@ADaDADaDA@a@I!2@A@!25?@ADITiTiTiTiDIDADA@A@?@$#17!283? 30 | @?@A@ADADADADA@A@A@!2A@A!2@!22?@A@ADiTITiTiTIDIDADA@A@?@$-#31!170~^N!6FN^!6~\W!2GC!142?_o!2{}!302~$#25!252?!2@B!2F 31 | DNFNL^v~\~v~\~v~\~v~\~v~\~v~\~v~\^FNDF!2B!2@$#19!253?A!3?A?G?A?G?a?G?a?G?a?G?a?G?a?G?a?G?A?G?A$-#31!159~^F!5B@!2B!11?!2@ 32 | Xw!2Ca!55?_w{!3}!5~!2}{!2w!2o!2_!28?_!2w!2{!4~!4}{!11w!13ow!2{!2}!307~$#25!266?!3@!2B@!3FD!3F 33 | D!3F@!2B!2@$#19!271?A!3?A!3?A!3?A$-#31!138~!5^!6~^NF!2B!5@!25?!2@!55?!15~^!4N!2^{!2_ow{!12}{w_!5? 34 | B!350~$#25!214?_!2oOws{[{s{SwowW!3oO_$#19!185?i!27?_!3?g?G?_?G?g?G?_!3?_$#17!174?_!9?_S$-#31!122~^NF!2B!6@!2B@!9? 35 | !2@B@!92?@FN!9~N@!9?f!9~NB!5?@N~!6?!2N!2^!102~^!2Nn!240~$#25!208?_o{[~t~\~V~\~t~\~V~\~t~\~V~[}so!157?_!2oO 36 | $#19!167?GO_!5?_TiSgSgSiTA!23?G?a?I?a?g?a?I?a?g?a?I?a?g?a?G$#17!167?Og?_!4?@iSgSgSgSiD$-#31!62~^!5N!3FB!3b!2fF!2N!2^ 37 | !29~!2^N!2F!2B!4@!146?!6@!8?EF!11?@F^!82~!3^!2N!4F!3B!2@!3?ow!241~$#25!206?os~\~v~T~v~\~v~T~v~\~v~T~ 38 | v~\~v~T~v~\{u}T~v~\~v~T~v~\~v~S}u}[}u}S{s{[{s{SwowW!2ofLNV^\~v}Wo_!84?_?_!2oWwow[{s}[~v~LF$#19!64?_?_?gO_!85?iSgO_O_!2? 39 | OGCJCBCB?B?B?BCDiTiTg!21?G?a?G?i?G?a?G?i?G?a?G?i?G?a?G?i?G?a?G?i?G?a?G?i?G?a?G?i?G?a?G?i?G?_?G?g?G?_!3?a?G?a?G?_!87?_!3? 40 | _?G?_?G?a?G?A$#17!65?_?_?_O_!84?TiSgO_?_O!2?ICBCB?B?B?BCBITiTiS$-#31!60~B!12?!2@!2BFN]!2{wpb!2FN!2^!11~!3^!3N 41 | fb`!4o!2w!3oO!177?_!64~!3^!3N!3F!2B!2@!19?_w}!243~$#25!206?~\~t~\~V~\~t~\~V~\~t~\N!2FDfdn\~V~\~t~\~V~\~t~\~ 42 | V~\~t~\~V~\~t~\~V~\~t~\~V~\~t~\~V~[}s{[xRrDF!65?_?_OoOwow[{U}\~t~\~V~\~t~\~V~\~t^D@$#19!61?OiTi@i@iDiDiSiSgO_!41?_?_!34?@IT 43 | iT!19?iTiTiTgO!18?a?I?a?g?a?I?a?g?a?I?A!3?A?I?a?g?a?I?a?g?a?I?a?g?a?I?a?g?a?I?a?g?a?I?a?g?a?I?a?g?a?I?_?g?a!67?_?_?_?G?_? 44 | g?a?I?a?g?a?I?a?g?a?I?A$#17!61?gSiTiTiTiTiTiSgSgO_!39?_?_!34?@ADiTi@!18?TITiTiS_$#23;2;50;50;50!65?S?S?O?O$-#31!59~!22? 45 | !2@!2BF!2M!2K!2G!2H!3@D!2K!4MN!6F!3B!3@!61?_o!6w!5{}{o!104?@BFN^!47~!3N!2F!2B!2@!32?o{!247~$#25 46 | !206?@V~\~v~\~v~T~v~\~v~T~o}\{o`@BF^\~v~T~v~\~v~T~v~\~v~T~v~\~v~T~v~\~v~T~v~\~v~T~v~\~v~T~v~\~v}Swo_!49?!3oOws{[}v~T~v~\~v~ 47 | T~v~\~v~T~v~\~v~T~v~\~FB$#19!60?iTiOi?i@i?iOi?i@iDiTiSiSgO_O_O_O_OgOgO_O_O_O_O_OgOgOgSiSiDiTiTiO_!36?@ADISISA?A!10?A@B@?@!20? 48 | G?a?G?a?G?i?G?a?G?i!3?a?G?a?G?A?G?i?G?a?G?i?G?a?G?i?G?a?G?i?G?a?G?i?G?a?G?i?G?a?G?i?G?a?G?g!55?g?G?a?G?i?G?a?G?i?G?a?G?i?G? 49 | a?G?i?G?a?G$#17!60?TiTiTiTiTiTiTiTiTiTiTiSgOgO_O_O_O_OgOgO_O_O_O_O_OgOgOgSiSiTiTiTg?_!36?@ACISI?A@$#23!63?D?T?S?T?D?T?S?O!39? 50 | O$#21;2;50;0;50!181?@A?A@?@$-#31!58~@!118?A!4F!5N!5^!3~}{o_!46?Oo!4_!53?@BN!18~!3^!2N!2F!11~F!3@!41? 51 | o{}!249~$#25!208?@FN\~t~\~V~\~t~\~V~\~t~\~V~[{!2o?`@BDFDFLNFNL^T^\^V^\^T^\^V^\^T^\NfnLndnLnfnLndnLnfnLNDNL^V^\^T}[wO!19?!2_ 52 | ?!2owW!11?W}U}\~t~\~V~\~t~\~V~\~t~\~V~\~t~\~V~\~t~\~V~\N!2@$#19!59?SiTiOiSiSiOiTiTiSiSiSiSiDi@iPiPiTiTiPiPiTiPiPiPiTiTiOi 53 | OiSiSiSiPiTiTiTiSgO!79?a?I?a?g?a?I?a?g?a?I?a?g?a!3?_!3?A?A?A?G?A?I?A?G?A?I?A?G?A?I?A?G?a?I?a?G?a?I?a?G?a?I?A?G?A?I?_?_!21? 54 | _!3?_!11?_?g?a?I?a?g?a?I?a?g?a?I?a?g?a?I?a?g?a?I?a?g?a?A$#17!59?gTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTi 55 | TiTiTiTiTiSiS_$#23!63?D?@?@?D!5?@?@?@?@?O?S?C?C!5?C?C!3?C?C?C!5?D?D?@?@?@?C$-#31!58~o!75?_!63?@!48?@!3BEC!38?o!23~ 56 | !2^N!2F!2B!2@!8?!10~^!42?ow!253~$#25!211?@FD!3FD!3FD!3FD!3FD!3FD!3FD!3FDEC!2KG?W!3O!15?_oW~v~\~v 57 | ~T~v~\~v~T~v~\~F!23?!2_Owo{[}u~T~v~\~v!10?_v~\~v~T~v~\~v~T~v~\~v~T~v~\~v~T~v~\~v~T~v~LF$#19!59?DaTiTiTiTiTiTiTiTiTiTiTiTiT 58 | iTiTIDiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTgS!79?A!3?A!3?A!3?A!3?A!3?A!3?A!3?A!5?G!21?_?G?a?G?i?G?a?G?i?G 59 | ?a?G!25?_?G?_?G?i?G?a?G!11?G?a?G?i?G?a?G?i?G?a?G?i?G?a?G?i?G?a?G?i?G?A$#17!59?AP_TiTiTgOiTiTiTiTiTiTiTiTiTiTiDATiTiTiTiTiTiT 60 | iTiTiTiTiTiTiTiTiTiTiTiTiTiSg$-#31!59~}!50?!6o!4{!5}!14~!3}{C!144?w{!17~^N!2FB@!19?!9~N!40?_{!256~$#25!262?!2[ 61 | }u~\~V~\~t~\~V~\~t~\~V~\F@!18?!2ow[}t~\~V~\~t~\~V~\~t~\!9?o~\~t~\~V~\~t~\~V~\~t~\~V~\~t~\~V~\~t~\~V^@$#19!61?TiTiTiTiTiTi 62 | TiTiTiTiTiTiTiTiTg!2?TiTiTiTiTiTiTiTiTIDIDID?@?@?@?@!137?G!3?G?a?g?a?I?a?g?a?I?a?g?a?A!17?_?G?_?I?a?g?a?I?a?g?a?I?a!11?a 63 | ?I?a?g?a?I?a?g?a?I?a?g?a?I?a?g?a?I?a?g?A$#17!61?iTiTiTiTiTiTiTiTiTiTiTiTiTiTiP!2?iTiTiTiTiTiTiTiTiDIDIDI@?@?@?@?@$-#31!60~}!37? 64 | !2_o!2w{!37~N!138?!2_!2o{!2}!18~o!26?!9~!37?_{}!259~$#25!266?!2@FL~v~T~v~\~v^TNFB!2@!18?D~v~\~v~\~v~\~v~T~ 65 | v~\~v~T~v!9?\~v~T~v~\~v~T~v~\~v~T~v~\~v~T~v~\~v~T^B@$#19!62?iOiTiTiTiTiTiTiTiTiTiTiTiTiTi!2?TiTiTITIDA@!165?A?G?i?G?a?G?I? 66 | G!21?I?G?a?G?a?G?a?G?i?G?a?G?i?G!9?a?G?i?G?a?G?i?G?a?G?i?G?a?G?i?G?a?G?i$#17!62?TiTiTiTiTiTiTiTiTiTiTiTiTiTiT!2?iTiTiTIDA 67 | DA$#23!63?D$-#31!62~{wo!27?!3{}!44~N!133?!3w{}!27~w!25?!8~!35?w{!263~$#25!271?@B!2FDF!2@!27?F\~v~\~v~\~t~\~V~\~t 68 | ~\~V~\!8?~v~\~V~\~t~\~V~\~t~\~V~\~t~\~V~\~t~DB$#19!63?DITiTiTiTiTiTiTiTiTiTiTiTiTi!2?@A@!179?A?A!29?a?G?a?G?a?I?a?g?a?I?a 69 | ?g?a!9?G?a?g?a?I?a?g?a?I?a?g?a?I?a?g?a?I?A$#17!62?@ADITiTiTiTiTiTiTiTiTiTiTiTiT!2?A@$-#31!66~{o_!22?o!48~!134?B!32~w!24? 70 | !6~^!32?_w}!266~$#25!173?Ows{[{!2oO_!124?F~T~v~\~v~T~v~\~v~T~v~\~v!6?_v~T~v~\~v~T~v~\~v~T~v~\~v~T~v~\~VF@$#19!67?@ITiTiT 71 | iTiTiTiTiTiTiDiTI!82?_?G?_?G?_!127?i?G?a?G?i?G?a?G?i?G?a?G!7?G?i?G?a?G?i?G?a?G?i?G?a?G?i?G?a?G$#17!67?ADITiTiTiTiTiTiTiTiTi 72 | TID$-#31!70~}{!2w!3o!7_!2o!2w{}!50~o!134?B!32~!24?!5~N!30?_w}!269~$#25!172?~v~\~v~\~v~\}u{W!2o_!117?~V~\~v~\~V 73 | ~\~t~\~V~\~t~\!5?o~\~V~\~t~\~V~\~t~\~V~\~t~\~V~\^D@$#19!71?@A@ADADADIDIDA@A@!83?_?G?a?G?a?G?a?G?_!3?_!117?g?a?G?a?g?a?I?a 74 | ?g?a?I?a!7?a?g?a?I?a?g?a?I?a?g?a?I?a?g?a?A$#17!72?@ADADILADIDIDA@A@$-#31!142~}o!132?^!31~!24?!2~^B!26?_ow!2}!273~$#25!170? 75 | wv~\~v~\~v~\~v~\~v~\~v~S{owOo_!108?~\~v~\~v~\~v~T~v~\~v~T~v!2?_s~T~v~\~v~T~v~\~v~T~v~\~v~T^!2F!2@$#19!171?G?a?G?a?G?a?G?a 76 | ?G?a?G?i?G?_!111?a?G?a?G?a?G?i?G?a?G?i?G!3?G?i?G?a?G?i?G?a?G?i?G?a?G?i?G$-#31!144~w_!130?@!31~!24?B!23?!2_w{!2}!278~$#25 77 | !169?v~\~v~\~v~\~t~\~V~\~t~\~V~\~t~\~V}[{swOo!2_!97?~t~\~v~\~t~\~v~\~t~\~v~\{v~\~v~\~t~\~v~\^T^LN!2FDFDF!3@$#19!169?G?a? 78 | G?a?G?a?I?a?g?a?I?a?g?a?I?a?g?a?G?_!3?_!97?I?a?G?a?I?a?G?a?I?a?G?a?G?a?G?a?I?a?G?a?I?A?G?A?A?A$-#31!146~}wo!2_!9?!3_!2o 79 | !3w!2{o!106?F!29~F!43?_ow{}!284~$#25!169?@Nv~T~v~\~v~T~v~\~v~T~v~\~v~T~v~\~v~T~v~\~v~\~u}[{s{Ww!2oO!2_!79?o~\~v~\~v~ 80 | \~v~\~v~\~v~\^V^LN!2FDF!2B!3@$#19!171?G?i?G?a?G?i?G?a?G?i?G?a?G?i?G?a?G?i?G?a?G?a?G?a?G?_?G?_!81?G?a?G?a?G?a?G?a?G?a?G?a 81 | ?G?A?G?A$-#31!171~w_!105?B!9~!4^!2N!7^!5~B!39?_ow{!290~$#25!171?D^v~\~t~\~V~\~t~\~V~\~t~\~V~\~t~\~V~\~t~\~V~\~t~\~V~\~ 82 | v~\~u}[wo!72?{\~v^\NFND!3F@!3B!3@$#19!171?A?G?a?I?a?g?a?I?a?g?a?I?a?g?a?I?a?g?a?I?a?g?a?I?a?g?a?G?a?G?_!75?a?G?A?G?A!3? 83 | A$-#31!173~}wo!104?!3B!2@!18?!3@!35?_!2w}!207~^NFbr!2x{w_?C[{!2xrbFN^!67~$#25!173?@!2F~\~v~\~v~T~v~\~v~T~v~\~v~T 84 | ~v~\~v~T~v~\~v~T~v~\~v~T~v~\~v~\~sw$#19!175?G?a?G?a?G?i?G?a?G?i?G?a?G?i?G?a?G?i?G?a?G?i?G?a?G?i?G?a?G?a?G$#17!560?E]}w_$-#31!178~{ 85 | o!156?!2_w{}!209~F@W]!3^!5~}wopbBF^~}w@F!65~$#25!178?BL~t~\~V~\~t~\~V~\~t~\~V~\~t~\~V~\~t~\~V~\~t~\~V~\~t~\~v~\~v{O$#19!179? 86 | A?I?a?g?a?I?a?g?a?I?a?g?a?I?a?g?a?I?a?g?a?I?a?g?a?I?a?G?a?G?_$#17!552?!5_!5?@FNM[{w_$-#31!181~}!2{!2wo!2_!142?_ow{}!214~ 87 | o?M!2}!2wx!2pr!2b!2F!2^}!3{wG?o!65~$#25!181?@!2BFDNV^T~v~\~v~T~v~\~v~T~v~\~v~\~v~\~v~\~v~\~v~T~v~\N!2FD!2F 88 | $#17!552?!3@!2FE!2MK!2[!2w!2_@!3B!2F!2E$#19!185?A?G?i?G?a?G?i?G?a?G?i?G?a?G?a?G?a?G?a?G?a?G?i?G?A!3?A$-#31!191~ 89 | }!2{!3w!2o!3_!63?!5A!12?!2Oo!38?_ow!2{}!223~{wpbf!2N!4^!2]W?@Frpw{!67~$#25!191?@B@FD!2FN\^T~\~V~D!3F 90 | D!3FD!2FNLNFN\^V~\~v~@$#19!193?A?A?G?A?I?a?g?A!3?A!3?A!3?A?G?A?G?a?G?A$#17!563?!2@F~}o$-#31!205~}!116?y!236~!7}!74~$#25 91 | !205?@!23?@$-#31!206~!24?@!5?G!9?!3_!70?ow!319~$-#31!206~w_!11?W[{}o_!23?!2@AEKWwo!2_!4?[{}!9~!2}!2{!37?C[ 92 | }!322~$-#31!208~{o!12?@BCO!2?A?!2C!2G!18?CGO`@B!2FM[wo`B!2FB!5@B!2FK!3?!2o!3_!31?!323~$-#31!211~}wo!13?EO!7? 93 | !2O?GL!4E!2N!9?@!2?G!2O?BFNM!14?F!6~}!2{wo_!23?@^!20~!3^!2N!3F!2N^!290~$#25!269?K[s}[}s{Wo!2_$#19!271? 94 | G?a?G?_$-#31!216~{wo_!8?GN]M?@!49?F^!9~}{o!22?FN!2^!6~!5^NFB@!12?!2@B!2zBN!283~$#25!272?@BFD!2FN\^s$#19!275?A?G 95 | ?A?G$-#31!221~}{wo!2_!13?!2_o_!41?@N^!10~{o_!19?w{wo?!3@!26?Bxw!283~$#25!327?AE[}u{Wo$#19!329?a?G?_$-#31!228~!2}!2{!4w 96 | {!2}!9~!3}!3{!2w!2o!2_!10?_?O!2?GC!12?BF^!9~{wo!15?o!2~^N!30?_r!284~$#25!328?ot^\NF@$#19!327?_?I?A$-#31!262~ 97 | }C?A?!2@!24?F!11~}{wo!7_o!2w!12{!3wo!7O!11?!287~$#25!326?!2@$-#31!262~|o_!4?!2Oo?!2G!2C?@BV!9?!2ow!43~ 98 | }{c!5C!9?@^!285~$-#31!267~!2}!2{w!2op`_A?@BF!4?_w{}!50~p!4PO!7?EoB^!283~$-#31!281~!3}!57~!5C!9?E?B!282~$-#31 99 | !325~!2^NF!9B!3F!2HP`@!12?!13~FN!2~^Nf~!2^Nn!257~$-#31!319~^NFB@!21?@AC!8?_oBN!9~B_}{`@w`K!2]!260~$-#31!318~p 100 | !6o?O!3oO?!17o!2wsu!3wxw{!10~}!4~}!4~!3}{xz!2r!253~$-#31!325n!2mklkm!309n$-\ -------------------------------------------------------------------------------- /testfiles/oriole_clean.six: -------------------------------------------------------------------------------- 1 | "1;2;640;442$-#31;2;100;100;100!640~$-#31 2 | !640~$-#31!640~$-#31!640~$-#31!640~$-#31!640~$-#31!640~$-#31!640~$-#31!640~$-#31!640~$-#31!640~$-#31!402~!16N!5^!217~$-#31!381~!3^!4N!2Ff!2b 3 | r!2p!2x!2w!4{!12~!5}!3{wo`BFN!210~$-#31!362~!3^!3N!2Ff!2b!2r!2px!2w!3{!3}!4~!3^!4N!3F!3B 4 | !5@!2?!7@!8?!2@B!3?F!208~$#17;2;50;0;0!391?_?_O_OgOgSgSiSiSiTiSiSiSiSiTiTiTiTiSgS_$#19;2;50;50;0!390? 5 | _?_O_O_OgOgSgSiSiSiTiSiSiSiTiTiTiTiSiSgO$-#31!252~!11N!79~!3^!3N!2F!2f!2br!2px!2w!4{!3}!3~!3^!3N!4F!3B 6 | !4@!45?F!207~$#17!371?_?_O_OgOgSgSiSiSiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiS$#19!255?_!3?_!110?_?_O_OgOgOgSgSiSiTiTiT 7 | iTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTg$-#31!239~!7^N^!3NFMK!5?!2KM!2N^!56~!3^!4N!2FB!3br!2p!2x!2w!3{ 8 | !3}~!3^!4N!3F!5B!3@!67?!207~$#17!349?_?_O_O_OgSgSgSiSiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTi 9 | TiTiTI$#19!257?A!90?_?_O_O_OgOgSgSgSiSiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiD$#25;2;100;0;0!253? 10 | @B!2FD!3@$-#31!224~^N!2F!3B!3@!33?!2@!2B!2F!2N^!28~!3^!3NF!2f!2r!2p!2xw!4{!3}!5~!3^!2N 11 | !3F!3B!3@!86?_!207~$#17!333?_?_OgOgSgSiSiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiT 12 | iTiTiT$#19!332?_?_O_OgOgSgSiSiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTi$-#31!221~^B!17? 13 | !2o_!35?@BF!6~!2^!4N!3f!2r!2px!2w!2{!3}!4~!5^!2N!3F!3B!3@!102?_w!208~$#17!313?_?_?_OgOgSgSiSiT 14 | iTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTI@$#19!312?_?_?_O_OgOgSgSiS 15 | iTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTA$-#31!220~@!19?B!2@!39? 16 | @xw!2{!4}!3~!3^!3N!3F!3B!3@!116?_ow{}!210~$#19!294?_?_O_OgOgSgSiSiTiTItItItItItItItItItItItItItItItItItItITiTiT 17 | iTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTITiTIDA@$#17!295?_?_O_OgOgSgSiSiTitItItItItItItItItItItItItIt 18 | ItItItItItiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTITID$-#31!218~N!64?N!2F!3B!3@!118?!3_ 19 | !5ow{!3}!217~$#19!261?_?G?a?G?_!13?OgOgSgSiSiTiTiTiTiTiTiTiTiTiTtItItItItItItItItItItItItItItItiTiTi!2TiTiTiTiTiTiTiTiTi 20 | TiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTITIDIDIDADA@$#17!283?_OgSgSiSiTiTiTiTiTiTiTiTiTiTiItItItItItItItItItItItItItItItITiTiT!2i 21 | TiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTIDIDIDADADA@$#25!261?W{u}[{!2o?!3_$-#31!193~^!3N!3F!8B!7F!2N 22 | ^!167?!4_!4o!4w!4{!4}!235~$#19!261?G?a?G?a?G?a?G!9?TiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiUhUh 23 | UhTiTiTiTiTiTiTiTiTiTiTiTiTiTiTITITIDIDIDA@A@A@A@?@?@?@$#17!283?iTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiThUh 24 | UhUiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTITIDIDIDADA@A@A@?@?@?@$#25!260?FV^\~v~\~v~\~V$-#31!192~}[]KME!2C!24?!3GW!6Oo!3_!122?!4_ 25 | !4o!5w!4{!4}!259~$#19!283?tItItItItItItItItItITiTitItIDIDIDIDITITITItITiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTIDIDIDADADA@A@A@?@ 26 | $#17!283?ItItItItItItItItItItiTiTItITIDIDIDIDITITITItiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTIDIDADADADA@A@?@$#25!242?!2_!21?!7@$-#31 27 | !187~^NB!2@!45?@!105?!2_!4o!3w!3{!3}!282~$#25!238?!2@!2BFDFDNLNF^\~t~\~v~\~s}[}u}[}s}[}u}[}u}[}u{[{owW!3oO!3o 28 | O!2owWwOWG$#19!239?A!3?A?A?A?G?a?I?a?G?a?I?a?G?a?I?a?G?a?G?a?G?_@G@a@ADaDADaDA@a@I!2@A@!25?@ADITiTiTiTiDIDADA@A@?@$#17!283? 29 | @?@A@ADADADADA@A@A@!2A@A!2@!22?@A@ADiTITiTiTIDIDADA@A@?@$-#31!170~^N!6FN^!6~\W!2GC!142?_o!2{}!302~$#25!252?!2@B!2F 30 | DNFNL^v~\~v~\~v~\~v~\~v~\~v~\~v~\^FNDF!2B!2@$#19!253?A!3?A?G?A?G?a?G?a?G?a?G?a?G?a?G?a?G?A?G?A$-#31!159~^F!5B@!2B!11?!2@ 31 | Xw!2Ca!55?_w{!3}!5~!2}{!2w!2o!2_!28?_!2w!2{!4~!4}{!11w!13ow!2{!2}!307~$#25!266?!3@!2B@!3FD!3F 32 | D!3F@!2B!2@$#19!271?A!3?A!3?A!3?A$-#31!138~!5^!6~^NF!2B!5@!25?!2@!55?!15~^!4N!2^{!2_ow{!12}{w_!5? 33 | B!350~$#25!214?_!2oOws{[{s{SwowW!3oO_$#19!185?i!27?_!3?g?G?_?G?g?G?_!3?_$#17!174?_!9?_S$-#31!122~^NF!2B!6@!2B@!9? 34 | !2@B@!92?@FN!9~N@!9?f!9~NB!5?@N~!6?!2N!2^!102~^!2Nn!240~$#25!208?_o{[~t~\~V~\~t~\~V~\~t~\~V~[}so!157?_!2oO 35 | $#19!167?GO_!5?_TiSgSgSiTA!23?G?a?I?a?g?a?I?a?g?a?I?a?g?a?G$#17!167?Og?_!4?@iSgSgSgSiD$-#31!62~^!5N!3FB!3b!2fF!2N!2^ 36 | !29~!2^N!2F!2B!4@!146?!6@!8?EF!11?@F^!82~!3^!2N!4F!3B!2@!3?ow!241~$#25!206?os~\~v~T~v~\~v~T~v~\~v~T~ 37 | v~\~v~T~v~\{u}T~v~\~v~T~v~\~v~S}u}[}u}S{s{[{s{SwowW!2ofLNV^\~v}Wo_!84?_?_!2oWwow[{s}[~v~LF$#19!64?_?_?gO_!85?iSgO_O_!2? 38 | OGCJCBCB?B?B?BCDiTiTg!21?G?a?G?i?G?a?G?i?G?a?G?i?G?a?G?i?G?a?G?i?G?a?G?i?G?a?G?i?G?a?G?i?G?_?G?g?G?_!3?a?G?a?G?_!87?_!3? 39 | _?G?_?G?a?G?A$#17!65?_?_?_O_!84?TiSgO_?_O!2?ICBCB?B?B?BCBITiTiS$-#31!60~B!12?!2@!2BFN]!2{wpb!2FN!2^!11~!3^!3N 40 | fb`!4o!2w!3oO!177?_!64~!3^!3N!3F!2B!2@!19?_w}!243~$#25!206?~\~t~\~V~\~t~\~V~\~t~\N!2FDfdn\~V~\~t~\~V~\~t~\~ 41 | V~\~t~\~V~\~t~\~V~\~t~\~V~\~t~\~V~[}s{[xRrDF!65?_?_OoOwow[{U}\~t~\~V~\~t~\~V~\~t^D@$#19!61?OiTi@i@iDiDiSiSgO_!41?_?_!34?@IT 42 | iT!19?iTiTiTgO!18?a?I?a?g?a?I?a?g?a?I?A!3?A?I?a?g?a?I?a?g?a?I?a?g?a?I?a?g?a?I?a?g?a?I?a?g?a?I?a?g?a?I?_?g?a!67?_?_?_?G?_? 43 | g?a?I?a?g?a?I?a?g?a?I?A$#17!61?gSiTiTiTiTiTiSgSgO_!39?_?_!34?@ADiTi@!18?TITiTiS_$#23;2;50;50;50!65?S?S?O?O$-#31!59~!22? 44 | !2@!2BF!2M!2K!2G!2H!3@D!2K!4MN!6F!3B!3@!61?_o!6w!5{}{o!104?@BFN^!47~!3N!2F!2B!2@!32?o{!247~$#25 45 | !206?@V~\~v~\~v~T~v~\~v~T~o}\{o`@BF^\~v~T~v~\~v~T~v~\~v~T~v~\~v~T~v~\~v~T~v~\~v~T~v~\~v~T~v~\~v}Swo_!49?!3oOws{[}v~T~v~\~v~ 46 | T~v~\~v~T~v~\~v~T~v~\~FB$#19!60?iTiOi?i@i?iOi?i@iDiTiSiSgO_O_O_O_OgOgO_O_O_O_O_OgOgOgSiSiDiTiTiO_!36?@ADISISA?A!10?A@B@?@!20? 47 | G?a?G?a?G?i?G?a?G?i!3?a?G?a?G?A?G?i?G?a?G?i?G?a?G?i?G?a?G?i?G?a?G?i?G?a?G?i?G?a?G?i?G?a?G?g!55?g?G?a?G?i?G?a?G?i?G?a?G?i?G? 48 | a?G?i?G?a?G$#17!60?TiTiTiTiTiTiTiTiTiTiTiSgOgO_O_O_O_OgOgO_O_O_O_O_OgOgOgSiSiTiTiTg?_!36?@ACISI?A@$#23!63?D?T?S?T?D?T?S?O!39? 49 | O$#21;2;50;0;50!181?@A?A@?@$-#31!58~@!118?A!4F!5N!5^!3~}{o_!46?Oo!4_!53?@BN!18~!3^!2N!2F!11~F!3@!41? 50 | o{}!249~$#25!208?@FN\~t~\~V~\~t~\~V~\~t~\~V~[{!2o?`@BDFDFLNFNL^T^\^V^\^T^\^V^\^T^\NfnLndnLnfnLndnLnfnLNDNL^V^\^T}[wO!19?!2_ 51 | ?!2owW!11?W}U}\~t~\~V~\~t~\~V~\~t~\~V~\~t~\~V~\~t~\~V~\N!2@$#19!59?SiTiOiSiSiOiTiTiSiSiSiSiDi@iPiPiTiTiPiPiTiPiPiPiTiTiOi 52 | OiSiSiSiPiTiTiTiSgO!79?a?I?a?g?a?I?a?g?a?I?a?g?a!3?_!3?A?A?A?G?A?I?A?G?A?I?A?G?A?I?A?G?a?I?a?G?a?I?a?G?a?I?A?G?A?I?_?_!21? 53 | _!3?_!11?_?g?a?I?a?g?a?I?a?g?a?I?a?g?a?I?a?g?a?I?a?g?a?A$#17!59?gTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTi 54 | TiTiTiTiTiSiS_$#23!63?D?@?@?D!5?@?@?@?@?O?S?C?C!5?C?C!3?C?C?C!5?D?D?@?@?@?C$-#31!58~o!75?_!63?@!48?@!3BEC!38?o!23~ 55 | !2^N!2F!2B!2@!8?!10~^!42?ow!253~$#25!211?@FD!3FD!3FD!3FD!3FD!3FD!3FD!3FDEC!2KG?W!3O!15?_oW~v~\~v 56 | ~T~v~\~v~T~v~\~F!23?!2_Owo{[}u~T~v~\~v!10?_v~\~v~T~v~\~v~T~v~\~v~T~v~\~v~T~v~\~v~T~v~LF$#19!59?DaTiTiTiTiTiTiTiTiTiTiTiTiT 57 | iTiTIDiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTgS!79?A!3?A!3?A!3?A!3?A!3?A!3?A!3?A!5?G!21?_?G?a?G?i?G?a?G?i?G 58 | ?a?G!25?_?G?_?G?i?G?a?G!11?G?a?G?i?G?a?G?i?G?a?G?i?G?a?G?i?G?a?G?i?G?A$#17!59?AP_TiTiTgOiTiTiTiTiTiTiTiTiTiTiDATiTiTiTiTiTiT 59 | iTiTiTiTiTiTiTiTiTiTiTiTiTiSg$-#31!59~}!50?!6o!4{!5}!14~!3}{C!144?w{!17~^N!2FB@!19?!9~N!40?_{!256~$#25!262?!2[ 60 | }u~\~V~\~t~\~V~\~t~\~V~\F@!18?!2ow[}t~\~V~\~t~\~V~\~t~\!9?o~\~t~\~V~\~t~\~V~\~t~\~V~\~t~\~V~\~t~\~V^@$#19!61?TiTiTiTiTiTi 61 | TiTiTiTiTiTiTiTiTg!2?TiTiTiTiTiTiTiTiTIDIDID?@?@?@?@!137?G!3?G?a?g?a?I?a?g?a?I?a?g?a?A!17?_?G?_?I?a?g?a?I?a?g?a?I?a!11?a 62 | ?I?a?g?a?I?a?g?a?I?a?g?a?I?a?g?a?I?a?g?A$#17!61?iTiTiTiTiTiTiTiTiTiTiTiTiTiTiP!2?iTiTiTiTiTiTiTiTiDIDIDI@?@?@?@?@$-#31!60~}!37? 63 | !2_o!2w{!37~N!138?!2_!2o{!2}!18~o!26?!9~!37?_{}!259~$#25!266?!2@FL~v~T~v~\~v^TNFB!2@!18?D~v~\~v~\~v~\~v~T~ 64 | v~\~v~T~v!9?\~v~T~v~\~v~T~v~\~v~T~v~\~v~T~v~\~v~T^B@$#19!62?iOiTiTiTiTiTiTiTiTiTiTiTiTiTi!2?TiTiTITIDA@!165?A?G?i?G?a?G?I? 65 | G!21?I?G?a?G?a?G?a?G?i?G?a?G?i?G!9?a?G?i?G?a?G?i?G?a?G?i?G?a?G?i?G?a?G?i$#17!62?TiTiTiTiTiTiTiTiTiTiTiTiTiTiT!2?iTiTiTIDA 66 | DA$#23!63?D$-#31!62~{wo!27?!3{}!44~N!133?!3w{}!27~w!25?!8~!35?w{!263~$#25!271?@B!2FDF!2@!27?F\~v~\~v~\~t~\~V~\~t 67 | ~\~V~\!8?~v~\~V~\~t~\~V~\~t~\~V~\~t~\~V~\~t~DB$#19!63?DITiTiTiTiTiTiTiTiTiTiTiTiTi!2?@A@!179?A?A!29?a?G?a?G?a?I?a?g?a?I?a 68 | ?g?a!9?G?a?g?a?I?a?g?a?I?a?g?a?I?a?g?a?I?A$#17!62?@ADITiTiTiTiTiTiTiTiTiTiTiTiT!2?A@$-#31!66~{o_!22?o!48~!134?B!32~w!24? 69 | !6~^!32?_w}!266~$#25!173?Ows{[{!2oO_!124?F~T~v~\~v~T~v~\~v~T~v~\~v!6?_v~T~v~\~v~T~v~\~v~T~v~\~v~T~v~\~VF@$#19!67?@ITiTiT 70 | iTiTiTiTiTiTiDiTI!82?_?G?_?G?_!127?i?G?a?G?i?G?a?G?i?G?a?G!7?G?i?G?a?G?i?G?a?G?i?G?a?G?i?G?a?G$#17!67?ADITiTiTiTiTiTiTiTiTi 71 | TID$-#31!70~}{!2w!3o!7_!2o!2w{}!50~o!134?B!32~!24?!5~N!30?_w}!269~$#25!172?~v~\~v~\~v~\}u{W!2o_!117?~V~\~v~\~V 72 | ~\~t~\~V~\~t~\!5?o~\~V~\~t~\~V~\~t~\~V~\~t~\~V~\^D@$#19!71?@A@ADADADIDIDA@A@!83?_?G?a?G?a?G?a?G?_!3?_!117?g?a?G?a?g?a?I?a 73 | ?g?a?I?a!7?a?g?a?I?a?g?a?I?a?g?a?I?a?g?a?A$#17!72?@ADADILADIDIDA@A@$-#31!142~}o!132?^!31~!24?!2~^B!26?_ow!2}!273~$#25!170? 74 | wv~\~v~\~v~\~v~\~v~\~v~S{owOo_!108?~\~v~\~v~\~v~T~v~\~v~T~v!2?_s~T~v~\~v~T~v~\~v~T~v~\~v~T^!2F!2@$#19!171?G?a?G?a?G?a?G?a 75 | ?G?a?G?i?G?_!111?a?G?a?G?a?G?i?G?a?G?i?G!3?G?i?G?a?G?i?G?a?G?i?G?a?G?i?G$-#31!144~w_!130?@!31~!24?B!23?!2_w{!2}!278~$#25 76 | !169?v~\~v~\~v~\~t~\~V~\~t~\~V~\~t~\~V}[{swOo!2_!97?~t~\~v~\~t~\~v~\~t~\~v~\{v~\~v~\~t~\~v~\^T^LN!2FDFDF!3@$#19!169?G?a? 77 | G?a?G?a?I?a?g?a?I?a?g?a?I?a?g?a?G?_!3?_!97?I?a?G?a?I?a?G?a?I?a?G?a?G?a?G?a?I?a?G?a?I?A?G?A?A?A$-#31!146~}wo!2_!9?!3_!2o 78 | !3w!2{o!106?F!29~F!43?_ow{}!284~$#25!169?@Nv~T~v~\~v~T~v~\~v~T~v~\~v~T~v~\~v~T~v~\~v~\~u}[{s{Ww!2oO!2_!79?o~\~v~\~v~ 79 | \~v~\~v~\~v~\^V^LN!2FDF!2B!3@$#19!171?G?i?G?a?G?i?G?a?G?i?G?a?G?i?G?a?G?i?G?a?G?a?G?a?G?_?G?_!81?G?a?G?a?G?a?G?a?G?a?G?a 80 | ?G?A?G?A$-#31!171~w_!105?B!9~!4^!2N!7^!5~B!39?_ow{!290~$#25!171?D^v~\~t~\~V~\~t~\~V~\~t~\~V~\~t~\~V~\~t~\~V~\~t~\~V~\~ 81 | v~\~u}[wo!72?{\~v^\NFND!3F@!3B!3@$#19!171?A?G?a?I?a?g?a?I?a?g?a?I?a?g?a?I?a?g?a?I?a?g?a?I?a?g?a?G?a?G?_!75?a?G?A?G?A!3? 82 | A$-#31!173~}wo!104?!3B!2@!18?!3@!35?_!2w}!207~^NFbr!2x{w_?C[{!2xrbFN^!67~$#25!173?@!2F~\~v~\~v~T~v~\~v~T~v~\~v~T 83 | ~v~\~v~T~v~\~v~T~v~\~v~T~v~\~v~\~sw$#19!175?G?a?G?a?G?i?G?a?G?i?G?a?G?i?G?a?G?i?G?a?G?i?G?a?G?i?G?a?G?a?G$#17!560?E]}w_$-#31!178~{ 84 | o!156?!2_w{}!209~F@W]!3^!5~}wopbBF^~}w@F!65~$#25!178?BL~t~\~V~\~t~\~V~\~t~\~V~\~t~\~V~\~t~\~V~\~t~\~V~\~t~\~v~\~v{O$#19!179? 85 | A?I?a?g?a?I?a?g?a?I?a?g?a?I?a?g?a?I?a?g?a?I?a?g?a?I?a?G?a?G?_$#17!552?!5_!5?@FNM[{w_$-#31!181~}!2{!2wo!2_!142?_ow{}!214~ 86 | o?M!2}!2wx!2pr!2b!2F!2^}!3{wG?o!65~$#25!181?@!2BFDNV^T~v~\~v~T~v~\~v~T~v~\~v~\~v~\~v~\~v~\~v~T~v~\N!2FD!2F 87 | $#17!552?!3@!2FE!2MK!2[!2w!2_@!3B!2F!2E$#19!185?A?G?i?G?a?G?i?G?a?G?i?G?a?G?a?G?a?G?a?G?a?G?i?G?A!3?A$-#31!191~ 88 | }!2{!3w!2o!3_!63?!5A!12?!2Oo!38?_ow!2{}!223~{wpbf!2N!4^!2]W?@Frpw{!67~$#25!191?@B@FD!2FN\^T~\~V~D!3F 89 | D!3FD!2FNLNFN\^V~\~v~@$#19!193?A?A?G?A?I?a?g?A!3?A!3?A!3?A?G?A?G?a?G?A$#17!563?!2@F~}o$-#31!205~}!116?y!236~!7}!74~$#25 90 | !205?@!23?@$-#31!206~!24?@!5?G!9?!3_!70?ow!319~$-#31!206~w_!11?W[{}o_!23?!2@AEKWwo!2_!4?[{}!9~!2}!2{!37?C[ 91 | }!322~$-#31!208~{o!12?@BCO!2?A?!2C!2G!18?CGO`@B!2FM[wo`B!2FB!5@B!2FK!3?!2o!3_!31?!323~$-#31!211~}wo!13?EO!7? 92 | !2O?GL!4E!2N!9?@!2?G!2O?BFNM!14?F!6~}!2{wo_!23?@^!20~!3^!2N!3F!2N^!290~$#25!269?K[s}[}s{Wo!2_$#19!271? 93 | G?a?G?_$-#31!216~{wo_!8?GN]M?@!49?F^!9~}{o!22?FN!2^!6~!5^NFB@!12?!2@B!2zBN!283~$#25!272?@BFD!2FN\^s$#19!275?A?G 94 | ?A?G$-#31!221~}{wo!2_!13?!2_o_!41?@N^!10~{o_!19?w{wo?!3@!26?Bxw!283~$#25!327?AE[}u{Wo$#19!329?a?G?_$-#31!228~!2}!2{!4w 95 | {!2}!9~!3}!3{!2w!2o!2_!10?_?O!2?GC!12?BF^!9~{wo!15?o!2~^N!30?_r!284~$#25!328?ot^\NF@$#19!327?_?I?A$-#31!262~ 96 | }C?A?!2@!24?F!11~}{wo!7_o!2w!12{!3wo!7O!11?!287~$#25!326?!2@$-#31!262~|o_!4?!2Oo?!2G!2C?@BV!9?!2ow!43~ 97 | }{c!5C!9?@^!285~$-#31!267~!2}!2{w!2op`_A?@BF!4?_w{}!50~p!4PO!7?EoB^!283~$-#31!281~!3}!57~!5C!9?E?B!282~$-#31 98 | !325~!2^NF!9B!3F!2HP`@!12?!13~FN!2~^Nf~!2^Nn!257~$-#31!319~^NFB@!21?@AC!8?_oBN!9~B_}{`@w`K!2]!260~$-#31!318~p 99 | !6o?O!3oO?!17o!2wsu!3wxw{!10~}!4~}!4~!3}{xz!2r!253~$-#31!325n!2mklkm!309n$- -------------------------------------------------------------------------------- /testfiles/sampsa1.sixel: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jerch/node-sixel/3f933dbadcaed5ab90b749c032433b394bd0b289/testfiles/sampsa1.sixel -------------------------------------------------------------------------------- /testfiles/space.six: -------------------------------------------------------------------------------- 1 | [?80hP0;0;0;q"1;2;640;350 2 | #15;2;100;100;100@$-#6;2;0;50;50!440?_?_OgOgSiSiSiTiTiTiTiTiTiSiSiSgOgO_?_$#1;2;50;0;0!441?_OgOgSgSiSiSiTiTIT 3 | iTiTiSiSiSgSgOgO_$#15!206?@!14?A$-#6!434?_OgSiTiTiTiTAPiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiSgO_$#1!435?gSiSiTiTiTiDaPiTiTiPgOgOgPiTi 4 | TiTiTiTiTiTiTiSiSg$#15!207?G$-#6!430?_OiTiTiTiTiTiTiTiSaTaPaDiTiTiTiTiTIDIDADA@A@A@?@?@?@?@$#1!430?OgSiTiTaOiTiTiTiSiDaPaDiTiTiT 5 | iTiTITIDADADA@A@?@?@?@?@$#15!565?AFA!48?C$#7;2;50;50;50!39?O!397?GD$-#1!430?TiTiTiTiTiTiTiTiTiTiTiTiTIDADA@$#6!430?iTiTi 6 | TiTiTiTiTiTiTiTiTiTiTIDA@$#15!550?OgO!55?@$#7!44?G!526?_?_$-#1!430?DiTiTiTiT_OiTiTiTiTiTA@A@$#6!430?ATiTiTiTi?gTiTiTiTiTi@A@$#7 7 | !49?_!522?@$-#6!432?ADITiTiTiTiTiTiTiTA$#1!432?@ADiTiTiTiTiTiTiTi@$-#6!437?@ADIDITiTiTiT_$#1!436?@A@ADITiTiTiTi$#15!574?DAD$#7!49? 8 | C$-#6!447?@A@A$#1!446?@?@A@$-#15!431?A$-#15!105?ICI!59?AFA$#7!49?G!25?O!5?A$-#7!56?G!9?G$-#11;2;100;100;0!314?[$#15!422?C$-#14;2;0;100;100 9 | !306?_w{}!4~!2F!3~!2}{w$-#14!304?_{!6~^B!2?B^!6~w_$#11!313?_Sg$#7!120?@!2?C$#5;2;50;0;50!314?gS_$-#14!302?o{ 10 | !3~!2^!2NB!6?B!2N!2^!3~{o$#11!309?_?_SIDATgO_$#5!310?_OgTADIS_?_$#7!128?@$-#14!300?o!2~r!2`_o!2{!3}!4~!2} 11 | !3{o_!2`r~{$#5!304?GCI@?@!9?@?@ACGC$#11!304?CGCA@?@!9?@?@ICG$#15!484?@$#7!128?@$-#14!304?!7@!6B!8@$#7!128?@!168?owF!30? 12 | @qsw$-#6!300?!2~{!11~?!12~{!2~$#7!132?!2@!6?@!155?}!2~!32?!3~{$#15!393?@E$-#6!300?!2nN!2n!2^!7~?!2~!2x 13 | !6}]}N^~$#7!295?!4~!32?!4~}$#15!538?A$-#6!300?!5~@?!7~?!9~}!2^!3N$#7!294?o!4~!6?}!25?!6~_$-#6!300?!2~r!2~ 14 | }{~Bp!4~?!2|!4{K!4{~r!2~$#7!293?{!5~!6?@!2?{M!21?!7~$-#6!300?!14~?!2~!2^!2~{!8~$#7!292?}!6~!32?!7~ 15 | {$#15!389?o!50?!2C$-#6!300?!2~w!11~?!12~wN~$#7!290?_!8~!32?!9~_$#15!388?@!94?@$-#6!300?!2~vFn!4^!2nV!2~?!5~ 16 | !2^!4nNno~$#7!287?!2_[!9~!32?!7~!2^nk!2_$-#7!279?!2_ow!2{}!3~}!2|zf^!4~!32?!4~^fz!2}!4~!2}{!2w 17 | o_$#6!300?!3~}!7~?!2~?!5~!2}!4~?!3~$-#7!270?_o!2w!2{]^nv~z|!2}~!2^!7~b^!2~!32?!2~b{!7~|!2r!2n 18 | !2^!5~!2}!2wo!2_$#6!50?_OG?GSgSgCGCG!237?!3zb!2v!3n!3v!2zw!2z!3v!3n!3vobBz$#1!44?_?_?_!2?_OGCg 19 | SgSGCGC!5?_?_?_$#15!52?_?O!4?o?!2OoG!2?_!332?AE$#11!53?_!6?O_?GO_$-#7!260?!2_ow!2{}]~^nv~z|}~^~nvz~!2|}!10~{B~ 20 | !32?~N!16~}|z!2~zv!2n^!4~}!2{wo!2_$#6!44?_OgSa@_!4?@A@ACG!7?_O?O!228?!13~NK!2N!7~B!3~o~$#1!14?_?_O_Og 21 | SgSgSGSGSICICIDADADADADQ`Ub@_@!4?A@ADG!5?@?_O`QLAdADADADICICICISGSGSgOgO_O_O_$#2;2;0;50;0!15?_?_OgOgSgSGSGSICICIc 22 | IdAdAdAdADA@!19?@!3?@ADADAdAdAdIcICISGSGSgSgOgO_O_$#15!43?_GK!2?OC?hseAsk?W`D@UYGse?@KC?w?_$#11!47?GCISA@GTGOg?AOi@_TIOIC 23 | A?_$#5!48?GOISIPg!2?S_OaSgDa?GTI?g$-#7!251?!2_o!2w{}]nv~!2z~|}~^!2~nvz~|E!8F!10~^~{~!32?~{!9~F!2~F~f!2Vv~^ 24 | !2F^!3~}!2|!2z!2v!2n^!2~}!2{w!2o!2_$#6!41?SiSiTITaTaO_O_O!2?IDID!4?gTI@!3?TiS!224?nN^X!8^~!4?~!6^ 25 | ?!2^X^N$#2!7?_OgSiTIDA`Q`OhSGCIcYkQHUHEHCJCBCBCB!33?@?B?BCBC@EHEHQKQcAcGCHQ`QdACISgO_$#1!8?gSiSIDADA@A@!4?_O_OgSGCGCI 26 | CICACAgSiTiTITaT_O_O_!2?DIDI!3?_OiD!4?gTiA?ACACACGCGCGO_O_!2?@?@A@ADISgOgO_!192?_!27?_$#10;2;0;100;0!17?_OgOGS 27 | GCICA@A@A@A@?@?@?@!35?@?@?@?@A@A@A@ACACGCGOGO_?_$#15!42?@!4?_G?GKHA!2G_@_oO_T@PM!3?KWboA!203?_?_O_O_$#5!43?@!2?_!4? 28 | @ADADIS!2?_OiSiPA?_OaSI!3?g$#11!49?G?ACGDATiO!4?iC?D?OaDGD!3?O$#4;2;0;0;50!314?!2~$#9;2;100;0;0!276?_ 29 | ?_WgWgWw$#12;2;0;0;100!276?!3W$-#7!243?_!2ow{!2}!2n~!2z~|}!9~Mv!7~}Mu!6}!2~Nv!2~!2r!2|}!3~!32? 30 | !5~vN!4~{!2|{~!3|{~sN~{!7~vN!4~}|!9~}!2{wo$#1!7?DITiSgO_!9?ADISGOgO_O_O_?_?_?dADA!2?@A@A@A@a?_OgC_SaPa@_ 31 | !2?CGSaPI!4?_?_OgOgOgOGSID!6?_?_OgTiTA!191?TiTiSiSiSiSiS!4?gSiSiSiSiSITi$#6!42?ADAD!2?A@A@A@a@_?_O_CaOa@a@!2?GCiD 32 | _T!224?iTITiSiSiSiSh!4?TiSiSiSiSiTiT$#2!7?ATiTgO_?_ITITgOgOdIdISgO_O_O_?_?_?_!37?_?_O_OgOgOGsGdQ_OgSiTIdOgSiTID$#15!41?^!2? 33 | WGBPOS!2?K!2?G?]MDXY@L?[CEAVPA!3?_!98?O$#5!42?GO_OgCg?gS_SGSI@!2?A@G?G?GOg?aP?OI?I$#11!42?Og?_SiCgSgOgS?T?@A!4?C?O 34 | GTg?_@GC?D$#10!16?DITiTgOgO_?_!61?_?_O_OgOgTITI$#9!276?!9@$#4!314?!2~$-#2!10?@?@ADIDITIShShUhQhQlQdQdZcJcJcJcJsHUHUHUHUHU 35 | HUhUhUhUHUHUHUHUHuHcJcJcJcZcZdQdQlQhQhUhSjSIDIDADA@$#1!11?@ADADIDISiSgSgOgOgO_O_O_P_@_@_@_@a`A`A`A`A`A@A@Q@A@A`A`A`A`A@q@_@_@ 36 | _@_P_O_OgOgOgSgSgSITIDIDA@?@!194?TiOiTATiTITiT!4?iTATiTiTiTgTi$#10!22?@?@A@ADADADACICICICISISGSGSGSGSGSgSgCgSgSGSGSGSGSGCICI 37 | CICICICIDADADA@A@A@$#6!46?_?_?_?_?_!7?_?_?_?_?_!229?iTgTiDiTiTiTi!4?Ti@iTiTITiOiT$#4!314?!2~$-#1!25?@?@?@A@A@ADADADADAD 38 | IDIDIDYdYdYdYdYDIDIDIDIDIDIDADADADADA@A@A@A@?@?@!207?iDITITiTiTGTiD!4?iDITITiSiTITiC$#2!24?@?@?@A@A@A@ADADADADADIDIDIDIDIDI 39 | DIDIDIDIDIDIDIDIDADADADADA@A@A@?@?@$#6!51?O_O_O_?_!240?CiTITiSiTIOIDi!4?DiTITiTgTITIDi$#4!314?!2~$-#6!55?@?@?@!238?iTaDIP 40 | iSiOIPiTi!4?TiTgPaSiSID_TiS$#1!54?@?@?@!239?SaDITaSiSAPgTiT!4?iTiP_CiSiTG@aTi$#7!303?K!3?KCE!10?EMW!4?AE$#4!314?!2~ 41 | $#15!60?!3@$-#6!299?@ADACADACADA@!7?@A@?DADADA@$#1!300?@ACADADACA@!7?@AD?@AD?CADA@$#9!303?!5G!14?!5G$#4!314?!2^$#15 42 | !487?C$--#14!303?C_!2?CACAC!12?GO_?a$#6!304?CG?AO_O_!12?OI!2?@AO$#11!308?G!18?G$#9!307?@!18?A$-#6!304?OI!2?P!15?PG!5?G 43 | $#14!304?_?A!2?DAO!16?AO$-#14!307?@A?_!13?_O!4?I$#9!311?G!14?C$#6!306?D_!3?_!16?O$-#14!305?@!2?IO?C!11?@A!2?@!3?C$#6 44 | !304?@!2?I?AC_!14?@$-#6!304?C?O!16?_?_!4?S$#14!304?_?_?a!19?_?_$#11!307?A?O$-#6!303?_!3?aC!14?AOG?AOG?G$#14!302?_?_@?O!15? 45 | @_?G!3?I$#11!329?C$-#14!308?g!17?A?G?G$#6!305?A!20?C!3?OG$#9!329?@$-#14!307?@G!17?_!3?G!2?C$#11!305?GC!21?A$#6!307?AC!22? 46 | g$-#14!300?AC!2?G!22?C_!4?@_$#6!303?A!22?@!3?PA?G$-#15!518?!2_!2o?_!4w!2{[Coy!5}M!2]Q!5uU}!2{[!2{!4w!3o 47 | _$#6!298?OI?_O!4?A!21?a!4?S!187?oO!6?_wKC!5?o!2_k!5Gg!3?_$#14!300?g@a!4?@!21?C_?I!231?_$-#15!518?B!3FC!2D!3F 48 | !2N!4J!2NL!3NF^!2}]M!3m!2~|{~^\LJBNB@$#14!298?A!2?@IOA!26?@!10?_!165?_OgOgSiSiTgOgOgOgOgO_O_O_O_O_O_Og!13?_? 49 | _O_OgSiTiTiSiSgOgO_$#12!507?_?_OgSgSiTiSgOgOgOgOgO_O_O_O_O_O_O_!13?_O_O_SiTiTiTiSgSgO_?_$#6!298?CGCGO_D?D!23?@!6?_!184?B!2A 50 | !5?!4C!2?A!5?!2@`p!3P!2?AB!2?!2ACK$#7!504?@B@$-#12!501?_OgSiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiSiSiSiSiTiTiTi 51 | TiTiTiTiTiTiTiTiTiTiTiSgO_$#14!335?@?O!5?O_!157?_OgSiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiSiSiSiSiTiTiTiTiTiTiTiTiTITiTiTITiSg 52 | O_$#15!542?!2@?!5@!18?_!5?_$#6!339?_?GS_?_?_!196?@$-#14!338?AD_@_SAO!154?ADADADADADITiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTi 53 | TiTiTiTiTITiTGOiDiTi@iPiTaTiTiTiTiSgO_$#12!499?ADADADADADADITiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTITaTATi@ACiDaSaDiTiT 54 | iTiTiSg$#1!496?_w!2{!10wo_$#15!558?!2_?Gal?OSgPSOK@GW$#6!339?AP?O?PA$-#12!498?CE!9?_OgSiTGTaPADADITITITIDIDIDIDIDIDIdIDADA 55 | @A@aTiTiTGDiDaSa@AC?C_?I?aTiTiTiTiTiTiTiO_$#14!508?_OgSgSgTgPiDADITITITITIDIDIDIDIDIDIDIDADA@A@iTiTgTITgTASa?APgCA@_SiDiTiTiTiT 56 | iTiSg$#1!493?_w}!2~zx!7~!2^NF!2B@!3AKC!3wo!7_!12oO!2o!3w!3{[$#15!45?DAD!3?!3CO?CO!449?_!7?_!40?Aao 57 | ?QGhH[|xmRZ|s^H?O$-#1!492?w!2~Nn!6~}{U^@!12?F!20~}~|}!3~B@$#12!495?oO!8?_?gCiTaTITITiTiO!20?@?A@!3?SiTiTiTiTiSiSaDa 58 | TgDgDiSgTiTiTiDiTaTiTiTiTiO$#14!506?_SiTiTiTiTaTiTg!27?gSiTiTiTiTgSi@iPiPiTiTaSATiTiTiTaTITaTiTiTg$#15!29?IcI?Q?Y!2_!8?ACA 59 | @?A!2?@!448?@BH?AP!2?G?_?_G!41?B!2@[OK?EOAO?HBg!5?O?!2G_?G$-#1!492?~^!2N!2FB!2@!20?BFN!2^!19~|LB$#14!494?_O 60 | gOgSiTiCGPaPgTiT_TGTiTiTiSgO_!21?_SiTiTiTiTiTiTiTiTiTiTiTiTISiTiDiDiDiSiDiTiTi$#12!493?_O_OgSiSgTi@gPgTaPiTiTiTiTiTgO_?_!21?G 61 | SiP_SgTGDiTiTiTiTiTiTiTiTiTiTaPaTITgPiTiTiT$#15!12?O!8?G!3?GAPH!3?!2B!7?AFA!2?G!2?A!5?ADA!4?A!438?A?PuEKEAGC 62 | ?I?a!30?AQ_@?CI@A?aO!15?_@!3?WCW?o?BCO$-#12!492?@iTiTiTiTiTiTiTiTgPgPiTiTiTiTiTiTiSgO!2?iSiTiTiTiTiSgO_@aDiSiDaTaTiTiTiTiT 63 | iTiTiTiTiTiTiTiTiSiSiTiTi@$#14!492?ATiTiTiTiTiTgTiTiSiTiSiTiTiTiTiTiTiO_!2?SiTiTiTiTiTiS_?aD_TiSiPgTiTiTiTiTiTiTiTiTiTiSiTiTg 64 | TiTiSiTiTA$#15!13?A!3?O!2?@A@G?G!2?GA?_!17?DA!12?ICI!438?A!4?BCAC@!34?A?!2@OKAG!19?@!4?A?@?!2@$#1!526?@FN!2~ 65 | !2@!9?@BN^[!2W$-#12!494?@ITiTiTiTiTiTiTiTiTiTgTiTiTiTiTiTiTiO?DIDIDIDADADADADADADADITiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTI@$#14 66 | !494?ADiTiTiTiTiTiTiTiTiTiPgTITiTiTiTiTiSgPADIDIDIDADADADAD?DADIDiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiDA$#1!515?EA?_!10?@Fmw!6o 67 | !9wy!3w!2o$#15!10?ICI!2?O!5?D?D!2?@!3?_!11?O!7?_O_!2?GC!7?C!34?K!3?!2_!11?_!371?_$#9!22?A$-#12!497?A 68 | DITiTiTiTiTiTiTiTiSIDIDiTiTiTiTiTiSgO_O_!15?gTiTiTiTiTiTiTiTiTiTiTiTiTiTiTIDA$#14!497?@ADITiTiTiTiTiTiTiTiTiDiDiTiTiTiTiSgOgO_ 69 | !16?OiTiTiTiTiTiTiTiTiTiTiTiTiTiTIDA@$#1!516?@!14?@B!2FJF^~n^zv~^n~n!5~F$#15!16?@!4?ADA!6?g?W@?_oAGBaA?C?@E!4?@!8? 70 | A@!35?@!4?G!4?CG!4?C!4?G!398?_Oo!2O!13?CG!2?O_CG?_O?O$#7!11?DAD!473?@?@$#9!22?A$-#14!503?@ADITiTiTiTiTgTiPiTiTiT 71 | iTiTiTiTiTiTiCg!11?iTiTiTiTiTiTiTiTiTiTiTiTIDA@$#12!502?@ADITiTiTiTiTiOaDg@_PgSiTiTiTiTiTADiTi!11?_SiTiTiTiTiTiTiTiTiTiTiTiTI 72 | DA@$#1!539?@DZj!2^~^}!3~^@$#15!26?O_?C@!2?!2@M?K!2?K?G!2?AG!48?AOA!2?o!2?O!2?@!409?FGOESICA@!10?gO!2?OQc 73 | S!2_?_@$-#14!509?@A@ADIDITiTiTiTiTiTiTiTiTiTiTiTiO_?_?_OgSiTiTiTiTiTiTiTiTIDIDA@A@$#12!510?@ADADITITiTiTiTiTiTiTiTiTiTiSiTgO_?_ 74 | ?_OiTiTiTiTiTiTiTiTITIDADA@$#1!541?FN!4^NF@$#15!36?!2CD!4?_!4?C!2?G!2?O!44?@!18?@!419?@$-#14!521?@?@A@A@ADA@A@A@A@ 75 | A@A@ADADADI@A@ADA@A@A@?@$#12!58?O!463?@?@A@ADA@A@A@A@A@A@ADADADA@I@ADADA@A@?@$#15!34?C!2?A!7?O!484?!3C!9K!6G!4C$#7 76 | !57?OgO!559?_!3?{?wC!2cg?CcSKC$-#7!619?@!3A@?@!3A@?B!4A$#15!34?_!6?_O_!3?C!4?DAD!2?O$-#15!42?@!6?a$-\ 77 | -------------------------------------------------------------------------------- /testfiles/space_clean.six: -------------------------------------------------------------------------------- 1 | "1;2;640;350 2 | #15;2;100;100;100@$-#6;2;0;50;50!440?_?_OgOgSiSiSiTiTiTiTiTiTiSiSiSgOgO_?_$#1;2;50;0;0!441?_OgOgSgSiSiSiTiTIT 3 | iTiTiSiSiSgSgOgO_$#15!206?@!14?A$-#6!434?_OgSiTiTiTiTAPiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiSgO_$#1!435?gSiSiTiTiTiDaPiTiTiPgOgOgPiTi 4 | TiTiTiTiTiTiTiSiSg$#15!207?G$-#6!430?_OiTiTiTiTiTiTiTiSaTaPaDiTiTiTiTiTIDIDADA@A@A@?@?@?@?@$#1!430?OgSiTiTaOiTiTiTiSiDaPaDiTiTiT 5 | iTiTITIDADADA@A@?@?@?@?@$#15!565?AFA!48?C$#7;2;50;50;50!39?O!397?GD$-#1!430?TiTiTiTiTiTiTiTiTiTiTiTiTIDADA@$#6!430?iTiTi 6 | TiTiTiTiTiTiTiTiTiTiTIDA@$#15!550?OgO!55?@$#7!44?G!526?_?_$-#1!430?DiTiTiTiT_OiTiTiTiTiTA@A@$#6!430?ATiTiTiTi?gTiTiTiTiTi@A@$#7 7 | !49?_!522?@$-#6!432?ADITiTiTiTiTiTiTiTA$#1!432?@ADiTiTiTiTiTiTiTi@$-#6!437?@ADIDITiTiTiT_$#1!436?@A@ADITiTiTiTi$#15!574?DAD$#7!49? 8 | C$-#6!447?@A@A$#1!446?@?@A@$-#15!431?A$-#15!105?ICI!59?AFA$#7!49?G!25?O!5?A$-#7!56?G!9?G$-#11;2;100;100;0!314?[$#15!422?C$-#14;2;0;100;100 9 | !306?_w{}!4~!2F!3~!2}{w$-#14!304?_{!6~^B!2?B^!6~w_$#11!313?_Sg$#7!120?@!2?C$#5;2;50;0;50!314?gS_$-#14!302?o{ 10 | !3~!2^!2NB!6?B!2N!2^!3~{o$#11!309?_?_SIDATgO_$#5!310?_OgTADIS_?_$#7!128?@$-#14!300?o!2~r!2`_o!2{!3}!4~!2} 11 | !3{o_!2`r~{$#5!304?GCI@?@!9?@?@ACGC$#11!304?CGCA@?@!9?@?@ICG$#15!484?@$#7!128?@$-#14!304?!7@!6B!8@$#7!128?@!168?owF!30? 12 | @qsw$-#6!300?!2~{!11~?!12~{!2~$#7!132?!2@!6?@!155?}!2~!32?!3~{$#15!393?@E$-#6!300?!2nN!2n!2^!7~?!2~!2x 13 | !6}]}N^~$#7!295?!4~!32?!4~}$#15!538?A$-#6!300?!5~@?!7~?!9~}!2^!3N$#7!294?o!4~!6?}!25?!6~_$-#6!300?!2~r!2~ 14 | }{~Bp!4~?!2|!4{K!4{~r!2~$#7!293?{!5~!6?@!2?{M!21?!7~$-#6!300?!14~?!2~!2^!2~{!8~$#7!292?}!6~!32?!7~ 15 | {$#15!389?o!50?!2C$-#6!300?!2~w!11~?!12~wN~$#7!290?_!8~!32?!9~_$#15!388?@!94?@$-#6!300?!2~vFn!4^!2nV!2~?!5~ 16 | !2^!4nNno~$#7!287?!2_[!9~!32?!7~!2^nk!2_$-#7!279?!2_ow!2{}!3~}!2|zf^!4~!32?!4~^fz!2}!4~!2}{!2w 17 | o_$#6!300?!3~}!7~?!2~?!5~!2}!4~?!3~$-#7!270?_o!2w!2{]^nv~z|!2}~!2^!7~b^!2~!32?!2~b{!7~|!2r!2n 18 | !2^!5~!2}!2wo!2_$#6!50?_OG?GSgSgCGCG!237?!3zb!2v!3n!3v!2zw!2z!3v!3n!3vobBz$#1!44?_?_?_!2?_OGCg 19 | SgSGCGC!5?_?_?_$#15!52?_?O!4?o?!2OoG!2?_!332?AE$#11!53?_!6?O_?GO_$-#7!260?!2_ow!2{}]~^nv~z|}~^~nvz~!2|}!10~{B~ 20 | !32?~N!16~}|z!2~zv!2n^!4~}!2{wo!2_$#6!44?_OgSa@_!4?@A@ACG!7?_O?O!228?!13~NK!2N!7~B!3~o~$#1!14?_?_O_Og 21 | SgSgSGSGSICICIDADADADADQ`Ub@_@!4?A@ADG!5?@?_O`QLAdADADADICICICISGSGSgOgO_O_O_$#2;2;0;50;0!15?_?_OgOgSgSGSGSICICIc 22 | IdAdAdAdADA@!19?@!3?@ADADAdAdAdIcICISGSGSgSgOgO_O_$#15!43?_GK!2?OC?hseAsk?W`D@UYGse?@KC?w?_$#11!47?GCISA@GTGOg?AOi@_TIOIC 23 | A?_$#5!48?GOISIPg!2?S_OaSgDa?GTI?g$-#7!251?!2_o!2w{}]nv~!2z~|}~^!2~nvz~|E!8F!10~^~{~!32?~{!9~F!2~F~f!2Vv~^ 24 | !2F^!3~}!2|!2z!2v!2n^!2~}!2{w!2o!2_$#6!41?SiSiTITaTaO_O_O!2?IDID!4?gTI@!3?TiS!224?nN^X!8^~!4?~!6^ 25 | ?!2^X^N$#2!7?_OgSiTIDA`Q`OhSGCIcYkQHUHEHCJCBCBCB!33?@?B?BCBC@EHEHQKQcAcGCHQ`QdACISgO_$#1!8?gSiSIDADA@A@!4?_O_OgSGCGCI 26 | CICACAgSiTiTITaT_O_O_!2?DIDI!3?_OiD!4?gTiA?ACACACGCGCGO_O_!2?@?@A@ADISgOgO_!192?_!27?_$#10;2;0;100;0!17?_OgOGS 27 | GCICA@A@A@A@?@?@?@!35?@?@?@?@A@A@A@ACACGCGOGO_?_$#15!42?@!4?_G?GKHA!2G_@_oO_T@PM!3?KWboA!203?_?_O_O_$#5!43?@!2?_!4? 28 | @ADADIS!2?_OiSiPA?_OaSI!3?g$#11!49?G?ACGDATiO!4?iC?D?OaDGD!3?O$#4;2;0;0;50!314?!2~$#9;2;100;0;0!276?_ 29 | ?_WgWgWw$#12;2;0;0;100!276?!3W$-#7!243?_!2ow{!2}!2n~!2z~|}!9~Mv!7~}Mu!6}!2~Nv!2~!2r!2|}!3~!32? 30 | !5~vN!4~{!2|{~!3|{~sN~{!7~vN!4~}|!9~}!2{wo$#1!7?DITiSgO_!9?ADISGOgO_O_O_?_?_?dADA!2?@A@A@A@a?_OgC_SaPa@_ 31 | !2?CGSaPI!4?_?_OgOgOgOGSID!6?_?_OgTiTA!191?TiTiSiSiSiSiS!4?gSiSiSiSiSITi$#6!42?ADAD!2?A@A@A@a@_?_O_CaOa@a@!2?GCiD 32 | _T!224?iTITiSiSiSiSh!4?TiSiSiSiSiTiT$#2!7?ATiTgO_?_ITITgOgOdIdISgO_O_O_?_?_?_!37?_?_O_OgOgOGsGdQ_OgSiTIdOgSiTID$#15!41?^!2? 33 | WGBPOS!2?K!2?G?]MDXY@L?[CEAVPA!3?_!98?O$#5!42?GO_OgCg?gS_SGSI@!2?A@G?G?GOg?aP?OI?I$#11!42?Og?_SiCgSgOgS?T?@A!4?C?O 34 | GTg?_@GC?D$#10!16?DITiTgOgO_?_!61?_?_O_OgOgTITI$#9!276?!9@$#4!314?!2~$-#2!10?@?@ADIDITIShShUhQhQlQdQdZcJcJcJcJsHUHUHUHUHU 35 | HUhUhUhUHUHUHUHUHuHcJcJcJcZcZdQdQlQhQhUhSjSIDIDADA@$#1!11?@ADADIDISiSgSgOgOgO_O_O_P_@_@_@_@a`A`A`A`A`A@A@Q@A@A`A`A`A`A@q@_@_@ 36 | _@_P_O_OgOgOgSgSgSITIDIDA@?@!194?TiOiTATiTITiT!4?iTATiTiTiTgTi$#10!22?@?@A@ADADADACICICICISISGSGSGSGSGSgSgCgSgSGSGSGSGSGCICI 37 | CICICICIDADADA@A@A@$#6!46?_?_?_?_?_!7?_?_?_?_?_!229?iTgTiDiTiTiTi!4?Ti@iTiTITiOiT$#4!314?!2~$-#1!25?@?@?@A@A@ADADADADAD 38 | IDIDIDYdYdYdYdYDIDIDIDIDIDIDADADADADA@A@A@A@?@?@!207?iDITITiTiTGTiD!4?iDITITiSiTITiC$#2!24?@?@?@A@A@A@ADADADADADIDIDIDIDIDI 39 | DIDIDIDIDIDIDIDIDADADADADA@A@A@?@?@$#6!51?O_O_O_?_!240?CiTITiSiTIOIDi!4?DiTITiTgTITIDi$#4!314?!2~$-#6!55?@?@?@!238?iTaDIP 40 | iSiOIPiTi!4?TiTgPaSiSID_TiS$#1!54?@?@?@!239?SaDITaSiSAPgTiT!4?iTiP_CiSiTG@aTi$#7!303?K!3?KCE!10?EMW!4?AE$#4!314?!2~ 41 | $#15!60?!3@$-#6!299?@ADACADACADA@!7?@A@?DADADA@$#1!300?@ACADADACA@!7?@AD?@AD?CADA@$#9!303?!5G!14?!5G$#4!314?!2^$#15 42 | !487?C$--#14!303?C_!2?CACAC!12?GO_?a$#6!304?CG?AO_O_!12?OI!2?@AO$#11!308?G!18?G$#9!307?@!18?A$-#6!304?OI!2?P!15?PG!5?G 43 | $#14!304?_?A!2?DAO!16?AO$-#14!307?@A?_!13?_O!4?I$#9!311?G!14?C$#6!306?D_!3?_!16?O$-#14!305?@!2?IO?C!11?@A!2?@!3?C$#6 44 | !304?@!2?I?AC_!14?@$-#6!304?C?O!16?_?_!4?S$#14!304?_?_?a!19?_?_$#11!307?A?O$-#6!303?_!3?aC!14?AOG?AOG?G$#14!302?_?_@?O!15? 45 | @_?G!3?I$#11!329?C$-#14!308?g!17?A?G?G$#6!305?A!20?C!3?OG$#9!329?@$-#14!307?@G!17?_!3?G!2?C$#11!305?GC!21?A$#6!307?AC!22? 46 | g$-#14!300?AC!2?G!22?C_!4?@_$#6!303?A!22?@!3?PA?G$-#15!518?!2_!2o?_!4w!2{[Coy!5}M!2]Q!5uU}!2{[!2{!4w!3o 47 | _$#6!298?OI?_O!4?A!21?a!4?S!187?oO!6?_wKC!5?o!2_k!5Gg!3?_$#14!300?g@a!4?@!21?C_?I!231?_$-#15!518?B!3FC!2D!3F 48 | !2N!4J!2NL!3NF^!2}]M!3m!2~|{~^\LJBNB@$#14!298?A!2?@IOA!26?@!10?_!165?_OgOgSiSiTgOgOgOgOgO_O_O_O_O_O_Og!13?_? 49 | _O_OgSiTiTiSiSgOgO_$#12!507?_?_OgSgSiTiSgOgOgOgOgO_O_O_O_O_O_O_!13?_O_O_SiTiTiTiSgSgO_?_$#6!298?CGCGO_D?D!23?@!6?_!184?B!2A 50 | !5?!4C!2?A!5?!2@`p!3P!2?AB!2?!2ACK$#7!504?@B@$-#12!501?_OgSiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiSiSiSiSiTiTiTi 51 | TiTiTiTiTiTiTiTiTiTiTiSgO_$#14!335?@?O!5?O_!157?_OgSiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiSiSiSiSiTiTiTiTiTiTiTiTiTITiTiTITiSg 52 | O_$#15!542?!2@?!5@!18?_!5?_$#6!339?_?GS_?_?_!196?@$-#14!338?AD_@_SAO!154?ADADADADADITiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTi 53 | TiTiTiTiTITiTGOiDiTi@iPiTaTiTiTiTiSgO_$#12!499?ADADADADADADITiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTITaTATi@ACiDaSaDiTiT 54 | iTiTiSg$#1!496?_w!2{!10wo_$#15!558?!2_?Gal?OSgPSOK@GW$#6!339?AP?O?PA$-#12!498?CE!9?_OgSiTGTaPADADITITITIDIDIDIDIDIDIdIDADA 55 | @A@aTiTiTGDiDaSa@AC?C_?I?aTiTiTiTiTiTiTiO_$#14!508?_OgSgSgTgPiDADITITITITIDIDIDIDIDIDIDIDADA@A@iTiTgTITgTASa?APgCA@_SiDiTiTiTiT 56 | iTiSg$#1!493?_w}!2~zx!7~!2^NF!2B@!3AKC!3wo!7_!12oO!2o!3w!3{[$#15!45?DAD!3?!3CO?CO!449?_!7?_!40?Aao 57 | ?QGhH[|xmRZ|s^H?O$-#1!492?w!2~Nn!6~}{U^@!12?F!20~}~|}!3~B@$#12!495?oO!8?_?gCiTaTITITiTiO!20?@?A@!3?SiTiTiTiTiSiSaDa 58 | TgDgDiSgTiTiTiDiTaTiTiTiTiO$#14!506?_SiTiTiTiTaTiTg!27?gSiTiTiTiTgSi@iPiPiTiTaSATiTiTiTaTITaTiTiTg$#15!29?IcI?Q?Y!2_!8?ACA 59 | @?A!2?@!448?@BH?AP!2?G?_?_G!41?B!2@[OK?EOAO?HBg!5?O?!2G_?G$-#1!492?~^!2N!2FB!2@!20?BFN!2^!19~|LB$#14!494?_O 60 | gOgSiTiCGPaPgTiT_TGTiTiTiSgO_!21?_SiTiTiTiTiTiTiTiTiTiTiTiTISiTiDiDiDiSiDiTiTi$#12!493?_O_OgSiSgTi@gPgTaPiTiTiTiTiTgO_?_!21?G 61 | SiP_SgTGDiTiTiTiTiTiTiTiTiTiTaPaTITgPiTiTiT$#15!12?O!8?G!3?GAPH!3?!2B!7?AFA!2?G!2?A!5?ADA!4?A!438?A?PuEKEAGC 62 | ?I?a!30?AQ_@?CI@A?aO!15?_@!3?WCW?o?BCO$-#12!492?@iTiTiTiTiTiTiTiTgPgPiTiTiTiTiTiTiSgO!2?iSiTiTiTiTiSgO_@aDiSiDaTaTiTiTiTiT 63 | iTiTiTiTiTiTiTiTiSiSiTiTi@$#14!492?ATiTiTiTiTiTgTiTiSiTiSiTiTiTiTiTiTiO_!2?SiTiTiTiTiTiS_?aD_TiSiPgTiTiTiTiTiTiTiTiTiTiSiTiTg 64 | TiTiSiTiTA$#15!13?A!3?O!2?@A@G?G!2?GA?_!17?DA!12?ICI!438?A!4?BCAC@!34?A?!2@OKAG!19?@!4?A?@?!2@$#1!526?@FN!2~ 65 | !2@!9?@BN^[!2W$-#12!494?@ITiTiTiTiTiTiTiTiTiTgTiTiTiTiTiTiTiO?DIDIDIDADADADADADADADITiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTI@$#14 66 | !494?ADiTiTiTiTiTiTiTiTiTiPgTITiTiTiTiTiSgPADIDIDIDADADADAD?DADIDiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiTiDA$#1!515?EA?_!10?@Fmw!6o 67 | !9wy!3w!2o$#15!10?ICI!2?O!5?D?D!2?@!3?_!11?O!7?_O_!2?GC!7?C!34?K!3?!2_!11?_!371?_$#9!22?A$-#12!497?A 68 | DITiTiTiTiTiTiTiTiSIDIDiTiTiTiTiTiSgO_O_!15?gTiTiTiTiTiTiTiTiTiTiTiTiTiTiTIDA$#14!497?@ADITiTiTiTiTiTiTiTiTiDiDiTiTiTiTiSgOgO_ 69 | !16?OiTiTiTiTiTiTiTiTiTiTiTiTiTiTIDA@$#1!516?@!14?@B!2FJF^~n^zv~^n~n!5~F$#15!16?@!4?ADA!6?g?W@?_oAGBaA?C?@E!4?@!8? 70 | A@!35?@!4?G!4?CG!4?C!4?G!398?_Oo!2O!13?CG!2?O_CG?_O?O$#7!11?DAD!473?@?@$#9!22?A$-#14!503?@ADITiTiTiTiTgTiPiTiTiT 71 | iTiTiTiTiTiTiCg!11?iTiTiTiTiTiTiTiTiTiTiTiTIDA@$#12!502?@ADITiTiTiTiTiOaDg@_PgSiTiTiTiTiTADiTi!11?_SiTiTiTiTiTiTiTiTiTiTiTiTI 72 | DA@$#1!539?@DZj!2^~^}!3~^@$#15!26?O_?C@!2?!2@M?K!2?K?G!2?AG!48?AOA!2?o!2?O!2?@!409?FGOESICA@!10?gO!2?OQc 73 | S!2_?_@$-#14!509?@A@ADIDITiTiTiTiTiTiTiTiTiTiTiTiO_?_?_OgSiTiTiTiTiTiTiTiTIDIDA@A@$#12!510?@ADADITITiTiTiTiTiTiTiTiTiTiSiTgO_?_ 74 | ?_OiTiTiTiTiTiTiTiTITIDADA@$#1!541?FN!4^NF@$#15!36?!2CD!4?_!4?C!2?G!2?O!44?@!18?@!419?@$-#14!521?@?@A@A@ADA@A@A@A@ 75 | A@A@ADADADI@A@ADA@A@A@?@$#12!58?O!463?@?@A@ADA@A@A@A@A@A@ADADADA@I@ADADA@A@?@$#15!34?C!2?A!7?O!484?!3C!9K!6G!4C$#7 76 | !57?OgO!559?_!3?{?wC!2cg?CcSKC$-#7!619?@!3A@?@!3A@?B!4A$#15!34?_!6?_O_!3?C!4?DAD!2?O$-#15!42?@!6?a$- -------------------------------------------------------------------------------- /testfiles/testhlong.six: -------------------------------------------------------------------------------- 1 | Pq 2 | #0;2;0;0;0 3 | #1;2;0;100;0 4 | #1?F!500AF- 5 | \ 6 | -------------------------------------------------------------------------------- /testfiles/testhlong_clean.six: -------------------------------------------------------------------------------- 1 | #0;2;0;0;0 2 | #1;2;0;100;0 3 | #1?F!500AF- -------------------------------------------------------------------------------- /testfiles/zx81.six: -------------------------------------------------------------------------------- 1 | Pq#1 2 | !6~!12F!6~!6?!15w!6?www!12?www!9?!12w!9?www!24?!12w!12?!15w!6?!15w!30?~~~!18F!6~FFF!12~FFF~~~!6?!12w!15?!6w!9?- 3 | ~~~www!12F!6~!12?~~~!12?~~~FFFwww!6?~~~!6?~~~!12?FFF!6?~~~!21?~~~!12?~~~!15?~~~!12?~~~!12?~~~!27?!12~FFFwww!12~www!6Fwww!6~???FFF!12wFFF!9?FFF???~~~!9?- 4 | ~~~FFF!12~???~~~!12?~~~!12?~~~!6?FFFwww~~~!6?~~~!12?www!6?~~~!21?~~~!12F~~~!15?~~~!12?~~~!9F~~~!30?!6~FFFwww!18~FFF!6wFFF!6~???~~~!12?~~~!15?~~~!9?- 5 | !6~!12w!6~!6?!15F!6?FFF!12?FFF!9?!12F!9?!18F!6?FFF!12?FFF!9?!15F!6?FFF!12?FFF!27?~~~!18w!6~www!12~www~~~!6?!12F!12?!15F???- 6 | \ 7 | -------------------------------------------------------------------------------- /testfiles/zx81_clean.six: -------------------------------------------------------------------------------- 1 | #1 2 | !6~!12F!6~!6?!15w!6?www!12?www!9?!12w!9?www!24?!12w!12?!15w!6?!15w!30?~~~!18F!6~FFF!12~FFF~~~!6?!12w!15?!6w!9?- 3 | ~~~www!12F!6~!12?~~~!12?~~~FFFwww!6?~~~!6?~~~!12?FFF!6?~~~!21?~~~!12?~~~!15?~~~!12?~~~!12?~~~!27?!12~FFFwww!12~www!6Fwww!6~???FFF!12wFFF!9?FFF???~~~!9?- 4 | ~~~FFF!12~???~~~!12?~~~!12?~~~!6?FFFwww~~~!6?~~~!12?www!6?~~~!21?~~~!12F~~~!15?~~~!12?~~~!9F~~~!30?!6~FFFwww!18~FFF!6wFFF!6~???~~~!12?~~~!15?~~~!9?- 5 | !6~!12w!6~!6?!15F!6?FFF!12?FFF!9?!12F!9?!18F!6?FFF!12?FFF!9?!15F!6?FFF!12?FFF!27?~~~!18w!6~www!12~www~~~!6?!12F!12?!15F???- 6 | 7 | -------------------------------------------------------------------------------- /tsconfig.esm.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "ES2020", 4 | "target": "ES2017", 5 | "rootDir": "src", 6 | "outDir": "lib-esm", 7 | "sourceMap": true, 8 | "declaration": true, 9 | "noImplicitAny": true, 10 | "strict": true, 11 | "moduleResolution": "node" 12 | }, 13 | "exclude": [ 14 | "node_modules", 15 | "scripts", 16 | "lib", 17 | "lib-esm", 18 | "test", 19 | "examples" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "CommonJS", 4 | "target": "ES2017", 5 | "rootDir": "src", 6 | "outDir": "lib", 7 | "sourceMap": true, 8 | "declaration": true, 9 | "noImplicitAny": true, 10 | "strict": true 11 | }, 12 | "exclude": [ 13 | "node_modules", 14 | "scripts", 15 | "lib", 16 | "lib-esm", 17 | "test", 18 | "examples" 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "class-name": true, 4 | "comment-format": [ 5 | true, 6 | "check-space" 7 | ], 8 | "indent": [ 9 | true, 10 | "spaces" 11 | ], 12 | "eofline": true, 13 | "one-variable-per-declaration": [true, "ignore-for-loop"], 14 | "no-eval": true, 15 | "no-internal-module": true, 16 | "no-trailing-whitespace": true, 17 | "no-unsafe-finally": true, 18 | "no-var-keyword": true, 19 | "quotemark": [ 20 | true, 21 | "single" 22 | ], 23 | "semicolon": [ 24 | true, 25 | "always" 26 | ], 27 | "triple-equals": [ 28 | true, 29 | "allow-null-check" 30 | ], 31 | "typedef": [ 32 | "call-signature", 33 | "parameter", 34 | "property-declaration", 35 | "member-variable-declaration" 36 | ], 37 | "typedef-whitespace": [ 38 | true, 39 | { 40 | "call-signature": "nospace", 41 | "index-signature": "nospace", 42 | "parameter": "nospace", 43 | "property-declaration": "nospace", 44 | "variable-declaration": "nospace" 45 | } 46 | ], 47 | "variable-name": [ 48 | true, 49 | "ban-keywords" 50 | ], 51 | "whitespace": [ 52 | true, 53 | "check-branch", 54 | "check-decl", 55 | "check-operator", 56 | "check-separator", 57 | "check-type" 58 | ] 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /wasm/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Sixel band decoder 3 | 4 | 5 | __Features__: 6 | - written in C 7 | - allocation free (single instance static memory) 8 | - crafted for web assembly / emscripten 9 | - also embeddable in native code 10 | - quite fast (decoding throughput >150 MB/s) 11 | - small wasm binary (~11 kB) 12 | - small memory footprint (<<1 MB, depending on `MAX_WIDTH`) 13 | 14 | 15 | ### Note on WASM features 16 | 17 | Currently wasm engines differ alot in supported wasm features. 18 | To still support a wide variety of engines, this module is coded in vanilla C and 19 | only uses the `bulk-memory` feature (optionally). 20 | 21 | 22 | ### Note on native usage 23 | 24 | The decoder can be used from other native C/C++ projects by simply including `decoder.cpp`. 25 | Still for native embedding there are a few things to consider first: 26 | 27 | - single instance / static memory 28 | The parser state is a single statically allocated struct. While this is a perfect fit 29 | for isolated wasm module instances, it is prolly not what you want in a C or C++ project, 30 | unless for a simple cmdline converter tool. Without code modifications decoding is limited 31 | to one parser state / one image at a time. (This is easy fixable by introducing 32 | state indirections at the function interfaces, but beyond the scope here.) 33 | 34 | - code optimizations 35 | The decoder loop is a highly optimized byte-by-byte loop in vanilla C. 36 | While this also runs fast natively, things like SIMD extensions would give 37 | another quite remarkable boost, but do not work reliable in wasm yet. 38 | 39 | 40 | ### Future optimization ideas 41 | 42 | - wasm-SIMD, currently engines have no to lousy support with bad performance 43 | (see dysfunctional proof of concept in `decoder-simd.cpp`) 44 | - shared memory, currently shifting too much in browsers 45 | - wasm-threading, still not fully landed in wasm, depends on stable shared memory interfaces 46 | 47 | 48 | ### Interface 49 | 50 | To use the decoder from other non javascript wasm environments or natively, 51 | you gonna need to hook into the following API. 52 | For reference usage and how to build a full image decoder from the sixel band handling, 53 | see the implementation for javascript under `src/WasmDecoder.ts`. 54 | 55 | Important compile time settings (see build.sh to adjust): 56 | - `CHUNK_SIZE` - max amount of chunk bytes 57 | - `PALETTE_SIZE` - max colors in the palette 58 | - `MAX_WIDTH` - max band width (excess pixels to the right are truncated) 59 | 60 | Exported symbols: 61 | - `void* get_state_address()` 62 | Void pointer to the static `ParserState` struct in WASM memory.\ 63 | Properties of interest (indexed in 32bit): 64 | - 1: fill color as ABGR32 (as given by `init`) 65 | - 2: width+4 in M2, else 0 66 | - 3: height in M2, else 0 67 | - 4: raster numerator (unmodified) in M2, else 0 68 | - 5: raster denominator (unmodified) in M2, else 0 69 | - 6: raster width (unmodified) in M2, else 0 70 | - 7: raster height (unmodified)in M2, else 0 71 | - 8: truncate (as given by `init`) 72 | - 9: image level (L0 - undecided, L1 - level 1, L2 - level 2) 73 | - 10: operation mode (M0 - undecided, M1 - level 1/2 !truncate, M2 - level 2 truncating) 74 | - 11: palette length 75 | - `void* get_chunk_address()` 76 | Void pointer to `ParserState.chunk` byte array (max size of `CHUNK_SIZE`). 77 | Used to load image data to be processed by `decode`. 78 | - `void* get_p0_address()` 79 | Void pointer to first pixel line p0 the band. The other pixel lines p1 - p5 80 | start at `get_p0_address() + (MAX_WIDTH + 4) * line_idx`. 81 | Used to grab pixel data when a band was finished. 82 | - `void* get_palette_address()` 83 | Void pointer to `ParserState.palette` ABGR32 array (max size of `PALETTE_SIZE`). 84 | Used to read/write palette colors. 85 | - `void init(int fill_color, unsigned int palette_limit, int truncate)` 86 | Initialize decoder for new image. Must be called before any decoding happens. 87 | - `void decode(int start, int end)` 88 | Decode data loaded into `ParserState.chunk[start .. end]` (right exclusive). 89 | - `int current_width()` 90 | Return the cursor advance of the current band in M1 mode, or width in M2 mode. 91 | This is needed to properly construct the full image at the end of decoding, 92 | in case the data did not finish with LF. 93 | - `int current_width()` 94 | M1 mode only - return the current lowermost pixel position touched by a sixel 95 | of the current band. This is needed to properly construct the full image with 96 | proper height for the current band, in case the data did not finish with LF. 97 | 98 | Needed callbacks: 99 | - `int mode_parsed(int mode)` 100 | Called once early during decoding when the parser settled the operation mode. 101 | The operation mode will be settled by the decoder on reading a valid raster 102 | attributes command or any other sixel command or sixel data bytes. 103 | Used to announce the operation mode and potential raster attributes for 104 | further preparation steps. 105 | Return 0 to continue, 1 to abort further processing. 106 | - `int handle_band(int width)` 107 | Called upon finishing decoding of a sixel band (on LF). `width` is either the 108 | raster width (M2 mode), or the max cursor advance (both clamped to `maxWidth`). 109 | Used to copy pixel in p0 .. p5, before continuing with the next band. 110 | Return 0 to continue, 1 to abort further processing. 111 | 112 | 113 | ### Note on SIXEL handling 114 | 115 | - The data to be digested by this decoder should only be the "Picture Definition" part of a SIXEL 116 | escape sequence, as denoted by the spec. Any other data beyond that is likely to screw up 117 | the image creation. In particular this means, that the decoder should run behind 118 | a terminal escape sequence, that is capable of proper DEC style DCS parsing 119 | (also handling spurious ESC, SUB and C1 codes). 120 | - Raster or pixel ratio definitions are not dealt with beside width and height (if `truncate` is set). 121 | Raster attributes are exposed unmodified in 0 .. 2^31-1 (as read from data), 122 | and can be used to postprocess pixel data as needed. 123 | - With `truncate` set in `init`, the decoder will truncate the image to given raster dimensions. 124 | This does not apply to level 1 images without any raster attributes. While truncation to 125 | raster dimensions on decoder side is not spec-conform, it is the expected data format created by 126 | a spec-conform encoder. 127 | - If `truncate` is not set, the width will be derived from cursor advance to the right, clamped 128 | to `MAX_WIDTH-4`. In this mode, `current_height` is reported as lowermost pixel position touched by sixels. 129 | Furthermore in this mode the height is not limited by any means, thus decoding may run forever. 130 | Use some sort of accounting during `handle_band` to spot malformed data or excessive memory usage, 131 | especially when dealing with data streams. 132 | - Palette colors are applied immediately to sixels (printer mode), there is no terminal-like indexed mode. 133 | While this is in line with the spec, it does not allow to mimick the palette behavior of older terminals 134 | (e.g. palette animations are not possible). 135 | - The decoder unconditionally strips the 8th bit, mapping all data bytes in 7-bit space. 136 | While the spec defines this only as error recovery strategy for GR codes, the decoder also does this 137 | for C1, which might lead to sixel command interpretation from spurious C1 codes. Note that C1 138 | never should appear in sixel data, if used behind a proper escape sequence parser. 139 | - Other than stated in the spec, the decoder does not error on low C0 codes, instead silently ignores them. 140 | Again a proper escape sequence parser will filter / act upon those. 141 | - The repeat count gets not limited/clamped to 32767. The digits are parsed in signed int32 for 142 | performance reasons, and converted to unsigned before used as repeat count. While the counter 143 | will show weird behavior above 2^31-1 (counting backwards), it should not be possible to overflow 144 | the pixel arrays with malicious data (separately tested against `MAX_WIDTH` before any painting). 145 | 146 | There are probably more deviations from the SIXEL spec not listed here. 147 | 148 | 149 | ### Status 150 | 151 | Beta. The code is tested with unit/regression tests from the JS integration. 152 | -------------------------------------------------------------------------------- /wasm/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ################################# 4 | # compile time decoder settings # 5 | ################################# 6 | 7 | # EMSCRIPTEN_PATH 8 | # Path to emscripten SDK. 9 | # Change this if you have emscripten already installed somewhere 10 | # and/or dont want to use the auto installation. 11 | # (auto install script is under bin/install_emscripten.sh) 12 | EMSCRIPTEN_PATH=../emsdk/emsdk_env.sh 13 | 14 | # CHUNK_SIZE 15 | # Maximum size of a single chunk, that can be loaded into the decoder. 16 | # This has only a tiny impact on the decoder speed, thus we can go with 17 | # a rather low value (aligned with typical PIPE_BUF values). 18 | # Use one of 2 ^ (8 .. 16). 19 | CHUNK_SIZE=16384 20 | 21 | # PALETTE_SIZE 22 | # Maximum color slots the internal palette may hold. 23 | # Most SIXEL images never request more than 256 colors (demanded by the spec). 24 | # We use a much higher default of 4096, thus can deal up to 12bit-RGB. 25 | # Use one of 2 ^ (8 .. 16). 26 | PALETTE_SIZE=4096 27 | 28 | # MAX_WIDTH 29 | # Maximum width of a pixel line the decoder can handle. 30 | # Changing this will also change the memory needs below. 31 | # This value must be a multiple of 128 to stay in line with the clear logic. 32 | MAX_WIDTH=16384 33 | 34 | # MEMORY 35 | # Memory used by an instance. Formula is roughly MAX_WIDTH * 4 * 6 + 65536. 36 | MEMORY=$((7 * 65536)) 37 | 38 | ################## 39 | # compile script # 40 | ################## 41 | 42 | 43 | # activate emscripten env 44 | source $EMSCRIPTEN_PATH 45 | 46 | # compile with customizations 47 | emcc -O3 \ 48 | -DCHUNK_SIZE=$CHUNK_SIZE \ 49 | -DPALETTE_SIZE=$PALETTE_SIZE \ 50 | -DMAX_WIDTH=$MAX_WIDTH \ 51 | -s ASSERTIONS=0 \ 52 | -s IMPORTED_MEMORY=0 \ 53 | -s MALLOC=none \ 54 | -s ALLOW_MEMORY_GROWTH=0 \ 55 | -s SAFE_HEAP=0 \ 56 | -s WARN_ON_UNDEFINED_SYMBOLS=0 \ 57 | -s ERROR_ON_UNDEFINED_SYMBOLS=0 \ 58 | -s DISABLE_EXCEPTION_CATCHING=1 \ 59 | -s DEFAULT_TO_CXX=0 \ 60 | -s STRICT=1 \ 61 | -s SUPPORT_ERRNO=0 \ 62 | -s TOTAL_STACK=0 \ 63 | -s INITIAL_MEMORY=$MEMORY \ 64 | -s MAXIMUM_MEMORY=$MEMORY \ 65 | -s EXPORTED_FUNCTIONS='[ 66 | "_init", 67 | "_decode", 68 | "_current_width", 69 | "_current_height", 70 | "_get_state_address", 71 | "_get_chunk_address", 72 | "_get_p0_address", 73 | "_get_palette_address" 74 | ]' \ 75 | --no-entry -mbulk-memory decoder.cpp -o decoder.wasm 76 | 77 | # export compile time settings with settings.json 78 | echo "{\"CHUNK_SIZE\": $CHUNK_SIZE, \"PALETTE_SIZE\": $PALETTE_SIZE, \"MAX_WIDTH\": $MAX_WIDTH}" > settings.json 79 | 80 | 81 | # SIMD test 82 | #emcc -O3 \ 83 | #-DCHUNK_SIZE=$CHUNK_SIZE \ 84 | #-DPALETTE_SIZE=$PALETTE_SIZE \ 85 | #-DMAX_WIDTH=$MAX_WIDTH \ 86 | #-s ASSERTIONS=0 \ 87 | #-s IMPORTED_MEMORY=0 \ 88 | #-s MALLOC=none \ 89 | #-s ALLOW_MEMORY_GROWTH=0 \ 90 | #-s SAFE_HEAP=0 \ 91 | #-s WARN_ON_UNDEFINED_SYMBOLS=0 \ 92 | #-s ERROR_ON_UNDEFINED_SYMBOLS=0 \ 93 | #-s DISABLE_EXCEPTION_CATCHING=1 \ 94 | #-s DEFAULT_TO_CXX=0 \ 95 | #-s STRICT=1 \ 96 | #-s SUPPORT_ERRNO=0 \ 97 | #-s TOTAL_STACK=0 \ 98 | #-s INITIAL_MEMORY=$((145 * 65536)) \ 99 | #-s MAXIMUM_MEMORY=$((145 * 65536)) \ 100 | #-s EXPORTED_FUNCTIONS='[ 101 | # "_init", 102 | # "_decode", 103 | # "_get_chunk_address", 104 | # "_get_canvas_address", 105 | # "_get_palette_address" 106 | #]' \ 107 | #--no-entry -msimd128 -msse -msse2 -msse4.1 decoder-simd.cpp -o decoder-simd.wasm 108 | -------------------------------------------------------------------------------- /wasm/decoder-simd.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * WasmDecoder - static instance SIXEL decoder with fixed canvas limit. 3 | * 4 | * Copyright (c) 2021 Joerg Breitbart. 5 | * @license MIT 6 | */ 7 | 8 | /** 9 | * Note: This decoder is just a PoC for SIMD. 10 | * It is in a much earlier development state and 11 | * not compatible to the current JS interface. 12 | */ 13 | 14 | #include 15 | 16 | 17 | // cmdline overridable defines 18 | #ifndef CHUNK_SIZE 19 | #define CHUNK_SIZE 4096 20 | #endif 21 | #ifndef PALETTE_SIZE 22 | #define PALETTE_SIZE 256 23 | #endif 24 | 25 | // internal defines 26 | #define ST_DATA 0 27 | #define ST_COMPRESSION 33 28 | #define ST_COLOR 35 29 | 30 | #define PARAM_SIZE 8 31 | 32 | 33 | // static parser state 34 | static struct { 35 | int width; 36 | int height; 37 | int state; 38 | int color; 39 | int cursor; 40 | int y_offset; 41 | int offset; 42 | int p_length; 43 | int palette_length; 44 | int params[PARAM_SIZE]; 45 | int palette[PALETTE_SIZE]; 46 | char chunk[CHUNK_SIZE + 1] __attribute__((aligned(16))); 47 | int canvas[2359296] __attribute__((aligned(16))); // fixed at 1536 * 1526 pixels for now 48 | } __attribute__((aligned(16))) ps = {0}; 49 | 50 | 51 | // exported functions 52 | extern "C" { 53 | void* get_chunk_address() { return &ps.chunk[0]; } 54 | void* get_canvas_address() { return &ps.canvas[0]; } 55 | void* get_palette_address() { return &ps.palette[0]; } 56 | 57 | void init(unsigned int width, unsigned int height, int fill_color, unsigned int palette_length); 58 | void decode(int length); 59 | } 60 | 61 | 62 | 63 | // Put sixel n-times from current cursor position. 64 | //__attribute__((noinline)) 65 | static inline void put(int code, int color, unsigned int n, unsigned int cursor) { 66 | if (code && cursor < ps.width) { 67 | if (cursor + n >= ps.width) { 68 | n = ps.width - cursor; 69 | } 70 | int *p = ps.canvas + ps.offset + cursor; 71 | if (code & 1) { int *pp = p; int r = n; while (r--) *pp++ = color; } 72 | if (code & 2) { int *pp = p + ps.width; int r = n; while (r--) *pp++ = color; } 73 | if (code & 4) { int *pp = p + ps.width * 2; int r = n; while (r--) *pp++ = color; } 74 | if (code & 8) { int *pp = p + ps.width * 3; int r = n; while (r--) *pp++ = color; } 75 | if (code & 16) { int *pp = p + ps.width * 4; int r = n; while (r--) *pp++ = color; } 76 | if (code & 32) { int *pp = p + ps.width * 5; int r = n; while (r--) *pp++ = color; } 77 | } 78 | } 79 | 80 | // Put 4 consecutive sixels with SIMD. 81 | #ifdef EMSCRIPTEN 82 | static inline void put_simd(int sixels_agg, int offset, int color) { 83 | v128_t colors = wasm_i32x4_splat(color); 84 | v128_t sixels = wasm_i32x4_make(sixels_agg, 0, 0, 0); 85 | sixels = wasm_u16x8_extend_low_u8x16(sixels); 86 | sixels = wasm_u32x4_extend_low_u16x8(sixels); 87 | 88 | int *pp = &ps.canvas[offset]; 89 | for (int i = 0; i < 6; ++i, pp += ps.width) { 90 | v128_t matcher = wasm_i32x4_splat(1 << i); 91 | v128_t bitmask = wasm_i32x4_eq(matcher, wasm_v128_and((v128_t) sixels, matcher)); 92 | v128_t updated = wasm_v128_and(bitmask, colors); 93 | v128_t prev = wasm_v128_load((v128_t *) pp); 94 | v128_t keep = wasm_v128_andnot(prev, bitmask); 95 | wasm_v128_store((v128_t *) pp, wasm_v128_or(keep, updated)); 96 | } 97 | // much nicer, but also 10% slower on v8 (bitselect not optimized in wasm engine?): 98 | //for (int i = 0; i < 6; ++i, pp += ps.width) { 99 | // v128_t matcher = wasm_i32x4_splat(1 << i); 100 | // v128_t bitmask = wasm_i32x4_eq(matcher, wasm_v128_and((v128_t) sixels, matcher)); 101 | // v128_t prev = wasm_v128_load((v128_t *) pp); 102 | // wasm_v128_store((v128_t *) pp, wasm_v128_bitselect(colors, prev, bitmask)); 103 | //} 104 | } 105 | #else 106 | static inline void put_simd(int sixels_agg, int offset, int color) { 107 | __m128i colors = _mm_set1_epi32(color); 108 | __m128i sixels; 109 | sixels = _mm_insert_epi32(sixels, sixels_agg, 0); 110 | sixels = _mm_cvtepu8_epi32(sixels); 111 | 112 | int *pp = &ps.canvas[offset]; 113 | for (int i = 0; i < 6; ++i) { 114 | __m128i matcher = _mm_set1_epi32(1 << i); 115 | __m128i bitmask = _mm_cmpeq_epi32(matcher, _mm_and_si128(sixels, matcher)); 116 | __m128i updated = _mm_and_si128(bitmask, colors); 117 | __m128i prev = _mm_loadu_si128((__m128i *) pp); 118 | __m128i keep = _mm_andnot_si128(bitmask, prev); 119 | _mm_storeu_si128((__m128i *) pp, _mm_or_si128(keep, updated)); 120 | pp += ps.width; 121 | } 122 | } 123 | #endif 124 | 125 | 126 | // Normalize %-based SIXEL RGB 0..100 to to RGBA8888. 127 | #ifdef EMSCRIPTEN 128 | static inline int normalize_rgb_simd(float r, float g, float b) { 129 | v128_t reg = wasm_f32x4_make(r, g, b, 100); 130 | reg = wasm_f32x4_mul(reg, wasm_f32x4_splat(2.55f)); 131 | reg = wasm_u32x4_trunc_sat_f32x4(reg); 132 | // this might be faster than swizzle: 133 | //reg = wasm_u16x8_narrow_i32x4(reg, reg); 134 | //reg = wasm_u8x16_narrow_i16x8(reg, reg); 135 | reg = wasm_i8x16_swizzle(reg, wasm_i8x16_make( 136 | 0x00, 0x04, 0x08, 0x0C, 137 | 0x80, 0x80, 0x80, 0x80, 138 | 0x80, 0x80, 0x80, 0x80, 139 | 0x80, 0x80, 0x80, 0x80 140 | )); 141 | return wasm_i32x4_extract_lane(reg, 0); 142 | } 143 | #else 144 | static inline int normalize_rgb_simd(float r, float g, float b) { 145 | __m128 reg = _mm_set_ps(r, g, b, 100); 146 | reg = _mm_mul_ps(reg, _mm_set1_ps(2.55f)); 147 | __m128i result = _mm_cvtps_epi32(reg); 148 | result = _mm_shuffle_epi8(result, _mm_set_epi8( 149 | 0x80, 0x80, 0x80, 0x80, 150 | 0x80, 0x80, 0x80, 0x80, 151 | 0x80, 0x80, 0x80, 0x80, 152 | 0x00, 0x04, 0x08, 0x0C 153 | )); 154 | return _mm_cvtsi128_si32(result); 155 | } 156 | #endif 157 | 158 | 159 | // hue to channel value helper. 160 | static inline float h2c(float t1, float t2, float c) { 161 | if (c < 0) c += 1; 162 | else if (c > 1) c -= 1; 163 | return c < 0.1666666716f // c * 6 < 1 164 | ? t2 + (t1 - t2) * 6 * c 165 | : c < 0.5f // c * 2 < 1 166 | ? t1 167 | : c < 0.6666666865f // c * 3 < 2 168 | ? t2 + (t1 - t2) * (4 - c * 6) 169 | : t2; 170 | } 171 | 172 | 173 | // Normalize SIXEL HLS to RGBA8888. 174 | // Incoming values are in: H - 0..360 (hue turned by 240°), L - 0..100, S - 0..100. 175 | // TODO: faster SIMD version possible? 176 | static inline int normalize_hls(float h, float l, float s) { 177 | if (!s) { 178 | return normalize_rgb_simd(l, l, l); 179 | } 180 | h = (h + 240 % 360) / 360; 181 | l = l / 100; 182 | s = s / 100; 183 | float t1 = l < 0.5f ? l * (1 + s) : l * (1 - s) + s; 184 | float t2 = l * 2 - t1; 185 | unsigned char r = 255 * h2c(t1, t2, h + 0.3333333433f); // + 1.0f / 3 186 | unsigned char g = 255 * h2c(t1, t2, h); 187 | unsigned char b = 255 * h2c(t1, t2, h - 0.3333333433f); // - 1.0f / 3 188 | return 0xFF000000 | b << 16 | g << 8 | r; 189 | } 190 | 191 | // Static color converter fp array to avoid branching. 192 | typedef int (*color_converter)(float, float, float); 193 | const static color_converter COLOR_CONVERTERS[2] = { &normalize_hls, &normalize_rgb_simd }; 194 | 195 | 196 | // Tiny modulo optimization. 197 | static inline int fastmod(int value, int ceil) { 198 | return value < ceil ? value : value % ceil; 199 | } 200 | 201 | // Apply color request. 202 | //__attribute__((noinline)) 203 | static inline int apply_color(int color) { 204 | if (ps.p_length == 1) { 205 | color = ps.palette[fastmod(ps.params[0], ps.palette_length)]; 206 | } else if (ps.p_length == 5 207 | && ps.params[1] == 1 ? ps.params[2] <= 360 : ps.params[2] <= 100 208 | && ps.params[3] <= 100 209 | && ps.params[4] <= 100) 210 | { 211 | if (ps.params[1] && ps.params[1] < 3) { 212 | ps.palette[fastmod(ps.params[0], ps.palette_length)] = COLOR_CONVERTERS[ps.params[1] - 1]( 213 | ps.params[2], ps.params[3], ps.params[4]); 214 | } 215 | color = ps.palette[fastmod(ps.params[0], ps.palette_length)]; 216 | } 217 | return color; 218 | } 219 | 220 | 221 | /** 222 | * @brief Initialize a new SIXEL image. 223 | */ 224 | void init(unsigned int width, unsigned int height, int fill_color, unsigned int palette_length) { 225 | // note: overflow/range checks already done in JS 226 | ps.width = width; 227 | ps.height = height; 228 | ps.state = ST_DATA; 229 | ps.color = 0; 230 | ps.cursor = 0; 231 | ps.y_offset = 0; 232 | ps.offset = 0; 233 | ps.palette_length = (palette_length < PALETTE_SIZE) ? palette_length : PALETTE_SIZE; 234 | ps.params[0] = 0; 235 | ps.p_length = 1; 236 | 237 | // clear canvas with fill_color 238 | // we dont have to use SIMD manually here (done by compiler) 239 | int length = (((height + 5) / 6 * 6) * width + 8); 240 | int *p = ps.canvas; 241 | while (length--) *p++ = fill_color; 242 | } 243 | 244 | 245 | /** 246 | * FIXME: 247 | * - compression stacking with multiple !255!255? 248 | * - cursor width overflow (may overwrite next line pixels, possible mem overflow in last line) 249 | */ 250 | void decode(int length) { 251 | if (ps.y_offset < ps.height) { 252 | int cur = ps.cursor; 253 | int state = ps.state; 254 | int color = ps.color; 255 | ps.chunk[length] = 0xFF; 256 | for (int i = 0; i < length; ++i) { 257 | int code = ps.chunk[i] & 0x7F; 258 | 259 | int p = ps.params[ps.p_length - 1]; 260 | while (unsigned(code - 48) < 10) { 261 | p = p * 10 + code - 48; 262 | code = ps.chunk[++i] & 0x7F; 263 | } 264 | ps.params[ps.p_length - 1] = p; 265 | 266 | if (unsigned(code - 63) < 64 && state != ST_DATA) { 267 | if (state == ST_COMPRESSION) { 268 | put(code - 63, color, ps.params[0], cur); 269 | cur += ps.params[0]; 270 | code = ps.chunk[++i] & 0x7F; 271 | } else { 272 | color = apply_color(color); 273 | } 274 | state = ST_DATA; 275 | } 276 | 277 | int shift = 0; 278 | int off = cur + ps.offset; 279 | int agg = 0; 280 | while (unsigned(code - 63) < 64) { 281 | agg |= (code - 63) << shift; 282 | cur++; 283 | shift += 8; 284 | if (shift == 32) { 285 | put_simd(agg, off, color); 286 | agg = shift = 0; 287 | off += 4; 288 | } 289 | code = ps.chunk[++i] & 0x7F; 290 | } 291 | if (agg) put_simd(agg, off, color); 292 | 293 | if (code == ST_COMPRESSION || code == ST_COLOR) { 294 | if (state == ST_COLOR) color = apply_color(color); 295 | ps.params[0] = 0; 296 | ps.p_length = 1; 297 | state = code == ST_COMPRESSION ? ST_COMPRESSION : ST_COLOR; 298 | } else 299 | if (code == '$' || code == '-') { 300 | if (code == '-') { 301 | ps.y_offset += 6; 302 | if (ps.y_offset >= ps.height) return; 303 | ps.offset = ps.y_offset * ps.width; 304 | } 305 | cur = 0; 306 | off = ps.offset; 307 | } else 308 | if (code == ';') { 309 | if (ps.p_length < PARAM_SIZE) { 310 | ps.params[ps.p_length++] = 0; 311 | } 312 | } 313 | 314 | } 315 | ps.cursor = cur; 316 | ps.state = state; 317 | ps.color = color; 318 | } 319 | } 320 | -------------------------------------------------------------------------------- /wasm/wasmer_example.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Rudimentary test with wasmer-python 4 | # 5 | # Install with: 6 | # - pip install wasmer==1.0.0 7 | # - pip install wasmer_compiler_llvm==1.0.0 8 | # 9 | # Note: wasmer-python 1.0 does not support the bulk-memory feature, 10 | # thus compile wasm file without -mbulk-memory flag 11 | 12 | 13 | from wasmer import engine, Store, Module, Instance, ImportObject, Function 14 | from wasmer_compiler_llvm import Compiler 15 | 16 | 17 | # some test data: 18 | # - broken attributes --> mode 1 19 | # - 2 lines with width 7 20 | # - pending line with current_width 2 21 | TEST = b'"1;1;7ABCDEFG$-ABCDEFG$-AB' 22 | 23 | 24 | def handle_band(width: int) -> int: 25 | print('got a line of:', width) 26 | assert width == 7 27 | return 0 28 | 29 | def mode_parsed(mode: int) -> int: 30 | print('mode selected:', mode) 31 | assert mode == 1 32 | return 0 33 | 34 | # load wasm engine 35 | store = Store(engine.JIT(Compiler)) 36 | module = Module(store, open('./decoder.wasm', 'rb').read()) 37 | import_object = ImportObject() 38 | import_object.register("env", { 39 | "handle_band": Function(store, handle_band), 40 | 'mode_parsed': Function(store, mode_parsed), 41 | }) 42 | instance = Instance(module, import_object) 43 | mem = instance.exports.memory.int8_view() 44 | chunk_address = instance.exports.get_chunk_address() 45 | 46 | # load test data 47 | mem[chunk_address:] = TEST 48 | 49 | # run 50 | instance.exports.init(-1, 0, 256, 1) 51 | instance.exports.decode(0, len(TEST)) 52 | 53 | print('current_width:', instance.exports.current_width()) 54 | assert instance.exports.current_width() == 2 55 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const full_esm = { 4 | entry: `./lib-esm/index.js`, 5 | devtool: 'source-map', 6 | module: { 7 | rules: [ 8 | { 9 | test: /\.js$/, 10 | use: ["source-map-loader"], 11 | enforce: "pre", 12 | exclude: /node_modules/ 13 | } 14 | ] 15 | }, 16 | output: { 17 | filename: 'full.esm.js', 18 | path: path.resolve(__dirname, 'dist'), 19 | libraryTarget: 'module', 20 | }, 21 | mode: 'production', 22 | experiments: { 23 | outputModule: true, 24 | } 25 | }; 26 | 27 | const full_umd = { 28 | entry: `./lib-esm/index.js`, 29 | devtool: 'source-map', 30 | module: { 31 | rules: [ 32 | { 33 | test: /\.js$/, 34 | use: ["source-map-loader"], 35 | enforce: "pre", 36 | exclude: /node_modules/ 37 | } 38 | ] 39 | }, 40 | output: { 41 | filename: 'full.umd.js', 42 | path: path.resolve(__dirname, 'dist'), 43 | libraryTarget: 'umd', 44 | library: ['sixel'] 45 | }, 46 | mode: 'production' 47 | }; 48 | 49 | const decode_esm = { 50 | entry: `./lib-esm/bundle_decode.js`, 51 | devtool: 'source-map', 52 | module: { 53 | rules: [ 54 | { 55 | test: /\.js$/, 56 | use: ["source-map-loader"], 57 | enforce: "pre", 58 | exclude: /node_modules/ 59 | } 60 | ] 61 | }, 62 | output: { 63 | filename: 'decode.esm.js', 64 | path: path.resolve(__dirname, 'dist'), 65 | libraryTarget: 'module', 66 | }, 67 | mode: 'production', 68 | experiments: { 69 | outputModule: true, 70 | } 71 | }; 72 | 73 | const decode_umd = { 74 | entry: `./lib-esm/bundle_decode.js`, 75 | devtool: 'source-map', 76 | module: { 77 | rules: [ 78 | { 79 | test: /\.js$/, 80 | use: ["source-map-loader"], 81 | enforce: "pre", 82 | exclude: /node_modules/ 83 | } 84 | ] 85 | }, 86 | output: { 87 | filename: 'decode.umd.js', 88 | path: path.resolve(__dirname, 'dist'), 89 | libraryTarget: 'umd', 90 | library: ['sixel'] 91 | }, 92 | mode: 'production' 93 | }; 94 | 95 | const encode_esm = { 96 | entry: `./lib-esm/bundle_encode.js`, 97 | devtool: 'source-map', 98 | module: { 99 | rules: [ 100 | { 101 | test: /\.js$/, 102 | use: ["source-map-loader"], 103 | enforce: "pre", 104 | exclude: /node_modules/ 105 | } 106 | ] 107 | }, 108 | output: { 109 | filename: 'encode.esm.js', 110 | path: path.resolve(__dirname, 'dist'), 111 | libraryTarget: 'module', 112 | }, 113 | mode: 'production', 114 | experiments: { 115 | outputModule: true, 116 | } 117 | }; 118 | 119 | const encode_umd = { 120 | entry: `./lib-esm/bundle_encode.js`, 121 | devtool: 'source-map', 122 | module: { 123 | rules: [ 124 | { 125 | test: /\.js$/, 126 | use: ["source-map-loader"], 127 | enforce: "pre", 128 | exclude: /node_modules/ 129 | } 130 | ] 131 | }, 132 | output: { 133 | filename: 'encode.umd.js', 134 | path: path.resolve(__dirname, 'dist'), 135 | libraryTarget: 'umd', 136 | library: ['sixel'] 137 | }, 138 | mode: 'production' 139 | }; 140 | 141 | module.exports = [ 142 | full_esm, full_umd, 143 | decode_esm, decode_umd, 144 | encode_esm, encode_umd 145 | ]; 146 | --------------------------------------------------------------------------------