├── .babelrc ├── .gitignore ├── src ├── Screen │ ├── crtlines.png │ ├── CRTScreen.js │ └── index.js ├── exampleCartridges │ ├── cast.p8.png │ ├── hello.p8.png │ ├── camera.p8.png │ ├── otomat.p8.png │ └── zengarden.p8.png ├── OS │ ├── sound.lib.js │ ├── math.lib.js │ ├── readCartridge.lib.js │ ├── index.js │ ├── font.lib.js │ └── graphics.lib.js ├── JSGS.js ├── index.js ├── Controller │ ├── OnScreen.js │ └── Keyboard.js ├── index.html ├── Ram.js ├── style.css └── vendor │ ├── glfx.js │ └── pngtoy.js ├── utils └── build │ ├── index.js │ ├── dev.server.js │ └── webpack.config.js ├── LICENCE ├── package.json └── README.md /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015"] 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | dist 3 | node_modules 4 | npm-debug.log 5 | -------------------------------------------------------------------------------- /src/Screen/crtlines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/burakcan/jsgs/HEAD/src/Screen/crtlines.png -------------------------------------------------------------------------------- /src/exampleCartridges/cast.p8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/burakcan/jsgs/HEAD/src/exampleCartridges/cast.p8.png -------------------------------------------------------------------------------- /src/exampleCartridges/hello.p8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/burakcan/jsgs/HEAD/src/exampleCartridges/hello.p8.png -------------------------------------------------------------------------------- /src/exampleCartridges/camera.p8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/burakcan/jsgs/HEAD/src/exampleCartridges/camera.p8.png -------------------------------------------------------------------------------- /src/exampleCartridges/otomat.p8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/burakcan/jsgs/HEAD/src/exampleCartridges/otomat.p8.png -------------------------------------------------------------------------------- /src/exampleCartridges/zengarden.p8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/burakcan/jsgs/HEAD/src/exampleCartridges/zengarden.p8.png -------------------------------------------------------------------------------- /src/OS/sound.lib.js: -------------------------------------------------------------------------------- 1 | export default function getSoundFunctions(ram) { 2 | function music(n, fadeLen, channelMask) {} 3 | 4 | function sfx(n, channel, offset) {} 5 | 6 | return { 7 | music, 8 | sfx, 9 | }; 10 | } 11 | -------------------------------------------------------------------------------- /utils/build/index.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | const NODE_ENV = process.env.NODE_ENV; 3 | 4 | switch (NODE_ENV) { 5 | case 'development': { 6 | return require('./dev.server')(); 7 | } 8 | 9 | default: { // production 10 | const config = require('./webpack.config.js'); 11 | const compiler = webpack(config); 12 | 13 | return compiler.run(function(err, stats) { 14 | if (err) throw err; 15 | 16 | console.log(stats.toString({ 17 | colors : true, 18 | chunks : false 19 | })); 20 | }); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /utils/build/dev.server.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const webpack = require('webpack'); 3 | const config = require('./webpack.config'); 4 | const historyApiFallback = require('connect-history-api-fallback'); 5 | const devMiddleware = require('webpack-dev-middleware'); 6 | 7 | module.exports = function(options) { 8 | options = options || {}; 9 | 10 | const PORT = options.port || 4000; 11 | const app = express(); 12 | const compiler = webpack(config); 13 | 14 | app.use(historyApiFallback()); 15 | 16 | app.use(devMiddleware(compiler, { 17 | publicPath: config.output.publicPath, 18 | stats: { 19 | colors: true, 20 | chunks: false, 21 | } 22 | })); 23 | 24 | app.listen(PORT, 'localhost', err => { 25 | if (err) throw err; 26 | 27 | console.log(`Listening at http://localhost:${PORT}`); 28 | }); 29 | } 30 | -------------------------------------------------------------------------------- /src/JSGS.js: -------------------------------------------------------------------------------- 1 | export default class JSGS { 2 | constructor(options) { 3 | this.devices = options.devices; 4 | this.os = options.os; 5 | 6 | this.os 7 | .sendEvent('boot', this) 8 | .sendEvent('cartridgeMount', this); 9 | 10 | this.updateLoop(() => { 11 | this.os.update(); 12 | this.devices.screens.forEach( 13 | screen => screen.update(this.devices.ram) 14 | ); 15 | }); 16 | } 17 | 18 | updateLoop(fn, fps) { 19 | fn(); 20 | 21 | let then = Date.now(); 22 | fps = fps || 30; 23 | const interval = 1000 / fps; 24 | 25 | return (function loop(time){ 26 | requestAnimationFrame(loop); 27 | 28 | const now = Date.now(); 29 | const delta = now - then; 30 | 31 | if (delta > interval) { 32 | then = now - (delta % interval); 33 | fn(); 34 | } 35 | }(0)); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/OS/math.lib.js: -------------------------------------------------------------------------------- 1 | export default { 2 | rnd(x) { 3 | return Math.random() * x; 4 | }, 5 | 6 | sgn(x) { 7 | return (x < 0) ? -1 : 1; 8 | }, 9 | 10 | max: Math.max, 11 | 12 | min: Math.min, 13 | 14 | mid(x, y, z) { 15 | return Math.max(x, Math.min(y, z)); 16 | }, 17 | 18 | flr: Math.floor, 19 | 20 | cos(x) { // x = 0 - 1 21 | return Math.cos((x * 360) * (3.1415/180)); 22 | }, 23 | 24 | sin(x) { 25 | return Math.sin((-x * 360) * (3.1415/180)); 26 | }, 27 | 28 | atan2: Math.atan2, 29 | 30 | sqrt: Math.sqrt, 31 | 32 | abs: Math.abs, 33 | 34 | srand(x) {}, 35 | 36 | band(x, y) { 37 | return x & y; 38 | }, 39 | 40 | bor(x, y) { 41 | return x | y; 42 | }, 43 | 44 | bxor(x, y) { 45 | return x ^ y; 46 | }, 47 | 48 | bnot(x) { 49 | return ~x; 50 | }, 51 | 52 | shl(x, y) {}, 53 | 54 | shr(x, y) {}, 55 | } 56 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import 'normalize.css'; 2 | import './style.css'; 3 | import 'script!./vendor/pngtoy'; 4 | import 'script!./vendor/glfx'; 5 | import JSGS from './JSGS'; 6 | import OS from './OS'; 7 | import Ram from './Ram'; 8 | import Screen from './Screen'; 9 | import CRTScreen from './Screen/CRTScreen'; 10 | import KeyboardController from './Controller/Keyboard'; 11 | import OnScreenController from './Controller/OnScreen'; 12 | import exampleCartridge from './exampleCartridges/hello.p8.png'; 13 | 14 | const screen = new Screen({ size: 128 }); 15 | const ram = new Ram(0x8000); 16 | const controller = new KeyboardController(); 17 | const os = new OS(); 18 | 19 | const machine = new JSGS({ 20 | os: os, 21 | devices: { 22 | controller: controller, 23 | ram: ram, 24 | cartridge: exampleCartridge, 25 | screens: [screen], 26 | }, 27 | }); 28 | 29 | screen.mountCanvas( 30 | document.getElementById('screen') 31 | ); 32 | -------------------------------------------------------------------------------- /src/Controller/OnScreen.js: -------------------------------------------------------------------------------- 1 | import KeyboardController from './Keyboard'; 2 | 3 | export default class OnSreenController extends KeyboardController { 4 | constructor(machine) { 5 | super(machine); 6 | this.buildButtons(); 7 | } 8 | 9 | buildButtons() { 10 | const wrapper = document.getElementById('mobileButtons'); 11 | 12 | this.keys.forEach((key, i) => { 13 | const button = document.createElement('button'); 14 | button.innerHTML = i; 15 | button.classList.add(`b_${key}`); 16 | 17 | button.addEventListener('touchstart', () => { 18 | const index = this.keys.indexOf(key); 19 | if (index < 0) return false; 20 | 21 | this.status[index] = true; 22 | }); 23 | 24 | button.addEventListener('touchend', () => { 25 | const index = this.keys.indexOf(key); 26 | if (index < 0) return false; 27 | 28 | this.status[index] = false; 29 | }); 30 | 31 | wrapper.appendChild(button) 32 | }); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JSGS 6 | 12 | 13 | 14 |
15 |
16 |
17 |
18 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/Screen/CRTScreen.js: -------------------------------------------------------------------------------- 1 | import Screen from './'; 2 | import crtlines from './crtlines.png'; 3 | 4 | export default class CRTScreen extends Screen { 5 | createCanvas(options) { 6 | const canvas = super.createCanvas(options); 7 | const glCanvas = this.glCanvas = fx.canvas(); 8 | 9 | this.lines = new Image(); 10 | this.lines.src = crtlines; 11 | this.srcctx = canvas.getContext('2d'); 12 | 13 | glCanvas.width = glCanvas.height = 512; 14 | glCanvas.style.dispay = 'block'; 15 | glCanvas.style.imageRendering = 'pixelated'; 16 | 17 | return canvas; 18 | } 19 | 20 | mountCanvas(element) { 21 | element.appendChild(this.glCanvas); 22 | } 23 | 24 | update(ram) { 25 | super.update(ram); 26 | 27 | const halfSize = this.size / 2; 28 | 29 | this.srcctx.drawImage(this.lines, 0, 0, this.size, this.size); 30 | 31 | this.glCanvas 32 | .draw(this.glCanvas.texture(this.canvas)) 33 | .bulgePinch(halfSize, halfSize, this.size * 0.75, 0.12) 34 | .vignette(0.15, 0.5) 35 | .update(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | MIT License 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 all 11 | 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 THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "JSGS", 3 | "version": "0.0.9", 4 | "main": "index.js", 5 | "author": "Burak Can", 6 | "license": "MIT", 7 | "repository": "https://gitlab.com/burakcan/jsgs.git", 8 | "scripts": { 9 | "clean": "rimraf dist", 10 | "build": "npm run clean && NODE_ENV=production node ./utils/build", 11 | "start-dev": "NODE_ENV=development node ./utils/build" 12 | }, 13 | "dependencies": { 14 | "babel-core": "^6.18.2", 15 | "babel-loader": "^6.2.7", 16 | "babel-preset-es2015": "^6.18.0", 17 | "connect-history-api-fallback": "^1.3.0", 18 | "css-loader": "^0.25.0", 19 | "escodegen": "^1.8.1", 20 | "estraverse": "^4.2.0", 21 | "express": "^4.14.0", 22 | "file-loader": "^0.9.0", 23 | "html-webpack-plugin": "^2.24.1", 24 | "json-loader": "^0.5.4", 25 | "lua2js": "^0.0.11", 26 | "normalize.css": "^5.0.0", 27 | "rimraf": "^2.5.4", 28 | "script-loader": "^0.7.0", 29 | "style-loader": "^0.13.1", 30 | "uglify-js": "github:mishoo/UglifyJS2#harmony", 31 | "webpack": "2.1.0-beta.25", 32 | "webpack-dev-middleware": "^1.8.4" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Controller/Keyboard.js: -------------------------------------------------------------------------------- 1 | export default class KeyboardController { 2 | constructor() { 3 | this.keys = [ 4 | 37, 39, 38, 40, 90, 88, // Player 1 (left, right, up, down, z, x) 5 | ]; 6 | 7 | this.status = [ 8 | false, false, false, false, false, false, // player 1 9 | false, false, false, false, false, false, // player 2 10 | ]; 11 | 12 | this.btnpWait = {}; 13 | 14 | document.addEventListener('keydown', event => { 15 | const index = this.keys.indexOf(event.keyCode); 16 | if (index < 0) return false; 17 | 18 | this.status[index] = true; 19 | }); 20 | 21 | document.addEventListener('keyup', event => { 22 | const index = this.keys.indexOf(event.keyCode); 23 | if (index < 0) return false; 24 | 25 | this.status[index] = false; 26 | }); 27 | 28 | this.api = { 29 | btn: this.btn.bind(this), 30 | btnp: this.btnp.bind(this), 31 | }; 32 | } 33 | 34 | btn(which, player = 0) { 35 | return this.status[which]; 36 | } 37 | 38 | btnp(which, player = 0) { 39 | const pressed = this.btn(which, player); 40 | 41 | if (!pressed) { 42 | this.btnpWait[`${which}${player}`] = false; 43 | return pressed; 44 | } 45 | 46 | if (this.btnpWait[`${which}${player}`]) return false; 47 | 48 | this.btnpWait[`${which}${player}`] = true; 49 | 50 | setTimeout(() => { 51 | this.btnpWait[`${which}${player}`] = false; 52 | }, 120); 53 | 54 | return pressed; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Ram.js: -------------------------------------------------------------------------------- 1 | export default class Ram { 2 | constructor(size) { 3 | this.size = size; 4 | this.arr = new Uint8Array(size); 5 | 6 | this.api = { 7 | peek: this.peek.bind(this), 8 | poke: this.poke.bind(this), 9 | memset: this.memset.bind(this), 10 | memcpy: this.memcpy.bind(this), 11 | memread: this.memread.bind(this), 12 | memwrite: this.memwrite.bind(this), 13 | reload: this.reload.bind(this), 14 | cstore: this.cstore.bind(this), 15 | }; 16 | } 17 | 18 | ifInRange(addr, fn) { 19 | if (addr >= 0x00 && addr < this.size) { 20 | return fn(); 21 | } 22 | 23 | throw new Error(`BAD MEMORY ACCESS! 0x${addr.toString(16)}`); 24 | } 25 | 26 | peek(addr) { 27 | return this.ifInRange(addr, () => this.arr[addr]); 28 | } 29 | 30 | poke(addr, val) { 31 | return this.ifInRange(addr, () => this.arr[addr] = val); 32 | } 33 | 34 | memset(dest_addr, value, length) { 35 | this.arr.fill(value, dest_addr, dest_addr + length); 36 | } 37 | 38 | memread(dest_addr, length) { 39 | return this.arr.slice(dest_addr, dest_addr + length); 40 | } 41 | 42 | memwrite(dest_addr, data) { 43 | const length = data.length - 1; 44 | for (let i = 0; i <= length; i++) { 45 | this.arr[dest_addr + i] = data[i]; 46 | } 47 | } 48 | 49 | memcpy(dest_addr, source_addr, length) { 50 | length = length - 1; 51 | for (let i = 0; i <= length; i++) { 52 | this.arr[dest_addr + i] = this.arr[source_addr + i]; 53 | } 54 | } 55 | 56 | reload() {} 57 | 58 | cstore() {} 59 | } 60 | -------------------------------------------------------------------------------- /src/style.css: -------------------------------------------------------------------------------- 1 | body, html { 2 | background: #000; 3 | box-sizing: border-box; 4 | } 5 | 6 | body * { 7 | box-sizing: inherit; 8 | } 9 | 10 | #layout.desktop #screen { 11 | position: fixed; 12 | left: 50%; 13 | top: 50%; 14 | width: 90vmin; 15 | height: 90vmin; 16 | transform: translateX(-50%) translateY(-50%); 17 | } 18 | 19 | #layout.desktop #screen canvas { 20 | width: 100%; 21 | } 22 | 23 | #layout.desktop #mobileButtons { 24 | display: none; 25 | } 26 | 27 | #layout.mobile { 28 | display: flex; 29 | position: fixed; 30 | left: 0; 31 | top: 0; 32 | width: 100%; 33 | height: 100%; 34 | flex-direction: column; 35 | background: #CCC; 36 | } 37 | 38 | #layout.mobile #screen { 39 | position: relative; 40 | padding: 20px; 41 | flex: 1; 42 | } 43 | 44 | #layout.mobile #screen canvas { 45 | width: 100% !important; 46 | border: 20px solid #EEEEEE; 47 | } 48 | 49 | #layout.mobile #mobileButtons { 50 | flex: 1; 51 | position: relative; 52 | } 53 | 54 | #layout.mobile #mobileButtons button { 55 | position: absolute; 56 | display: block; 57 | width: 15%; 58 | height: 0; 59 | padding-bottom: 15%; 60 | background: #aaa; 61 | border-radius: 50%; 62 | outline: none; 63 | border: none; 64 | } 65 | 66 | #layout.mobile #mobileButtons button.b_37 { 67 | right: 38%; 68 | top: 35%; 69 | } 70 | 71 | #layout.mobile #mobileButtons button.b_38 { 72 | right: 25%; 73 | top: 20%; 74 | } 75 | 76 | #layout.mobile #mobileButtons button.b_39 { 77 | right: 12%; 78 | top: 35%; 79 | } 80 | 81 | #layout.mobile #mobileButtons button.b_40 { 82 | right: 25%; 83 | top: 50%; 84 | } 85 | 86 | #layout.mobile #mobileButtons button.b_90 { 87 | left: 25%; 88 | top: 25%; 89 | } 90 | 91 | #layout.mobile #mobileButtons button.b_88 { 92 | left: 15%; 93 | top: 45%; 94 | } 95 | -------------------------------------------------------------------------------- /utils/build/webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | const path = require('path'); 3 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 4 | 5 | const srcPath = path.join(__dirname, '../../src'); 6 | const outPath = path.join(__dirname, '../../dist'); 7 | 8 | const config = { 9 | module: { 10 | loaders: [{ 11 | test: /\.css/, 12 | loaders: ['style', 'css'], 13 | }, { 14 | test: /\.js/, 15 | exclude: [/node_modules/], 16 | loader: 'babel', 17 | }, { 18 | test: /\.png/, 19 | loader: 'file', 20 | }, { 21 | test: /\.json/, 22 | loader: 'json', 23 | }], 24 | }, 25 | 26 | entry: { 27 | main: path.join(srcPath, '/index.js'), 28 | }, 29 | 30 | output: { 31 | path: outPath, 32 | filename: '[name].js', 33 | publicPath: '/', 34 | }, 35 | 36 | resolve: {}, 37 | 38 | plugins: [ 39 | new HtmlWebpackPlugin({ 40 | template: path.join(srcPath, 'index.html'), 41 | filename: 'index.html', 42 | inject: 'body', 43 | chunks: ['main'], 44 | }), 45 | ], 46 | 47 | externals: [], 48 | }; 49 | 50 | if (process.env.NODE_ENV === 'production') { 51 | config.plugins.push( 52 | new webpack.optimize.UglifyJsPlugin({ 53 | compress: { 54 | warnings: false, 55 | dead_code: true, 56 | drop_debugger: true, 57 | conditionals: true, 58 | unsafe: true, 59 | evaluate: true, 60 | booleans: true, 61 | loops: true, 62 | unused: true, 63 | if_return: true, 64 | join_vars: true, 65 | cascade: true, 66 | collapse_vars: true, 67 | negate_iife: true, 68 | pure_getters: true, 69 | drop_console: true, 70 | keep_fargs: false, 71 | }, 72 | 'screw-ie8': true, 73 | mangle: true, 74 | stats: true, 75 | }) 76 | ); 77 | } 78 | 79 | module.exports = config; 80 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](https://s15.postimg.org/f8msaljij/Screen_Shot_2016_11_13_at_18_44_52.png) 2 | 3 | JSGS is an experimental (WIP) javascript implementation of the Pico-8 fantasy console. 4 | 5 | #### Demos 6 | - [http://hello-world-p8.netlify.com/](http://hello-world-p8.netlify.com/) 7 | - [http://cast.netlify.com/](http://cast.netlify.com/) 8 | - [http://3dcamera.netlify.com/](http://3dcamera.netlify.com/) 9 | - [http://fireball.netlify.com/](http://fireball.netlify.com/) 10 | 11 | #### What's implemented? 12 | - Graphics api, font 13 | - Math api 14 | - Ram / Ram api 15 | - Reading cartridges, running lua code 16 | 17 | #### What's missing / Quirks 18 | - Sound (Help needed) 19 | - sspr function 20 | - Cursor 21 | - stat function 22 | - Not 100% compatible with Pico-8's Lua flavor. 23 | - No shorthand assignments 24 | - all() is not implemented (help needed) 25 | 26 | #### Extra features 27 | - Multiple screen (canvas) support 28 | - CRT Filter (optional) 29 | - On screen / Touch controller (very naìve now) 30 | - Custom color palette 31 | 32 | ### How to use? 33 | It's not yet packaged to be used by importing into your project. For now, the only way to run your cartridges 34 | is manually editing the cartridge url in `src/index.js` file. For cloning/installing/running the project, see 35 | "Development" section below. 36 | 37 | #### Classes / Options 38 | ###### JSGS 39 | The JSGS class is the class which we use for creating pico-8 machine instances. 40 | ```javascript 41 | const machine = new JSGS({ 42 | os: new OS(), // an "OS" instance 43 | devices: { 44 | controller: new KeyboardController(), // a "Controller" instance 45 | ram: new Ram(0x8000), // a "Ram" instance 46 | cartridge: "http://example.com/cartridge.p8.png", // url to a p8.png cartridge 47 | screen: [new Screen({ size: 128 })], // An array of "Screen" instances 48 | }, 49 | }); 50 | ``` 51 | 52 | ###### Ram 53 | ```javascipt 54 | // instantiate a 32kb ram 55 | const ram = new Ram(0x8000); 56 | ``` 57 | 58 | ###### Screen / CRTScreen 59 | ```javascript 60 | const screen = new Screen({ 61 | size: 128, // a px size (number) 62 | palette: Screen.greenPalette // And array of 16 hex colors. Defaults to pico-8 palette 63 | }); 64 | 65 | // Screen with crt filter 66 | const crtScreen = new CRTScreen({ 67 | size: 128, 68 | }); 69 | 70 | // Mount to dom 71 | screen.mountCanvas(element); 72 | crtScreen.mountCanvas(element); 73 | ``` 74 | 75 | ## Development 76 | ### Requirements 77 | For development, you will need [Node.js](http://nodejs.org/) >=6.0.0 and NPM(comes bundled with Node.js) installed on your environment. 78 | 79 | ### Install 80 | Clone the project and install dependencies: 81 | 82 | $ git clone https://github.com/burakcan/jsgs.git 83 | $ cd jsgs 84 | $ npm install 85 | 86 | ### Start & watch 87 | 88 | $ npm run start-dev 89 | 90 | Now you can browse the app at http://localhost:4000 91 | 92 | ### Build for production 93 | 94 | $ npm run build 95 | -------------------------------------------------------------------------------- /src/Screen/index.js: -------------------------------------------------------------------------------- 1 | export default class Screen { 2 | constructor(options) { 3 | this.type = 'defaultScreen'; 4 | this.px = options.size / 128; 5 | this.size = options.size; 6 | this.palette = options.palette || this.constructor.defaultPalette; 7 | this.canvas = this.createCanvas(options); 8 | this.ctx = this.canvas.getContext('2d'); 9 | } 10 | 11 | update(ram) { 12 | for (let addr = 0x6000; addr <= 0x7FFF; addr++) { 13 | const data = ram.arr[addr]; 14 | const dataBinary = ("000000000" + data.toString(2)).substr(-8); 15 | const [color1, color2] = [ 16 | this.palette[ 17 | parseInt(dataBinary.substring(0, 4), 2) 18 | ], 19 | this.palette[ 20 | parseInt(dataBinary.substring(4, 8), 2) 21 | ], 22 | ]; 23 | 24 | const i = addr - 0x6000; 25 | const x = (i * 2) % 128; 26 | const y = Math.floor(i / 64); 27 | 28 | this.ctx.fillStyle = color1; 29 | this.ctx.fillRect(x * this.px, y * this.px, this.px, this.px); 30 | 31 | this.ctx.fillStyle = color2; 32 | this.ctx.fillRect(x * this.px + this.px, y * this.px, this.px, this.px); 33 | }; 34 | } 35 | 36 | createCanvas({ element, size }) { 37 | const canvas = document.createElement('canvas'); 38 | canvas.width = canvas.height = size; 39 | 40 | canvas.style.dispay = 'block'; 41 | canvas.style.imageRendering = 'pixelated'; 42 | 43 | return canvas; 44 | } 45 | 46 | mountCanvas(element) { 47 | element.appendChild(this.canvas); 48 | } 49 | } 50 | 51 | Screen.utils = {}; 52 | 53 | Screen.utils.hexToRgb = function(hex) { 54 | var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); 55 | 56 | return result ? { 57 | r: parseInt(result[1], 16), 58 | g: parseInt(result[2], 16), 59 | b: parseInt(result[3], 16) 60 | } : null; 61 | } 62 | 63 | Screen.utils.componentToHex = function(c) { 64 | var hex = c.toString(16); 65 | return hex.length == 1 ? "0" + hex : hex; 66 | } 67 | 68 | Screen.utils.rgbToHex = function({r, g, b}) { 69 | return "#" + Screen.utils.componentToHex(r) + Screen.utils.componentToHex(g) + Screen.utils.componentToHex(b); 70 | } 71 | 72 | 73 | Screen.defaultPalette = [ 74 | '#000000', '#1D2B53', '#7E2553', '#008751', 75 | '#AB5236', '#5F574F', '#C2C3C7', '#FFF1E8', 76 | '#FF004D', '#FFA300', '#FFEC27', '#00E436', 77 | '#29ADFF', '#83769C', '#FF77A8', '#FFCCAA', 78 | ]; 79 | 80 | Screen.grayscalePalette = Screen.defaultPalette.map(color => { 81 | const { r, g, b } = Screen.utils.hexToRgb(color); 82 | const w = Math.floor(0.299 * r + 0.587 * g + 0.114 * b); 83 | return Screen.utils.rgbToHex({ r: w, g: w, b: w }); 84 | }); 85 | 86 | Screen.greenPalette = Screen.defaultPalette.map(color => { 87 | const { r, g, b } = Screen.utils.hexToRgb(color); 88 | const rbc = Math.floor((0.299 * r + 0.587 * g + 0.114 * b) * (1/2)); 89 | const gc = Math.floor(rbc * 2); 90 | return Screen.utils.rgbToHex({ r: rbc, g: gc, b: rbc }); 91 | }); 92 | -------------------------------------------------------------------------------- /src/OS/readCartridge.lib.js: -------------------------------------------------------------------------------- 1 | // http://www.lexaloffle.com/bbs/?tid=27845 2 | 3 | export default function readCartridge($bmp) { 4 | function pad(byte) { 5 | "use strict"; 6 | return byte.toString().replace(/^(.(..)*)$/, '0$1'); 7 | } 8 | 9 | function stringy(byte) { 10 | "use strict"; 11 | return pad(byte.toString(16)); 12 | } 13 | 14 | function buildString(str, len) { 15 | "use strict"; 16 | var reg = new RegExp('.{1,' + len + '}', 'g'); 17 | 18 | str = str.match(reg); 19 | str.forEach(function (m, idx) { 20 | str[idx] += "\n"; 21 | }); 22 | 23 | return str.join(''); 24 | } 25 | 26 | function decompress(code) { 27 | "use strict"; 28 | var mode = 0, copy, i = 8, lua = '', codelen = (code.charCodeAt(4) << 8) | code.charCodeAt(5); 29 | var dict = "\n 0123456789abcdefghijklmnopqrstuvwxyz!#%(){}[]<>+=/*:;.,~_".split(''); 30 | var byte, offset, length, buffer; 31 | 32 | while (lua.length < codelen) { 33 | byte = code.charCodeAt(i); 34 | 35 | if (mode === 1) { 36 | lua += code.charAt(i); 37 | mode = 0; 38 | } else if (mode === 2) { 39 | offset = lua.length - ((copy - 60) * 16 + (byte & 15)); 40 | length = (byte >> 4) + 2; 41 | buffer = lua.substring(offset, offset + length); 42 | lua += buffer; 43 | mode = 0; 44 | } else if (byte === 0) { 45 | mode = 1; 46 | } else if (byte <= 59) { 47 | lua += dict[byte - 1]; 48 | } else if (byte >= 60) { 49 | mode = 2; 50 | copy = byte; 51 | } 52 | 53 | i++; 54 | } 55 | 56 | return lua; 57 | } 58 | 59 | function getData(bmp) { 60 | "use strict"; 61 | var gfx = '', map = '', gff = '', music = '', sfx = '', lua = '', imgData = bmp.bitmap, dataLen = imgData.length, i = 0, n = 0, r, g, b, a, byte, compressed, loop, lastbyte, loops = [], mode, speed, start, end, notes = '', tmp_music = [], track, step, note, version; 62 | 63 | if (bmp.width !== 160 || bmp.height !== 205) { 64 | return alert('Image is the wrong size.'); 65 | } 66 | 67 | while (i < dataLen) { 68 | // get the last 2 bytes of each value and shift left if necessary 69 | r = (imgData[i++] & 3) << 4; 70 | g = (imgData[i++] & 3) << 2; 71 | b = (imgData[i++] & 3); 72 | a = (imgData[i++] & 3) << 6; 73 | 74 | // compile new byte, convert to hex and pad left if needed 75 | byte = (r | g | b | a); 76 | 77 | if (n < 8192) { 78 | // change endianness and append to output string 79 | gfx += stringy(byte).split('').reverse().join(''); 80 | } else if (n < 12288) { 81 | map += stringy(byte); 82 | } else if (n < 12544) { 83 | gff += stringy(byte); 84 | } else if (n < 12800) { 85 | track = Math.floor((n - 12544) / 4); 86 | note = stringy(byte & 127); 87 | 88 | if (n % 4 === 0) { 89 | tmp_music.push([null, '']); 90 | } 91 | 92 | tmp_music[track][0] = stringy(((byte & 128) >> 7 - n % 4) | tmp_music[track][0]); 93 | tmp_music[track][1] += note; 94 | } else if (n < 17152) { 95 | step = (n - 12800) % 68; 96 | 97 | if (step < 64 && n % 2 === 1) { 98 | note = (byte << 8) + lastbyte; 99 | notes += (stringy(note & 63) + ((note & 448) >> 6).toString(16) + ((note & 3584) >> 9).toString(16) + ((note & 28672) >> 12).toString(16)); 100 | } else if (step === 64) { 101 | mode = pad(byte); 102 | } else if (step === 65) { 103 | speed = byte; 104 | } else if (step === 66) { 105 | start = byte; 106 | } else if (step === 67) { 107 | end = byte; 108 | sfx += mode + stringy(speed) + stringy(start) + stringy(end) + notes + "\n"; 109 | notes = ''; 110 | } 111 | } else if (n < 32768) { 112 | if (n === 17152) { 113 | compressed = (byte === 58); 114 | } 115 | 116 | lua += String.fromCharCode(byte); 117 | } else if (n === 32768) { 118 | version = byte; 119 | } 120 | 121 | lastbyte = byte; 122 | n++; 123 | } 124 | 125 | if (compressed) lua = decompress(lua); 126 | gfx = buildString(gfx, 128); 127 | gff = buildString(gff, 256); 128 | map = buildString(map, 256); 129 | 130 | tmp_music.forEach(function (m) { 131 | music += m[0] + ' ' + m[1] + "\n"; 132 | }); 133 | 134 | const result = { 135 | version: version, 136 | code: lua, 137 | gfx: gfx.replace(/(\r\n|\n|\r)/gm,""), 138 | gff: gff.replace(/(\r\n|\n|\r)/gm,""), 139 | map: map.replace(/(\r\n|\n|\r)/gm,""), 140 | sfx: sfx.replace(/(\r\n|\n|\r)/gm,""), 141 | music: music.replace(/(\r\n|\n|\r)/gm,""), 142 | }; 143 | 144 | return result; 145 | } 146 | 147 | return getData($bmp); 148 | }; 149 | -------------------------------------------------------------------------------- /src/OS/index.js: -------------------------------------------------------------------------------- 1 | import getGraphicsFunctions from './graphics.lib'; 2 | import getSoundFunctions from './sound.lib'; 3 | import mathLib from './math.lib'; 4 | import readCartridge from './readCartridge.lib'; 5 | import lua2js from 'lua2js'; 6 | import escodegen from 'escodegen'; 7 | import estraverse from 'estraverse'; 8 | 9 | export default class OS { 10 | constructor(machine) { 11 | this.$ = {}; 12 | this.bootTime = 0; 13 | } 14 | 15 | sendEvent(type, machine, payload) { 16 | switch (type) { 17 | case 'boot': 18 | this.boot(machine); 19 | break; 20 | 21 | case 'cartridgeMount': 22 | this.boot(machine).then( 23 | () => this.cartridgeMount(machine) 24 | ); 25 | break; 26 | 27 | case 'cartridgeEject': 28 | this.boot(machine).then( 29 | () => this.cartridgeEject(machine) 30 | ); 31 | break; 32 | } 33 | 34 | return this; 35 | } 36 | 37 | boot(machine) { 38 | if (this.bootProgress) return this.bootProgress; 39 | this.bootProgress = new Promise(resolve => { 40 | this.$ = Object.assign( 41 | this.$, 42 | lua2js.stdlib, 43 | machine.devices.ram.api, 44 | mathLib, 45 | getGraphicsFunctions(machine.devices.ram), 46 | getSoundFunctions(machine.devices.ram), 47 | machine.devices.controller.api, 48 | { time: this.getUptime.bind(this) } 49 | ); 50 | 51 | // flush defaults to ram 52 | this.$.clip(); 53 | this.$.color(6); 54 | 55 | // return resolve(this); // DEVELOPMENT 56 | 57 | let i = 1; 58 | 59 | const loadingAnim = setInterval(() => { 60 | this.$.cls(); 61 | if (i >= 98) { 62 | this.bootTime = Date.now(); 63 | clearInterval(loadingAnim); 64 | resolve(this); 65 | } else { 66 | this.$.print("javascript gaming system", 4, 4, 8); 67 | if (i >= 20) this.$.print("checking devices", 4, 12, 7); 68 | 69 | if (i >= 40) { 70 | this.$.print('booting' + '.'.repeat(Math.floor((i-40) / 15)), 4, 20, 7); 71 | } 72 | 73 | i += parseInt(Math.random() * 2); 74 | } 75 | }, 10); 76 | }); 77 | 78 | return this.bootProgress; 79 | } 80 | 81 | cartridgeMount(machine) { 82 | const { cartridge } = machine.devices; 83 | 84 | this.$.print("reading cartridge", 4, 4, 7); 85 | 86 | this 87 | .loadCartridge(cartridge, machine) 88 | .then(cartridgeData => 89 | setTimeout(() => this.runCartridge(cartridgeData), 500) 90 | ); 91 | } 92 | 93 | cartridgeEject(machine) { 94 | console.log('ejected', machine); 95 | } 96 | 97 | loadCartridge(url, machine) { 98 | const png = new PngToy(); 99 | 100 | return ( 101 | png 102 | .fetch(url) 103 | .then(() => png.decode().then(readCartridge)) 104 | .then(cartridgeData => { 105 | let gfxi = 0; 106 | const gfxData = cartridgeData.gfx + cartridgeData.map + cartridgeData.gff; 107 | for (let x = 0x0000; x <= 0x30ff; x++) { 108 | machine.devices.ram.poke(x, 109 | parseInt(`${gfxData[gfxi]}${gfxData[gfxi+1]}`, 16) 110 | ); 111 | gfxi = gfxi + 2; 112 | } 113 | 114 | let sfxi = 0; 115 | for (let x = 0x3200; x <= 0x42ff; x++) { 116 | machine.devices.ram.poke(x, 117 | parseInt(`${cartridgeData.sfx[sfxi]}${cartridgeData.sfx[sfxi+1]}`, 16) 118 | ); 119 | sfxi = sfxi + 2; 120 | } 121 | 122 | return cartridgeData; 123 | }) 124 | ); 125 | } 126 | 127 | runCartridge(____cartridgeData____) { 128 | this.$.cls(); 129 | const ____BLACKLIST____ = 130 | Object 131 | .keys(window) 132 | .join() 133 | .replace('console', '_'); 134 | 135 | const ____self____ = this; 136 | const ____api____ = Object.keys(this.$).map(key => { 137 | if (typeof ____self____.$[key] === 'function') { 138 | return ` 139 | function ${ key }() { 140 | return ____self____.$['${key}'].apply(null, arguments); 141 | } 142 | `; 143 | } 144 | return ''; 145 | }); 146 | 147 | ____api____.push(`var __lua = ____self____.$.__lua`); 148 | 149 | /*h*/eval(` 150 | (function(${____BLACKLIST____}) { 151 | ${ ____api____.join(';') } 152 | 153 | function _init() {} 154 | function _update() {} 155 | function _draw() {} 156 | 157 | function add(tbl, item) { 158 | console.log(tbl, item); 159 | tbl[Object.keys(tbl).length] = item; 160 | } 161 | 162 | function all() { 163 | return [() => {}, () => {}, () => {}]; 164 | } 165 | 166 | function count(tbl) { 167 | return Object.keys(tbl).length; 168 | } 169 | 170 | function menuitem() {} 171 | 172 | ${ this.transpileLua(____cartridgeData____.code).replace('~=', '!=') } 173 | 174 | _init() 175 | _update(); 176 | _draw(); 177 | 178 | ____self____._draw = _draw; 179 | ____self____._update = _update; 180 | })(); 181 | `); 182 | } 183 | 184 | _update() {} 185 | _draw() {} 186 | 187 | update() { 188 | this._update(); 189 | this._draw(); 190 | } 191 | 192 | transpileLua(code) { 193 | const ast = lua2js.parse(code, { 194 | decorateLuaObjects: false, 195 | encloseWithFunctions: false, 196 | forceVar: false, 197 | luaCalls: false, 198 | luaOperators: false, 199 | noSharedObjects: false, 200 | allowRegularFunctions: true, 201 | }); 202 | 203 | const result = escodegen.generate(ast); 204 | const globalVars = []; 205 | 206 | estraverse.traverse(ast, { 207 | enter(node, parent) { 208 | if (node.type === "AssignmentExpression" && 209 | node.left.name && 210 | !globalVars.includes(node.left.name) 211 | ) { 212 | globalVars.push(node.left.name); 213 | } 214 | }, 215 | }); 216 | 217 | return ` 218 | var ${globalVars.join()}; 219 | ${result.substring(1, result.length - 1)} 220 | `; 221 | } 222 | 223 | getUptime() { 224 | return Date.now() - this.bootTime; 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /src/OS/font.lib.js: -------------------------------------------------------------------------------- 1 | export default { 2 | a: [ 3 | 1, 1, 1, 4 | 1, 0, 1, 5 | 1, 1, 1, 6 | 1, 0, 1, 7 | 1, 0, 1, 8 | ], 9 | b: [ 10 | 1, 1, 1, 11 | 1, 0, 1, 12 | 1, 1, 0, 13 | 1, 0, 1, 14 | 1, 1, 1, 15 | ], 16 | c: [ 17 | 0, 1, 1, 18 | 1, 0, 0, 19 | 1, 0, 0, 20 | 1, 0, 0, 21 | 0, 1, 1, 22 | ], 23 | d: [ 24 | 1, 1, 0, 25 | 1, 0, 1, 26 | 1, 0, 1, 27 | 1, 0, 1, 28 | 1, 1, 1, 29 | ], 30 | e: [ 31 | 1, 1, 1, 32 | 1, 0, 0, 33 | 1, 1, 0, 34 | 1, 0, 0, 35 | 1, 1, 1, 36 | ], 37 | f: [ 38 | 1, 1, 1, 39 | 1, 0, 0, 40 | 1, 1, 0, 41 | 1, 0, 0, 42 | 1, 0, 0, 43 | ], 44 | g: [ 45 | 0, 1, 1, 46 | 1, 0, 0, 47 | 1, 0, 0, 48 | 1, 0, 1, 49 | 1, 1, 1, 50 | ], 51 | h: [ 52 | 1, 0, 1, 53 | 1, 0, 1, 54 | 1, 1, 1, 55 | 1, 0, 1, 56 | 1, 0, 1, 57 | ], 58 | i: [ 59 | 1, 1, 1, 60 | 0, 1, 0, 61 | 0, 1, 0, 62 | 0, 1, 0, 63 | 1, 1, 1, 64 | ], 65 | j: [ 66 | 1, 1, 1, 67 | 0, 1, 0, 68 | 0, 1, 0, 69 | 0, 1, 0, 70 | 1, 1, 0, 71 | ], 72 | k: [ 73 | 1, 0, 1, 74 | 1, 0, 1, 75 | 1, 1, 0, 76 | 1, 0, 1, 77 | 1, 0, 1, 78 | ], 79 | l: [ 80 | 1, 0, 0, 81 | 1, 0, 0, 82 | 1, 0, 0, 83 | 1, 0, 0, 84 | 1, 1, 1, 85 | ], 86 | m: [ 87 | 1, 1, 1, 88 | 1, 1, 1, 89 | 1, 0, 1, 90 | 1, 0, 1, 91 | 1, 0, 1, 92 | ], 93 | n: [ 94 | 1, 1, 0, 95 | 1, 0, 1, 96 | 1, 0, 1, 97 | 1, 0, 1, 98 | 1, 0, 1, 99 | ], 100 | o: [ 101 | 0, 1, 1, 102 | 1, 0, 1, 103 | 1, 0, 1, 104 | 1, 0, 1, 105 | 1, 1, 0, 106 | ], 107 | p: [ 108 | 1, 1, 1, 109 | 1, 0, 1, 110 | 1, 1, 1, 111 | 1, 0, 0, 112 | 1, 0, 0, 113 | ], 114 | q: [ 115 | 0, 1, 0, 116 | 1, 0, 1, 117 | 1, 0, 1, 118 | 1, 1, 0, 119 | 0, 1, 1, 120 | ], 121 | r: [ 122 | 1, 1, 1, 123 | 1, 0, 1, 124 | 1, 1, 0, 125 | 1, 0, 1, 126 | 1, 0, 1, 127 | ], 128 | s: [ 129 | 0, 1, 1, 130 | 1, 0, 0, 131 | 1, 1, 1, 132 | 0, 0, 1, 133 | 1, 1, 0, 134 | ], 135 | t: [ 136 | 1, 1, 1, 137 | 0, 1, 0, 138 | 0, 1, 0, 139 | 0, 1, 0, 140 | 0, 1, 0, 141 | ], 142 | u: [ 143 | 1, 0, 1, 144 | 1, 0, 1, 145 | 1, 0, 1, 146 | 1, 0, 1, 147 | 0, 1, 1, 148 | ], 149 | v: [ 150 | 1, 0, 1, 151 | 1, 0, 1, 152 | 1, 0, 1, 153 | 1, 0, 1, 154 | 0, 1, 0, 155 | ], 156 | w: [ 157 | 1, 0, 1, 158 | 1, 0, 1, 159 | 1, 0, 1, 160 | 1, 1, 1, 161 | 1, 1, 1, 162 | ], 163 | x: [ 164 | 1, 0, 1, 165 | 1, 0, 1, 166 | 0, 1, 0, 167 | 1, 0, 1, 168 | 1, 0, 1, 169 | ], 170 | y: [ 171 | 1, 0, 1, 172 | 1, 0, 1, 173 | 1, 1, 1, 174 | 0, 0, 1, 175 | 1, 1, 1, 176 | ], 177 | z: [ 178 | 1, 1, 1, 179 | 0, 0, 1, 180 | 0, 1, 0, 181 | 1, 0, 0, 182 | 1, 1, 1, 183 | ], 184 | "0": [ 185 | 1, 1, 1, 186 | 1, 0, 1, 187 | 1, 0, 1, 188 | 1, 0, 1, 189 | 1, 1, 1, 190 | ], 191 | "1": [ 192 | 1, 1, 0, 193 | 0, 1, 0, 194 | 0, 1, 0, 195 | 0, 1, 0, 196 | 1, 1, 1, 197 | ], 198 | "2": [ 199 | 1, 1, 1, 200 | 0, 0, 1, 201 | 1, 1, 1, 202 | 1, 0, 0, 203 | 1, 1, 1, 204 | ], 205 | "3": [ 206 | 1, 1, 1, 207 | 0, 0, 1, 208 | 0, 1, 1, 209 | 0, 0, 1, 210 | 1, 1, 1, 211 | ], 212 | "4": [ 213 | 1, 0, 1, 214 | 1, 0, 1, 215 | 1, 1, 1, 216 | 0, 0, 1, 217 | 0, 0, 1, 218 | ], 219 | "5": [ 220 | 1, 1, 1, 221 | 1, 0, 0, 222 | 1, 1, 1, 223 | 0, 0, 1, 224 | 1, 1, 1, 225 | ], 226 | "6": [ 227 | 1, 0, 0, 228 | 1, 0, 0, 229 | 1, 1, 1, 230 | 1, 0, 1, 231 | 1, 1, 1, 232 | ], 233 | "7": [ 234 | 1, 1, 1, 235 | 0, 0, 1, 236 | 0, 0, 1, 237 | 0, 0, 1, 238 | 0, 0, 1, 239 | ], 240 | "8": [ 241 | 1, 1, 1, 242 | 1, 0, 1, 243 | 1, 1, 1, 244 | 1, 0, 1, 245 | 1, 1, 1, 246 | ], 247 | "9": [ 248 | 1, 1, 1, 249 | 1, 0, 1, 250 | 1, 1, 1, 251 | 0, 0, 1, 252 | 0, 0, 1, 253 | ], 254 | "!": [ 255 | 0, 1, 0, 256 | 0, 1, 0, 257 | 0, 1, 0, 258 | 0, 0, 0, 259 | 0, 1, 0, 260 | ], 261 | '"': [ 262 | 1, 0, 1, 263 | 1, 0, 1, 264 | 0, 0, 0, 265 | 0, 0, 0, 266 | 0, 0, 0, 267 | ], 268 | "#": [ 269 | 1, 0, 1, 270 | 1, 1, 1, 271 | 1, 0, 1, 272 | 1, 1, 1, 273 | 1, 0, 1, 274 | ], 275 | "%": [ 276 | 1, 0, 1, 277 | 0, 0, 1, 278 | 0, 1, 0, 279 | 1, 0, 0, 280 | 1, 0, 1, 281 | ], 282 | "'": [ 283 | 0, 1, 0, 284 | 1, 0, 0, 285 | 0, 0, 0, 286 | 0, 0, 0, 287 | 0, 0, 0, 288 | ], 289 | "(": [ 290 | 0, 1, 0, 291 | 1, 0, 0, 292 | 1, 0, 0, 293 | 1, 0, 0, 294 | 0, 1, 0, 295 | ], 296 | ")": [ 297 | 0, 1, 0, 298 | 0, 0, 1, 299 | 0, 0, 1, 300 | 0, 0, 1, 301 | 0, 1, 0, 302 | ], 303 | "*": [ 304 | 1, 0, 1, 305 | 0, 1, 0, 306 | 1, 1, 1, 307 | 0, 1, 0, 308 | 1, 0, 1, 309 | ], 310 | "+": [ 311 | 0, 0, 0, 312 | 0, 1, 0, 313 | 1, 1, 1, 314 | 0, 1, 0, 315 | 0, 0, 0, 316 | ], 317 | ",": [ 318 | 0, 0, 0, 319 | 0, 0, 0, 320 | 0, 0, 0, 321 | 0, 1, 0, 322 | 1, 0, 0, 323 | ], 324 | "-": [ 325 | 0, 0, 0, 326 | 0, 0, 0, 327 | 1, 1, 1, 328 | 0, 0, 0, 329 | 0, 0, 0, 330 | ], 331 | ".": [ 332 | 0, 0, 0, 333 | 0, 0, 0, 334 | 0, 0, 0, 335 | 0, 0, 0, 336 | 0, 1, 0, 337 | ], 338 | "/": [ 339 | 0, 0, 1, 340 | 0, 1, 0, 341 | 0, 1, 0, 342 | 0, 1, 0, 343 | 1, 0, 0, 344 | ], 345 | ":": [ 346 | 0, 0, 0, 347 | 0, 1, 0, 348 | 0, 0, 0, 349 | 0, 1, 0, 350 | 0, 0, 0, 351 | ], 352 | "<": [ 353 | 0, 0, 1, 354 | 0, 1, 0, 355 | 1, 0, 0, 356 | 0, 1, 0, 357 | 0, 0, 1, 358 | ], 359 | ">": [ 360 | 1, 0, 0, 361 | 0, 1, 0, 362 | 0, 0, 1, 363 | 0, 1, 0, 364 | 1, 0, 0, 365 | ], 366 | "=": [ 367 | 0, 0, 0, 368 | 1, 1, 1, 369 | 0, 0, 0, 370 | 1, 1, 1, 371 | 0, 0, 0, 372 | ], 373 | "?": [ 374 | 1, 1, 1, 375 | 0, 0, 1, 376 | 0, 1, 1, 377 | 0, 0, 0, 378 | 0, 1, 0, 379 | ], 380 | "[": [ 381 | 1, 1, 0, 382 | 1, 0, 0, 383 | 1, 0, 0, 384 | 1, 0, 0, 385 | 1, 1, 0, 386 | ], 387 | "]": [ 388 | 0, 1, 1, 389 | 0, 0, 1, 390 | 0, 0, 1, 391 | 0, 0, 1, 392 | 0, 1, 1, 393 | ], 394 | "^": [ 395 | 0, 1, 0, 396 | 1, 0, 1, 397 | 0, 0, 0, 398 | 0, 0, 0, 399 | 0, 0, 0, 400 | ], 401 | "~": [ 402 | 0, 0, 0, 403 | 0, 0, 1, 404 | 1, 1, 1, 405 | 1, 0, 0, 406 | 0, 0, 0, 407 | ], 408 | } 409 | -------------------------------------------------------------------------------- /src/OS/graphics.lib.js: -------------------------------------------------------------------------------- 1 | import font from './font.lib'; 2 | 3 | export default function getGraphicsFunctions(ram) { 4 | function memoize(fn) { 5 | return function () { 6 | var args = Array.prototype.slice.call(arguments), 7 | hash = "", 8 | i = args.length; 9 | let currentArg = null; 10 | while (i--) { 11 | currentArg = args[i]; 12 | hash += (currentArg === Object(currentArg)) ? 13 | JSON.stringify(currentArg) : currentArg; 14 | fn.memoize || (fn.memoize = {}); 15 | } 16 | return (hash in fn.memoize) ? fn.memoize[hash] : 17 | fn.memoize[hash] = fn.apply(this, args); 18 | }; 19 | } 20 | 21 | function _color8toHexStr(color8) { 22 | return ( 23 | (str => '00'.substring(0, 2 - str.length) + str) 24 | (color8.toString(16)) 25 | ); 26 | } 27 | 28 | const flags = [0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80]; 29 | const initialColors = [ 30 | 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 31 | 0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0, 32 | ]; 33 | 34 | const color8toHexStr = memoize(_color8toHexStr); 35 | 36 | function pget(x, y) { 37 | x = Math.floor(x); 38 | y = Math.floor(y); 39 | 40 | if ( 41 | x < 0 || x > 127 || 42 | y < 0 || y > 127 43 | ) { 44 | return 0; 45 | } 46 | 47 | const addr = 0x6000 + (64 * y) + Math.floor(x / 2); 48 | const side = x % 2 === 0 ? 'left' : 'right'; 49 | 50 | if (side === 'left') { 51 | return parseInt(color8toHexStr(ram.peek(addr))[0], 16); 52 | } else if (side === 'right') { 53 | return parseInt(color8toHexStr(ram.peek(addr))[0], 16); 54 | } 55 | } 56 | 57 | function pset(x, y, colorIndex, color8) { 58 | const [camX, camY] = _getCamera(); 59 | const [cx1, cy1, cx2, cy2] = _getClip(); 60 | 61 | x = Math.floor(x + camX); 62 | y = Math.floor(y + camY); 63 | 64 | if ( 65 | x < cx1 || x > cx2 || 66 | y < cy1 || y > cy2 67 | ) { 68 | return false; 69 | } 70 | 71 | const drawPalette = ram.memread(0x5000, 16); 72 | 73 | if (colorIndex === undefined) { 74 | colorIndex = ram.arr[0x5f25]; // default color: color() 75 | } 76 | 77 | let colorStr; 78 | 79 | if (!color8) { 80 | color8 = drawPalette[colorIndex % 16]; 81 | } else { 82 | color8 = drawPalette[initialColors.indexOf(color8)]; 83 | } 84 | 85 | const addr = 0x6000 + (64 * y) + Math.floor(x / 2); 86 | const beforePoke = color8toHexStr(ram.arr[addr]); 87 | const side = x % 2 === 0 ? 'left' : 'right'; 88 | 89 | if (side === 'left') { 90 | colorStr = color8toHexStr(color8)[0] + beforePoke[1]; 91 | } else if (side === 'right') { 92 | colorStr = beforePoke[0] + color8toHexStr(color8)[0]; 93 | } 94 | 95 | ram.arr[addr] = parseInt(colorStr, 16); 96 | } 97 | 98 | function cls() { 99 | ram.memset(0x6000, 0x00, 0x8000 - 0x6000); 100 | } 101 | 102 | function line(x0, y0, x1, y1, colorIndex){ 103 | x0 = Math.floor(x0); 104 | y0 = Math.floor(y0); 105 | x1 = Math.floor(x1); 106 | y1 = Math.floor(y1); 107 | 108 | if (colorIndex === undefined) { 109 | colorIndex = ram.peek(0x5f25); 110 | } 111 | 112 | let dx = Math.abs(x1-x0); 113 | let dy = Math.abs(y1-y0); 114 | let sx = (x0 < x1) ? 1 : -1; 115 | let sy = (y0 < y1) ? 1 : -1; 116 | let err = dx-dy; 117 | 118 | while(true){ 119 | pset(x0, y0, colorIndex); 120 | 121 | if ((x0 === x1) && (y0 === y1)) break; 122 | const e2 = 2 * err; 123 | if (e2 >- dy){ err -= dy; x0 += sx; } 124 | if (e2 < dx){ err += dx; y0 += sy; } 125 | } 126 | } 127 | 128 | function rect(x1, y1, x2, y2, colorIndex) { 129 | if (colorIndex === undefined) { 130 | colorIndex = ram.peek(0x5f25); 131 | } 132 | 133 | line(x1, y1, x2, y1, colorIndex); 134 | line(x1, y1, x1, y2, colorIndex); 135 | line(x2, y1, x2, y2, colorIndex); 136 | line(x1, y2, x2, y2, colorIndex); 137 | } 138 | 139 | function rectfill(x1, y1, x2, y2, colorIndex) { 140 | if (colorIndex === undefined) { 141 | colorIndex = ram.peek(0x5f25); 142 | } 143 | 144 | for (let i = y1; i < y2; i++) { 145 | for (let j = x1; j < x2 ; j++) { 146 | pset(j, i, colorIndex); 147 | } 148 | } 149 | } 150 | 151 | function circ(x0, y0, r, colorIndex) { 152 | x0 = Math.floor(x0); 153 | y0 = Math.floor(y0); 154 | r = Math.floor(r); 155 | 156 | if (colorIndex === undefined) { 157 | colorIndex = ram.peek(0x5f25); 158 | } 159 | 160 | let x = r - 1; 161 | let y = 0; 162 | let rError = 1 - x; 163 | 164 | while (x >= y) { 165 | pset(x + x0, y + y0, colorIndex); 166 | pset(y + x0, x + y0, colorIndex); 167 | pset(-x + x0, y + y0, colorIndex); 168 | pset(-y + x0, x + y0, colorIndex); 169 | pset(-x + x0, -y + y0, colorIndex); 170 | pset(-y + x0, -x + y0, colorIndex); 171 | pset(x + x0, -y + y0, colorIndex); 172 | pset(y + x0, -x + y0, colorIndex); 173 | y++; 174 | 175 | if (rError < 0) { 176 | rError += 2 * y + 1; 177 | } else { 178 | x--; 179 | rError+= 2 * (y - x + 1); 180 | } 181 | } 182 | } 183 | 184 | function circfill(x0, y0, radius, colorIndex) { 185 | let x = radius - 1; 186 | let y = 0; 187 | let radiusError = 1 - x; 188 | 189 | if (colorIndex === undefined) { 190 | colorIndex = ram.peek(0x5f25); 191 | } 192 | 193 | while (x >= y) { 194 | line(-x + x0, y + y0, x + x0, y + y0, colorIndex); 195 | line(-y + x0, x + y0, y + x0, x + y0, colorIndex); 196 | line(-x + x0, -y + y0, x + x0, -y + y0, colorIndex); 197 | line(-y + x0, -x + y0, y + x0, -x + y0, colorIndex); 198 | y++; 199 | 200 | if (radiusError < 0) { 201 | radiusError += 2 * y + 1; 202 | } else { 203 | x--; 204 | radiusError+= 2 * (y - x + 1); 205 | } 206 | } 207 | } 208 | 209 | function mset(celX, celY, sNum) { 210 | let mapStartAddr = 0x2000; // Map (rows 0-31) 211 | 212 | if (celY >= 32) { 213 | mapStartAddr = 0x1000; 214 | } 215 | 216 | const tile = (celY * 128) + celX; 217 | ram.arr[mapStartAddr + tile] = sNum; 218 | } 219 | 220 | function mget(celX, celY) { 221 | celX = Math.floor(celX); 222 | celY = Math.floor(celY); 223 | 224 | let mapStartAddr = 0x2000; // Map (rows 0-31) 225 | 226 | if (celY >= 32) { 227 | mapStartAddr = 0x1000; 228 | } 229 | 230 | const tile = (celY * 128) + celX; 231 | return ram.arr[mapStartAddr + tile]; 232 | } 233 | 234 | function map(celX, celY, sx, sy, celW, celH, layer) { 235 | for (let x = celX; x < celX + celW; x++) { 236 | for (let y = celY; y < celY + celH; y++) { 237 | const sprNum = mget(x, y); 238 | 239 | if (layer !== undefined && !fget(sprNum, layer)) { 240 | continue; 241 | } 242 | 243 | if (sprNum !== 0) { 244 | spr(sprNum, sx + ((x - celX) * 8), sy + ((y - celY) * 8)); 245 | } 246 | } 247 | } 248 | } 249 | 250 | function spr(n, x, y, w, h, flip_x, flip_y) { 251 | x = Math.floor(x); 252 | y = Math.floor(y); 253 | 254 | const sx = (n % 16) * 4; 255 | const sy = Math.floor(n / 16) * 8; 256 | const startAddr = 0x0000 + sx + (sy * 64); 257 | const drawPalette = ram.memread(0x5000, 16); 258 | 259 | for (let i = 0; i < 8; i++) { 260 | for (let j = 0; j < 8; j=j+2) { 261 | const val = ram.arr[startAddr + (64 * i) + j / 2]; 262 | const str = color8toHexStr(val); 263 | 264 | let x1 = flip_x ? x - j + 7 : x + j; 265 | let x2 = flip_x ? x - j + 6 : x + j + 1; 266 | 267 | let y1 = flip_y ? y - i + 7 : y + i; 268 | let y2 = y1; 269 | 270 | if (color8toHexStr(drawPalette[parseInt(str[0], 16)])[1] === "0") { 271 | pset( 272 | x1, 273 | y1, 274 | null, 275 | parseInt(str[0] + '0', 16) 276 | ); 277 | } 278 | 279 | if (color8toHexStr(drawPalette[parseInt(str[1], 16)])[1] === "0") { 280 | pset( 281 | x2, 282 | y2, 283 | null, 284 | parseInt(str[1] + '0', 16) 285 | ); 286 | } 287 | } 288 | } 289 | } 290 | 291 | function sget(x, y) { 292 | x = Math.floor(x); 293 | y = Math.floor(y); 294 | 295 | const addr = 0x0000 + (64 * y) + Math.floor(x / 2); 296 | const side = x % 2 === 0 ? 'left' : 'right'; 297 | 298 | if (side === 'left') { 299 | return parseInt(color8toHexStr(ram.peek(addr))[0], 16); 300 | } else if (side === 'right') { 301 | return parseInt(color8toHexStr(ram.peek(addr))[0], 16); 302 | } 303 | } 304 | 305 | function sset(x, y, colorIndex) { 306 | x = Math.floor(x); 307 | y = Math.floor(y); 308 | 309 | const drawPalette = ram.memread(0x5000, 16); 310 | 311 | if (colorIndex === undefined) { 312 | colorIndex = ram.peek(0x5f25); 313 | } 314 | 315 | let colorStr; 316 | 317 | const color8 = drawPalette[colorIndex % 16]; 318 | const addr = 0x0000 + (64 * y) + Math.floor(x / 2); 319 | const beforePoke = color8toHexStr(ram.peek(addr)); 320 | const side = x % 2 === 0 ? 'left' : 'right'; 321 | 322 | if (side === 'left') { 323 | colorStr = color8toHexStr(color8)[0] + beforePoke[1]; 324 | } else if (side === 'right') { 325 | colorStr = beforePoke[0] + color8toHexStr(color8)[0]; 326 | } 327 | 328 | ram.poke(addr, parseInt(colorStr, 16)); 329 | } 330 | 331 | function print(text, x = 0, y = 0, colorIndex) { 332 | x = Math.floor(x); 333 | y = Math.floor(y); 334 | text = String(text); 335 | 336 | if (colorIndex === undefined) { 337 | colorIndex = ram.peek(0x5f25); 338 | } 339 | 340 | text = text.toLowerCase(); 341 | for (let i = 0; i < text.length; i++) { 342 | const letter = font[text[i]]; 343 | 344 | if (!letter) continue; 345 | 346 | for (let j = 0; j < 15; j++) { 347 | const lx = j % 3; 348 | const ly = Math.floor(j / 3); 349 | 350 | if (letter[j]) { 351 | pset( 352 | lx + x + (i * 4), 353 | ly + y, 354 | colorIndex 355 | ); 356 | } 357 | } 358 | } 359 | } 360 | 361 | function pal(c1, c2, p = 0) { 362 | if (c1 === undefined && c2 === undefined) { 363 | ram.memwrite(0x5000, initialColors); // load initial draw palette to ram 364 | ram.memwrite(0x5f10, initialColors); // load initial screen palette to ram 365 | return; 366 | } 367 | 368 | let startAddr = 0x5000; // draw palette 369 | 370 | if (p === 1) { 371 | startAddr = 0x5f10; 372 | } 373 | 374 | const drawPalette = ram.memread(startAddr, 16); 375 | drawPalette[c1] = initialColors[c2]; 376 | ram.memwrite(startAddr, drawPalette); 377 | } 378 | 379 | function palt(c, t) { 380 | if (c === undefined && t === undefined) { 381 | ram.memwrite(0x5000, initialColors); // load initial draw palette to ram 382 | palt(0, true); 383 | return; 384 | } 385 | 386 | const current = ram.peek(0x5000 + c); 387 | let str = color8toHexStr(current); 388 | 389 | if (t) { 390 | str = str[0] + "1"; 391 | } else { 392 | str = str[0] + "0"; 393 | } 394 | 395 | ram.poke(0x5000 + c, parseInt(str, 16)); 396 | } 397 | 398 | function _getCamera() { 399 | const xSign = ram.arr[0x5f28] === 1 ? -1 : 1; 400 | const ySign = ram.arr[0x5f2a] === 1 ? -1 : 1; 401 | 402 | const x = ram.arr[0x5f29]; 403 | const y = ram.arr[0x5f2b]; 404 | 405 | return [ 406 | x * xSign, 407 | y * ySign, 408 | ]; 409 | } 410 | 411 | function camera(x = 0, y = 0) { 412 | x = Math.floor(x); 413 | y = Math.floor(y); 414 | 415 | if (x < 0) { ram.poke(0x5f28, 1) } else { ram.poke(0x5f28, 0) } 416 | if (y < 0) { ram.poke(0x5f2a, 1) } else { ram.poke(0x5f2a, 0) } 417 | 418 | ram.poke(0x5f29, Math.abs(x)); 419 | ram.poke(0x5f2b, Math.abs(y)); 420 | } 421 | 422 | function _getClip() { 423 | return [ 424 | ram.arr[0x5f20], 425 | ram.arr[0x5f21], 426 | ram.arr[0x5f22], 427 | ram.arr[0x5f23], 428 | ]; 429 | } 430 | 431 | function clip(x = 0, y = 0, w = 127, h = 127) { 432 | x = Math.floor(x); 433 | y = Math.floor(y); 434 | w = Math.floor(w); 435 | h = Math.floor(h); 436 | 437 | ram.poke(0x5f20, x); 438 | ram.poke(0x5f21, y); 439 | ram.poke(0x5f22, x + w); 440 | ram.poke(0x5f23, y + h); 441 | } 442 | 443 | function color(colorIndex = 0) { 444 | ram.poke(0x5f25, colorIndex); 445 | } 446 | 447 | function _getCursor() { 448 | // TODO: Implement usage of this to print 449 | return [ 450 | ram.peek(0x5f26), 451 | ram.peek(0x5f27), 452 | ]; 453 | } 454 | 455 | function cursor(x, y) { 456 | // TODO: Implement usage of this to print 457 | 458 | x = Math.floor(x); 459 | y = Math.floor(y); 460 | 461 | ram.poke(0x5f26, x); 462 | ram.poke(0x5f27, y); 463 | } 464 | 465 | function fget(n, f) { 466 | const addr = 0x3000 + n; 467 | 468 | if (f === undefined) { 469 | return ram.arr[addr]; 470 | } 471 | 472 | return (ram.arr[addr] & flags[f]) === flags[f]; 473 | } 474 | 475 | function fset(n, f, v) { 476 | const addr = 0x3000 + n; 477 | 478 | if (arguments.length === 2) { 479 | return ram.poke(addr, f); 480 | } 481 | 482 | if (fget(n, f) && v) { 483 | return; 484 | } 485 | 486 | if (fget(n, f) && !v) { 487 | ram.poke(addr, fget(n) - flags[f]); 488 | } 489 | 490 | if (!fget(n, f) && v) { 491 | ram.poke(addr, fget(n) + flags[f]); 492 | } 493 | } 494 | 495 | function stat() { 496 | return ''; 497 | } 498 | 499 | ram.memwrite(0x5000, initialColors); // load initial draw palette to ram 500 | ram.memwrite(0x5f10, initialColors); // load initial screen palette to ram 501 | palt(0, true); 502 | 503 | return { 504 | camera, 505 | clip, 506 | cls, 507 | color, 508 | cursor, 509 | fget, 510 | fset, 511 | pget, 512 | pset, 513 | line, 514 | rect, 515 | rectfill, 516 | circ, 517 | circfill, 518 | spr, 519 | sget, 520 | sset, 521 | print, 522 | pal, 523 | palt, 524 | map, 525 | mget, 526 | mset, 527 | stat, 528 | }; 529 | } 530 | -------------------------------------------------------------------------------- /src/vendor/glfx.js: -------------------------------------------------------------------------------- 1 | /* 2 | * glfx.js 3 | * http://evanw.github.com/glfx.js/ 4 | * 5 | * Copyright 2011 Evan Wallace 6 | * Released under the MIT license 7 | */ 8 | window.fx=function(){function q(a,d,c){return Math.max(a,Math.min(d,c))}function w(b){return{_:b,loadContentsOf:function(b){a=this._.gl;this._.loadContentsOf(b)},destroy:function(){a=this._.gl;this._.destroy()}}}function A(a){return w(r.fromElement(a))}function B(b,d){var c=a.UNSIGNED_BYTE;if(a.getExtension("OES_texture_float")&&a.getExtension("OES_texture_float_linear")){var e=new r(100,100,a.RGBA,a.FLOAT);try{e.drawTo(function(){c=a.FLOAT})}catch(g){}e.destroy()}this._.texture&&this._.texture.destroy(); 9 | this._.spareTexture&&this._.spareTexture.destroy();this.width=b;this.height=d;this._.texture=new r(b,d,a.RGBA,c);this._.spareTexture=new r(b,d,a.RGBA,c);this._.extraTexture=this._.extraTexture||new r(0,0,a.RGBA,c);this._.flippedShader=this._.flippedShader||new h(null,"uniform sampler2D texture;varying vec2 texCoord;void main(){gl_FragColor=texture2D(texture,vec2(texCoord.x,1.0-texCoord.y));}");this._.isInitialized=!0}function C(a,d,c){this._.isInitialized&& 10 | a._.width==this.width&&a._.height==this.height||B.call(this,d?d:a._.width,c?c:a._.height);a._.use();this._.texture.drawTo(function(){h.getDefaultShader().drawRect()});return this}function D(){this._.texture.use();this._.flippedShader.drawRect();return this}function f(a,d,c,e){(c||this._.texture).use();this._.spareTexture.drawTo(function(){a.uniforms(d).drawRect()});this._.spareTexture.swapWith(e||this._.texture)}function E(a){a.parentNode.insertBefore(this,a);a.parentNode.removeChild(a);return this} 11 | function F(){var b=new r(this._.texture.width,this._.texture.height,a.RGBA,a.UNSIGNED_BYTE);this._.texture.use();b.drawTo(function(){h.getDefaultShader().drawRect()});return w(b)}function G(){var b=this._.texture.width,d=this._.texture.height,c=new Uint8Array(4*b*d);this._.texture.drawTo(function(){a.readPixels(0,0,b,d,a.RGBA,a.UNSIGNED_BYTE,c)});return c}function k(b){return function(){a=this._.gl;return b.apply(this,arguments)}}function x(a,d,c,e,g,l,n,p){var m=c-g,h=e-l,f=n-g,k=p-l;g=a-c+g-n;l= 12 | d-e+l-p;var q=m*k-f*h,f=(g*k-f*l)/q,m=(m*l-g*h)/q;return[c-a+f*c,e-d+f*e,f,n-a+m*n,p-d+m*p,m,a,d,1]}function y(a){var d=a[0],c=a[1],e=a[2],g=a[3],l=a[4],n=a[5],p=a[6],m=a[7];a=a[8];var f=d*l*a-d*n*m-c*g*a+c*n*p+e*g*m-e*l*p;return[(l*a-n*m)/f,(e*m-c*a)/f,(c*n-e*l)/f,(n*p-g*a)/f,(d*a-e*p)/f,(e*g-d*n)/f,(g*m-l*p)/f,(c*p-d*m)/f,(d*l-c*g)/f]}function z(a){var d=a.length;this.xa=[];this.ya=[];this.u=[];this.y2=[];a.sort(function(a,b){return a[0]-b[0]});for(var c=0;c0.0){color.rgb=(color.rgb-0.5)/(1.0-contrast)+0.5;}else{color.rgb=(color.rgb-0.5)*(1.0+contrast)+0.5;}gl_FragColor=color;}"); 15 | f.call(this,a.brightnessContrast,{brightness:q(-1,b,1),contrast:q(-1,d,1)});return this}function t(a){a=new z(a);for(var d=[],c=0;256>c;c++)d.push(q(0,Math.floor(256*a.interpolate(c/255)),255));return d}function I(b,d,c){b=t(b);1==arguments.length?d=c=b:(d=t(d),c=t(c));for(var e=[],g=0;256>g;g++)e.splice(e.length,0,b[g],d[g],c[g],255);this._.extraTexture.initFromBytes(256,1,e);this._.extraTexture.use(1);a.curves=a.curves||new h(null,"uniform sampler2D texture;uniform sampler2D map;varying vec2 texCoord;void main(){vec4 color=texture2D(texture,texCoord);color.r=texture2D(map,vec2(color.r)).r;color.g=texture2D(map,vec2(color.g)).g;color.b=texture2D(map,vec2(color.b)).b;gl_FragColor=color;}"); 16 | a.curves.textures({map:1});f.call(this,a.curves,{});return this}function J(b){a.denoise=a.denoise||new h(null,"uniform sampler2D texture;uniform float exponent;uniform float strength;uniform vec2 texSize;varying vec2 texCoord;void main(){vec4 center=texture2D(texture,texCoord);vec4 color=vec4(0.0);float total=0.0;for(float x=-4.0;x<=4.0;x+=1.0){for(float y=-4.0;y<=4.0;y+=1.0){vec4 sample=texture2D(texture,texCoord+vec2(x,y)/texSize);float weight=1.0-abs(dot(sample.rgb-center.rgb,vec3(0.25)));weight=pow(weight,exponent);color+=sample*weight;total+=weight;}}gl_FragColor=color/total;}"); 17 | for(var d=0;2>d;d++)f.call(this,a.denoise,{exponent:Math.max(0,b),texSize:[this.width,this.height]});return this}function K(b,d){a.hueSaturation=a.hueSaturation||new h(null,"uniform sampler2D texture;uniform float hue;uniform float saturation;varying vec2 texCoord;void main(){vec4 color=texture2D(texture,texCoord);float angle=hue*3.14159265;float s=sin(angle),c=cos(angle);vec3 weights=(vec3(2.0*c,-sqrt(3.0)*s-c,sqrt(3.0)*s-c)+1.0)/3.0;float len=length(color.rgb);color.rgb=vec3(dot(color.rgb,weights.xyz),dot(color.rgb,weights.zxy),dot(color.rgb,weights.yzx));float average=(color.r+color.g+color.b)/3.0;if(saturation>0.0){color.rgb+=(average-color.rgb)*(1.0-1.0/(1.001-saturation));}else{color.rgb+=(average-color.rgb)*(-saturation);}gl_FragColor=color;}"); 18 | f.call(this,a.hueSaturation,{hue:q(-1,b,1),saturation:q(-1,d,1)});return this}function L(b){a.noise=a.noise||new h(null,"uniform sampler2D texture;uniform float amount;varying vec2 texCoord;float rand(vec2 co){return fract(sin(dot(co.xy,vec2(12.9898,78.233)))*43758.5453);}void main(){vec4 color=texture2D(texture,texCoord);float diff=(rand(texCoord)-0.5)*amount;color.r+=diff;color.g+=diff;color.b+=diff;gl_FragColor=color;}"); 19 | f.call(this,a.noise,{amount:q(0,b,1)});return this}function M(b){a.sepia=a.sepia||new h(null,"uniform sampler2D texture;uniform float amount;varying vec2 texCoord;void main(){vec4 color=texture2D(texture,texCoord);float r=color.r;float g=color.g;float b=color.b;color.r=min(1.0,(r*(1.0-(0.607*amount)))+(g*(0.769*amount))+(b*(0.189*amount)));color.g=min(1.0,(r*0.349*amount)+(g*(1.0-(0.314*amount)))+(b*0.168*amount));color.b=min(1.0,(r*0.272*amount)+(g*0.534*amount)+(b*(1.0-(0.869*amount))));gl_FragColor=color;}"); 20 | f.call(this,a.sepia,{amount:q(0,b,1)});return this}function N(b,d){a.unsharpMask=a.unsharpMask||new h(null,"uniform sampler2D blurredTexture;uniform sampler2D originalTexture;uniform float strength;uniform float threshold;varying vec2 texCoord;void main(){vec4 blurred=texture2D(blurredTexture,texCoord);vec4 original=texture2D(originalTexture,texCoord);gl_FragColor=mix(blurred,original,1.0+strength);}"); 21 | this._.extraTexture.ensureFormat(this._.texture);this._.texture.use();this._.extraTexture.drawTo(function(){h.getDefaultShader().drawRect()});this._.extraTexture.use(1);this.triangleBlur(b);a.unsharpMask.textures({originalTexture:1});f.call(this,a.unsharpMask,{strength:d});this._.extraTexture.unuse(1);return this}function O(b){a.vibrance=a.vibrance||new h(null,"uniform sampler2D texture;uniform float amount;varying vec2 texCoord;void main(){vec4 color=texture2D(texture,texCoord);float average=(color.r+color.g+color.b)/3.0;float mx=max(color.r,max(color.g,color.b));float amt=(mx-average)*(-amount*3.0);color.rgb=mix(color.rgb,vec3(mx),amt);gl_FragColor=color;}"); 22 | f.call(this,a.vibrance,{amount:q(-1,b,1)});return this}function P(b,d){a.vignette=a.vignette||new h(null,"uniform sampler2D texture;uniform float size;uniform float amount;varying vec2 texCoord;void main(){vec4 color=texture2D(texture,texCoord);float dist=distance(texCoord,vec2(0.5,0.5));color.rgb*=smoothstep(0.8,size*0.799,dist*(amount+size));gl_FragColor=color;}"); 23 | f.call(this,a.vignette,{size:q(0,b,1),amount:q(0,d,1)});return this}function Q(b,d,c){a.lensBlurPrePass=a.lensBlurPrePass||new h(null,"uniform sampler2D texture;uniform float power;varying vec2 texCoord;void main(){vec4 color=texture2D(texture,texCoord);color=pow(color,vec4(power));gl_FragColor=vec4(color);}");var e="uniform sampler2D texture0;uniform sampler2D texture1;uniform vec2 delta0;uniform vec2 delta1;uniform float power;varying vec2 texCoord;"+ 24 | s+"vec4 sample(vec2 delta){float offset=random(vec3(delta,151.7182),0.0);vec4 color=vec4(0.0);float total=0.0;for(float t=0.0;t<=30.0;t++){float percent=(t+offset)/30.0;color+=texture2D(texture0,texCoord+delta*percent);total+=1.0;}return color/total;}"; 25 | a.lensBlur0=a.lensBlur0||new h(null,e+"void main(){gl_FragColor=sample(delta0);}");a.lensBlur1=a.lensBlur1||new h(null,e+"void main(){gl_FragColor=(sample(delta0)+sample(delta1))*0.5;}");a.lensBlur2=a.lensBlur2||(new h(null,e+"void main(){vec4 color=(sample(delta0)+2.0*texture2D(texture1,texCoord))/3.0;gl_FragColor=pow(color,vec4(power));}")).textures({texture1:1});for(var e= 26 | [],g=0;3>g;g++){var l=c+2*g*Math.PI/3;e.push([b*Math.sin(l)/this.width,b*Math.cos(l)/this.height])}b=Math.pow(10,q(-1,d,1));f.call(this,a.lensBlurPrePass,{power:b});this._.extraTexture.ensureFormat(this._.texture);f.call(this,a.lensBlur0,{delta0:e[0]},this._.texture,this._.extraTexture);f.call(this,a.lensBlur1,{delta0:e[1],delta1:e[2]},this._.extraTexture,this._.extraTexture);f.call(this,a.lensBlur0,{delta0:e[1]});this._.extraTexture.use(1);f.call(this,a.lensBlur2,{power:1/b,delta0:e[2]});return this} 27 | function R(b,d,c,e,g,l){a.tiltShift=a.tiltShift||new h(null,"uniform sampler2D texture;uniform float blurRadius;uniform float gradientRadius;uniform vec2 start;uniform vec2 end;uniform vec2 delta;uniform vec2 texSize;varying vec2 texCoord;"+s+"void main(){vec4 color=vec4(0.0);float total=0.0;float offset=random(vec3(12.9898,78.233,151.7182),0.0);vec2 normal=normalize(vec2(start.y-end.y,end.x-start.x));float radius=smoothstep(0.0,1.0,abs(dot(texCoord*texSize-start,normal))/gradientRadius)*blurRadius;for(float t=-30.0;t<=30.0;t++){float percent=(t+offset-0.5)/30.0;float weight=1.0-abs(percent);vec4 sample=texture2D(texture,texCoord+delta/texSize*percent*radius);sample.rgb*=sample.a;color+=sample*weight;total+=weight;}gl_FragColor=color/total;gl_FragColor.rgb/=gl_FragColor.a+0.00001;}"); 28 | var n=c-b,p=e-d,m=Math.sqrt(n*n+p*p);f.call(this,a.tiltShift,{blurRadius:g,gradientRadius:l,start:[b,d],end:[c,e],delta:[n/m,p/m],texSize:[this.width,this.height]});f.call(this,a.tiltShift,{blurRadius:g,gradientRadius:l,start:[b,d],end:[c,e],delta:[-p/m,n/m],texSize:[this.width,this.height]});return this}function S(b){a.triangleBlur=a.triangleBlur||new h(null,"uniform sampler2D texture;uniform vec2 delta;varying vec2 texCoord;"+s+"void main(){vec4 color=vec4(0.0);float total=0.0;float offset=random(vec3(12.9898,78.233,151.7182),0.0);for(float t=-30.0;t<=30.0;t++){float percent=(t+offset-0.5)/30.0;float weight=1.0-abs(percent);vec4 sample=texture2D(texture,texCoord+delta*percent);sample.rgb*=sample.a;color+=sample*weight;total+=weight;}gl_FragColor=color/total;gl_FragColor.rgb/=gl_FragColor.a+0.00001;}"); 29 | f.call(this,a.triangleBlur,{delta:[b/this.width,0]});f.call(this,a.triangleBlur,{delta:[0,b/this.height]});return this}function T(b,d,c){a.zoomBlur=a.zoomBlur||new h(null,"uniform sampler2D texture;uniform vec2 center;uniform float strength;uniform vec2 texSize;varying vec2 texCoord;"+s+"void main(){vec4 color=vec4(0.0);float total=0.0;vec2 toCenter=center-texCoord*texSize;float offset=random(vec3(12.9898,78.233,151.7182),0.0);for(float t=0.0;t<=40.0;t++){float percent=(t+offset)/40.0;float weight=4.0*(percent-percent*percent);vec4 sample=texture2D(texture,texCoord+toCenter*percent*strength/texSize);sample.rgb*=sample.a;color+=sample*weight;total+=weight;}gl_FragColor=color/total;gl_FragColor.rgb/=gl_FragColor.a+0.00001;}"); 30 | f.call(this,a.zoomBlur,{center:[b,d],strength:c,texSize:[this.width,this.height]});return this}function U(b,d,c,e){a.colorHalftone=a.colorHalftone||new h(null,"uniform sampler2D texture;uniform vec2 center;uniform float angle;uniform float scale;uniform vec2 texSize;varying vec2 texCoord;float pattern(float angle){float s=sin(angle),c=cos(angle);vec2 tex=texCoord*texSize-center;vec2 point=vec2(c*tex.x-s*tex.y,s*tex.x+c*tex.y)*scale;return(sin(point.x)*sin(point.y))*4.0;}void main(){vec4 color=texture2D(texture,texCoord);vec3 cmy=1.0-color.rgb;float k=min(cmy.x,min(cmy.y,cmy.z));cmy=(cmy-k)/(1.0-k);cmy=clamp(cmy*10.0-3.0+vec3(pattern(angle+0.26179),pattern(angle+1.30899),pattern(angle)),0.0,1.0);k=clamp(k*10.0-5.0+pattern(angle+0.78539),0.0,1.0);gl_FragColor=vec4(1.0-cmy-k,color.a);}"); 31 | f.call(this,a.colorHalftone,{center:[b,d],angle:c,scale:Math.PI/e,texSize:[this.width,this.height]});return this}function V(b,d,c,e){a.dotScreen=a.dotScreen||new h(null,"uniform sampler2D texture;uniform vec2 center;uniform float angle;uniform float scale;uniform vec2 texSize;varying vec2 texCoord;float pattern(){float s=sin(angle),c=cos(angle);vec2 tex=texCoord*texSize-center;vec2 point=vec2(c*tex.x-s*tex.y,s*tex.x+c*tex.y)*scale;return(sin(point.x)*sin(point.y))*4.0;}void main(){vec4 color=texture2D(texture,texCoord);float average=(color.r+color.g+color.b)/3.0;gl_FragColor=vec4(vec3(average*10.0-5.0+pattern()),color.a);}"); 32 | f.call(this,a.dotScreen,{center:[b,d],angle:c,scale:Math.PI/e,texSize:[this.width,this.height]});return this}function W(b){a.edgeWork1=a.edgeWork1||new h(null,"uniform sampler2D texture;uniform vec2 delta;varying vec2 texCoord;"+s+"void main(){vec2 color=vec2(0.0);vec2 total=vec2(0.0);float offset=random(vec3(12.9898,78.233,151.7182),0.0);for(float t=-30.0;t<=30.0;t++){float percent=(t+offset-0.5)/30.0;float weight=1.0-abs(percent);vec3 sample=texture2D(texture,texCoord+delta*percent).rgb;float average=(sample.r+sample.g+sample.b)/3.0;color.x+=average*weight;total.x+=weight;if(abs(t)<15.0){weight=weight*2.0-1.0;color.y+=average*weight;total.y+=weight;}}gl_FragColor=vec4(color/total,0.0,1.0);}"); 33 | a.edgeWork2=a.edgeWork2||new h(null,"uniform sampler2D texture;uniform vec2 delta;varying vec2 texCoord;"+s+"void main(){vec2 color=vec2(0.0);vec2 total=vec2(0.0);float offset=random(vec3(12.9898,78.233,151.7182),0.0);for(float t=-30.0;t<=30.0;t++){float percent=(t+offset-0.5)/30.0;float weight=1.0-abs(percent);vec2 sample=texture2D(texture,texCoord+delta*percent).xy;color.x+=sample.x*weight;total.x+=weight;if(abs(t)<15.0){weight=weight*2.0-1.0;color.y+=sample.y*weight;total.y+=weight;}}float c=clamp(10000.0*(color.y/total.y-color.x/total.x)+0.5,0.0,1.0);gl_FragColor=vec4(c,c,c,1.0);}"); 34 | f.call(this,a.edgeWork1,{delta:[b/this.width,0]});f.call(this,a.edgeWork2,{delta:[0,b/this.height]});return this}function X(b,d,c){a.hexagonalPixelate=a.hexagonalPixelate||new h(null,"uniform sampler2D texture;uniform vec2 center;uniform float scale;uniform vec2 texSize;varying vec2 texCoord;void main(){vec2 tex=(texCoord*texSize-center)/scale;tex.y/=0.866025404;tex.x-=tex.y*0.5;vec2 a;if(tex.x+tex.y-floor(tex.x)-floor(tex.y)<1.0)a=vec2(floor(tex.x),floor(tex.y));else a=vec2(ceil(tex.x),ceil(tex.y));vec2 b=vec2(ceil(tex.x),floor(tex.y));vec2 c=vec2(floor(tex.x),ceil(tex.y));vec3 TEX=vec3(tex.x,tex.y,1.0-tex.x-tex.y);vec3 A=vec3(a.x,a.y,1.0-a.x-a.y);vec3 B=vec3(b.x,b.y,1.0-b.x-b.y);vec3 C=vec3(c.x,c.y,1.0-c.x-c.y);float alen=length(TEX-A);float blen=length(TEX-B);float clen=length(TEX-C);vec2 choice;if(alen0.0){coord*=mix(1.0,smoothstep(0.0,radius/distance,percent),strength*0.75);}else{coord*=mix(1.0,pow(percent,1.0+strength*0.75)*radius/distance,1.0-percent);}}coord+=center;"); 37 | f.call(this,a.bulgePinch,{radius:c,strength:q(-1,e,1),center:[b,d],texSize:[this.width,this.height]});return this}function $(b,d,c){a.matrixWarp=a.matrixWarp||u("uniform mat3 matrix;uniform bool useTextureSpace;","if(useTextureSpace)coord=coord/texSize*2.0-1.0;vec3 warp=matrix*vec3(coord,1.0);coord=warp.xy/warp.z;if(useTextureSpace)coord=(coord*0.5+0.5)*texSize;");b=Array.prototype.concat.apply([],b);if(4==b.length)b= 38 | [b[0],b[1],0,b[2],b[3],0,0,0,1];else if(9!=b.length)throw"can only warp with 2x2 or 3x3 matrix";f.call(this,a.matrixWarp,{matrix:d?y(b):b,texSize:[this.width,this.height],useTextureSpace:c|0});return this}function aa(a,d){var c=x.apply(null,d),e=x.apply(null,a),c=y(c);return this.matrixWarp([c[0]*e[0]+c[1]*e[3]+c[2]*e[6],c[0]*e[1]+c[1]*e[4]+c[2]*e[7],c[0]*e[2]+c[1]*e[5]+c[2]*e[8],c[3]*e[0]+c[4]*e[3]+c[5]*e[6],c[3]*e[1]+c[4]*e[4]+c[5]*e[7],c[3]*e[2]+c[4]*e[5]+c[5]*e[8],c[6]*e[0]+c[7]*e[3]+c[8]*e[6], 39 | c[6]*e[1]+c[7]*e[4]+c[8]*e[7],c[6]*e[2]+c[7]*e[5]+c[8]*e[8]])}function ba(b,d,c,e){a.swirl=a.swirl||u("uniform float radius;uniform float angle;uniform vec2 center;","coord-=center;float distance=length(coord);if(distance>1;this.xa[e]>a?c=e:d=e}var e=this.xa[c]- 53 | this.xa[d],g=(this.xa[c]-a)/e;a=(a-this.xa[d])/e;return g*this.ya[d]+a*this.ya[c]+((g*g*g-g)*this.y2[d]+(a*a*a-a)*this.y2[c])*e*e/6};var r=function(){function b(b,c,d,f){this.gl=a;this.id=a.createTexture();this.width=b;this.height=c;this.format=d;this.type=f;a.bindTexture(a.TEXTURE_2D,this.id);a.texParameteri(a.TEXTURE_2D,a.TEXTURE_MAG_FILTER,a.LINEAR);a.texParameteri(a.TEXTURE_2D,a.TEXTURE_MIN_FILTER,a.LINEAR);a.texParameteri(a.TEXTURE_2D,a.TEXTURE_WRAP_S,a.CLAMP_TO_EDGE);a.texParameteri(a.TEXTURE_2D, 54 | a.TEXTURE_WRAP_T,a.CLAMP_TO_EDGE);b&&c&&a.texImage2D(a.TEXTURE_2D,0,this.format,b,c,0,this.format,this.type,null)}function d(a){null==c&&(c=document.createElement("canvas"));c.width=a.width;c.height=a.height;a=c.getContext("2d");a.clearRect(0,0,c.width,c.height);return a}b.fromElement=function(c){var d=new b(0,0,a.RGBA,a.UNSIGNED_BYTE);d.loadContentsOf(c);return d};b.prototype.loadContentsOf=function(b){this.width=b.width||b.videoWidth;this.height=b.height||b.videoHeight;a.bindTexture(a.TEXTURE_2D, 55 | this.id);a.texImage2D(a.TEXTURE_2D,0,this.format,this.format,this.type,b)};b.prototype.initFromBytes=function(b,c,d){this.width=b;this.height=c;this.format=a.RGBA;this.type=a.UNSIGNED_BYTE;a.bindTexture(a.TEXTURE_2D,this.id);a.texImage2D(a.TEXTURE_2D,0,a.RGBA,b,c,0,a.RGBA,this.type,new Uint8Array(d))};b.prototype.destroy=function(){a.deleteTexture(this.id);this.id=null};b.prototype.use=function(b){a.activeTexture(a.TEXTURE0+(b||0));a.bindTexture(a.TEXTURE_2D,this.id)};b.prototype.unuse=function(b){a.activeTexture(a.TEXTURE0+ 56 | (b||0));a.bindTexture(a.TEXTURE_2D,null)};b.prototype.ensureFormat=function(b,c,d,f){if(1==arguments.length){var h=arguments[0];b=h.width;c=h.height;d=h.format;f=h.type}if(b!=this.width||c!=this.height||d!=this.format||f!=this.type)this.width=b,this.height=c,this.format=d,this.type=f,a.bindTexture(a.TEXTURE_2D,this.id),a.texImage2D(a.TEXTURE_2D,0,this.format,b,c,0,this.format,this.type,null)};b.prototype.drawTo=function(b){a.framebuffer=a.framebuffer||a.createFramebuffer();a.bindFramebuffer(a.FRAMEBUFFER, 57 | a.framebuffer);a.framebufferTexture2D(a.FRAMEBUFFER,a.COLOR_ATTACHMENT0,a.TEXTURE_2D,this.id,0);if(a.checkFramebufferStatus(a.FRAMEBUFFER)!==a.FRAMEBUFFER_COMPLETE)throw Error("incomplete framebuffer");a.viewport(0,0,this.width,this.height);b();a.bindFramebuffer(a.FRAMEBUFFER,null)};var c=null;b.prototype.fillUsingCanvas=function(b){b(d(this));this.format=a.RGBA;this.type=a.UNSIGNED_BYTE;a.bindTexture(a.TEXTURE_2D,this.id);a.texImage2D(a.TEXTURE_2D,0,a.RGBA,a.RGBA,a.UNSIGNED_BYTE,c);return this}; 58 | b.prototype.toImage=function(b){this.use();h.getDefaultShader().drawRect();var f=4*this.width*this.height,k=new Uint8Array(f),n=d(this),p=n.createImageData(this.width,this.height);a.readPixels(0,0,this.width,this.height,a.RGBA,a.UNSIGNED_BYTE,k);for(var m=0;m-1){return a==="IEND"?!!PngToy._findChunk(this.chunks,"IEND"):PngToy["_"+a](this)}else{return PngToy._findChunk(this.chunks,a)}},getGammaLUT:function(c,b,f){c=c||1;b=b||2.2;f=f||1;var a=new Uint8Array(256),e=0,d=1/(c*b*f);for(;e<256;e++){a[e]=Math.round(Math.pow(e/255,d)*255)}return a},guessDisplayGamma:function(){return navigator.userAgent.indexOf("Mac OS")>-1?1.8:2.2}};PngToy._blockSize=3000000;PngToy._delay=7;PngToy._getChunks=function(b,F,k,a){var x=this,C=8,v=b.byteLength,e=[],d,w,m,A,h,g,B,E,p,z,D,t=!0,y=["iTXT","tIME","tEXt","zTXt"],l=PngToy._findChunk;if(k&&!this.table){this.table=new Uint32Array(256);for(var q=0,u;q<256;q++){h=q>>>0;for(u=0;u<8;u++){h=(h&1)?3988292384^(h>>>1):h>>>1}this.table[q]=h}}while(C2147483647&&!a){return{error:"Invalid chunk size."}}A=C;C=A+w;h=o();d=new PngToy.Chunk(m,A,w,h);if(k){c(d);if(!d.crcOk&&!a){return{error:"Invalid CRC in chunk "+m}}}if(d.isReserved&&!a){return{error:"Invalid chunk name: "+m}}e.push(d)}if(!a){if(!f("IHDR",1,1)){return{error:"Invalid number of IHDR chunks."}}if(!f("tIME",0,1)){return{error:"Invalid number of tIME chunks."}}if(!f("zTXt",0,-1)){return{error:"Invalid number of zTXt chunks."}}if(!f("tEXt",0,-1)){return{error:"Invalid number of tEXt chunks."}}if(!f("iTXt",0,-1)){return{error:"Invalid number of iTXt chunks."}}if(!f("pHYs",0,1)){return{error:"Invalid number of pHYs chunks."}}if(!f("sPLT",0,-1)){return{error:"Invalid number of sPLT chunks."}}if(!f("iCCP",0,1)){return{error:"Invalid number of iCCP chunks."}}if(!f("sRGB",0,1)){return{error:"Invalid number of sRGB chunks."}}if(!f("sBIT",0,1)){return{error:"Invalid number of sBIT chunks."}}if(!f("gAMA",0,1)){return{error:"Invalid number of gAMA chunks."}}if(!f("cHRM",0,1)){return{error:"Invalid number of cHRM chunks."}}if(!f("PLTE",0,1)){return{error:"Invalid number of PLTE chunks."}}if(!f("tRNS",0,1)){return{error:"Invalid number of tRNS chunks."}}if(!f("hIST",0,1)){return{error:"Invalid number of hIST chunks."}}if(!f("bKGD",0,1)){return{error:"Invalid number of bKGD chunks."}}if(!f("IDAT",1,-1)){return{error:"Invalid number of IDAT chunks."}}if(!f("IEND",1,1)){return{error:"Invalid number of IEND chunks."}}if(e[0].name!=="IHDR"||e[e.length-1].name!=="IEND"){return{error:"Invalid PNG chunk order."}}g=F.getUint8(l(e,"IHDR").offset+9);B=l(e,"PLTE");p=l(e,"hIST");E=l(e,"tRNS");z=l(e,"oFFs");D=l(e,"sTER");if(l(e,"iCCP")&&l(e,"sRGB")){return{error:"Both iCCP and sRGB cannot be present."}}if(g===3&&!B){return{error:"Missing PLTE chunk."}}if((g===0||g===4)&&B){return{error:"PLTE chunk should not appear with this color type."}}if((g===4||g===6)&&E){return{error:"tRNS chunk should not appear with this color type."}}if(p&&!B){return{error:"hIST chunk can only appear if a PLTE chunk is present."}}if(B){if(!r("PLTE","IDAT")){return{error:"Invalid chunk order for PLTE."}}if(p&&!s("PLTE","hIST","IDAT")){return{error:"Invalid chunk order for hIST."}}if(E&&!s("PLTE","tRNS","IDAT")){return{error:"Invalid chunk order for tRNS."}}if(l(e,"bKGD")&&!s("PLTE","bKGD","IDAT")){return{error:"Invalid chunk order for bKGD."}}if(!r("cHRM","PLTE")){return{error:"Invalid chunk order for cHRM."}}if(!r("gAMA","PLTE")){return{error:"Invalid chunk order for gAMA."}}if(!r("iCCP","PLTE")){return{error:"Invalid chunk order for iCCP."}}if(!r("sRGB","PLTE")){return{error:"Invalid chunk order for sRGB."}}}if(z&&!r("oFFs","IDAT")){return{error:"Invalid chunk order for oFFs."}}if(D&&!r("sTER","IDAT")){return{error:"Invalid chunk order for sTER."}}for(q=e.length-2;q>0;q--){if(t&&e[q].name!=="IDAT"&&y.indexOf(e[q].name)<0){t=!1}else{if(!t&&e[q].name==="IDAT"){return{error:"Invalid chunk inside IDAT chunk sequence."}}}}}return{chunks:e};function f(i,H,G){var j=PngToy._findChunks(e,i);return G<0?j.length>=H:j.length>=H&&j.length<=G}function s(j,G,i){return r(j,G)&&r(G,i)}function r(j,H){var G=-1,I=-1,J,K=e.length;for(J=0;J>>0),K=H.length,J;for(J=0;J>>8)^x.table[(I^H[J])&255]}return(I^-1)>>>0}}function n(){var j=o(),i=String.fromCharCode;return i((j&4278190080)>>>24)+i((j&16711680)>>>16)+i((j&65280)>>>8)+i((j&255)>>>0)}function o(){var j=F.getUint32(C);C+=4;return j>>>0}};PngToy._getChunks.table=null;PngToy._findChunk=function(b,d){for(var c=0,a;a=b[c++];){if(a.name===d){return a}}return null};PngToy._findChunks=function(b,e){for(var c=0,d=[],a;a=b[c++];){if(a.name===e){d.push(a)}}return d};PngToy._getStr=function(j,e,d){var g="",c=e,a=-1,h,k=!1,b=String.fromCharCode,f=" abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!\"%&'()*+,-./:;<=>?_";d+=c;for(;c-1){g+=h}else{k=!0}continue}break}return{offset:c,text:g,warning:k}};PngToy.Chunk=function(c,d,b,a){this.name=c;this.offset=d;this.length=b;this.crc=a;this.crcOk=!0;this.isCritical=!(c.charCodeAt(0)&32);this.isPrivate=!!(c.charCodeAt(1)&32);this.isReserved=!!(c.charCodeAt(2)&32);this.isCopySafe=!!(c.charCodeAt(3)&32)};PngToy._IHDR=function(d){var g=d.view,c=d.chunks,a=d.allowInvalid,b=PngToy._findChunk(c,"IHDR"),e,f;if(!b){return{error:"Critical - IHDR chunk is missing."}}e=b.offset;f={width:g.getUint32(e),height:g.getUint32(e+4),depth:g.getUint8(e+8),type:g.getUint8(e+9),compression:g.getUint8(e+10),filter:g.getUint8(e+11),interlaced:g.getUint8(e+12)};if(!a){if([0,2,3,4,6].indexOf(f.type)<0){return{error:"Invalid color type."}}switch(f.type){case 0:if([1,2,4,8,16].indexOf(f.depth)<0){return{error:"Invalid color depth."}}break;case 3:if([1,2,4,8].indexOf(f.depth)<0){return{error:"Invalid color depth."}}break;default:if([8,16].indexOf(f.depth)<0){return{error:"Invalid color depth."}}}if(!f.width||!f.height){return{error:"Invalid dimension."}}if(f.compression){return{error:"Invalid compression type."}}if(f.filter){return{error:"Invalid filter type."}}if(f.interlaced<0||f.interlaced>1){return{error:"Invalid interlace mode "+f.interlaced}}}return f};PngToy._IDAT=function(f){var b=f.buffer,d=f.chunks,a=f.allowInvalid,g=0,c,j=!1,h=new pako.Inflate(),e=!1;while(c=d[g++]){if(c.name==="IDAT"){e=!0;break}}while(c){j=c.name==="IEND";if(c.name==="IDAT"){h.push(new Uint8Array(b,c.offset,c.length),j)}c=d[g++]}if(!j&&!a){return{error:"Critical - missing IEND chunk."}}return e?(h.err?{error:h.msg}:{buffer:h.result}):(a?{buffer:null}:{error:"Critical - no IDAT chunk(s)."})};PngToy._PLTE=function(e){var b=e.buffer,d=e.chunks,a=e.allowInvalid,c=PngToy._findChunk(d,"PLTE"),f;if(!c){return null}f=new Uint8Array(b,c.offset,c.length);if(!a){if(f.length%3){return{error:"Invalid palette size."}}if(f.length<3||f.length>3*256){return{error:"Invalid number of palette entries."}}}return{palette:f,length:f.length/3}};PngToy._sPLT=function(d){var f=d.view,c=d.chunks,a=d.allowInvalid,b=PngToy._findChunks(c,"sPLT"),e=[];if(!b.length){return null}b.forEach(function(h){var p={depth:null,name:null,palette:[],entries:0},m,o,l,q,g,n=[],k,j;o=h.offset;m=PngToy._getStr(f,o,80);p.name=m.text;o=m.offset;p.depth=f.getUint8(o++);q=p.depth===8?6:10;g=p.depth===8?1:2;l=h.length-(o-h.offset);j=q===6?f.getUint8.bind(f):f.getUint16.bind(f);for(k=0;k>1};break;case 2:h={alphas:new Uint16Array(b.slice(c.offset,c.offset+c.length)),length:c.length/6};break;case 3:h={alphas:new Uint8Array(b,c.offset,c.length),length:c.length};break;default:return a?{alphas:null,length:0}:{error:"tRNS chunk is not valid for this color type."}}if(!a&&g&&h.length>g.length){return{error:"tRNS chunk contains more entries than palette entries."}}return h};PngToy._hIST=function(e){var i=e.view,c=e.chunks,a=e.allowInvalid,b=PngToy._findChunk(c,"hIST"),g=PngToy._PLTE(e),d=[],h,f;if(!b){return null}if(!a&&b.length%2){return{error:"Invalid length of hIST chunk."}}h=b.offset;f=h+b.length;for(;h31&&b<127)&&!(b>160&&b<256)){return{error:"ICC profile contains illegal chars in name."}}}}k.name=g;if(l.getUint8(j++)&&!a){return{error:"Invalid compression type."}}try{k.icc=pako.inflate(new Uint8Array(l.buffer,j,c.length-(j-c.offset)))}catch(e){if(!a){return{error:e}}}return k};PngToy._gAMA=function(e){var f=e.view,c=e.chunks,a=e.allowInvalid,b=PngToy._findChunk(c,"gAMA"),d;if(!b){return null}d=f.getUint32(b.offset)/100000;if(!a){}return{gamma:d}};PngToy._cHRM=function(d){var g=d.view,c=d.chunks,a=d.allowInvalid,b=PngToy._findChunk(c,"cHRM"),f,e;if(!b){return null}e=b.offset;f={whiteX:g.getUint32(e)/100000,whiteY:g.getUint32(e+4)/100000,redX:g.getUint32(e+8)/100000,redY:g.getUint32(e+12)/100000,greenX:g.getUint32(e+16)/100000,greenY:g.getUint32(e+20)/100000,blueX:g.getUint32(e+24)/100000,blueY:g.getUint32(e+28)/100000};if(!a){}return f};PngToy._sRGB=function(d){var g=d.view,c=d.chunks,a=d.allowInvalid,b=PngToy._findChunk(c,"sRGB"),e,f=["Perceptual","Relative colorimetric","Saturation","Absolute colorimetric"];if(!b){return null}e=g.getUint8(b.offset);if(!a){if(e<0||e>3){return{error:"Invalid range for sRGB render intent."}}}return{intent:e,desc:f[e]||null}};PngToy._pHYs=function(d){var g=d.view,c=d.chunks,a=d.allowInvalid,b=PngToy._findChunk(c,"pHYs"),e,f={};if(!b){return null}e=b.offset;f.ppuX=g.getUint32(e);f.ppuY=g.getUint32(e+4);f.unit=g.getUint8(e+8);if(f.unit===1){f.desc="Meters"}else{f.desc="ratio"}if(!a){if(f.ppuX>2147483647||f.ppuY>2147483647){return{error:"Invalid unit lengths."}}if(f.unit<0||f.unit>1){return{error:"Invalid unit for pHYs chunk."}}}else{f.ppuX&=2147483647;f.ppuY&=2147483647;f.unit&=1}return f};PngToy._bKGD=function(c){var e=c.view,b=c.chunks,a=PngToy._findChunk(b,"bKGD"),d=PngToy._IHDR(c);if(!a){return null}switch(d.type){case 0:case 4:return{background:[e.getUint16(a.offset)]};case 2:case 6:return{background:new Uint16Array(e.buffer,a.offset,6)};default:return{index:e.getUint8(a.offset)}}};PngToy._tIME=function(e){var h=e.view,c=e.chunks,a=e.allowInvalid,b=PngToy._findChunk(c,"tIME"),f,g;if(!b){return null}f=b.offset;g={year:h.getUint16(f),month:h.getUint8(f+2),day:h.getUint8(f+3),hour:h.getUint8(f+4),minute:h.getUint8(f+5),second:h.getUint8(f+6),date:null};if(!a){if(g.year<0||g.year>65535||g.month<1||g.month>12||g.day<1||g.day>31||g.hour<0||g.hour>23||g.minute<0||g.minute>59||g.second<0||g.second>60){return{error:"Invalid timestamp."}}}try{g.date=new Date(g.year,g.month-1,g.day,g.hour,g.minute,Math.min(59,g.second))}catch(d){if(!a){return{error:d}}}return g};PngToy._oFFs=function(d){var g=d.view,c=d.chunks,a=d.allowInvalid,b=PngToy._findChunk(c,"oFFs"),e,f={};if(!b){return null}e=b.offset;f.x=g.getInt32(e);f.y=g.getInt32(e+4);f.unit=g.getUint8(e+8);f.desc=["Pixels","Micrometers"][f.unit]||"Invalid";if(!a){if(f.unit<0||f.unit>1){return{error:"Invalid unit for oFFs chunk."}}}return f};PngToy._sCAL=function(d){var h=d.view,c=d.chunks,a=d.allowInvalid,b=PngToy._findChunk(c,"sCAL"),f,e,g={};if(!b.length){return null}f=b.offset;g.unit=h.getUint8(f++);g.desc=["meters","radians"][g.unit]||null;e=PngToy._getStr(h,f,100000);g.unitsX=e.text;f=e.offset;e=PngToy._getStr(h,f,b.length-(f-b.offset));g.unitsY=e.text;if(!a){if(g.unit<1||g.unit>2){return{error:"Invalid unit"}}}return g};PngToy._pCAL=function(d){var l=d.view,c=d.chunks,a=d.allowInvalid,b=PngToy._findChunk(c,"pCAL"),m=!1,j,g,k={},h=[],e=0,f;if(!b.length){return null}j=b.offset;g=PngToy._getStr(l,j,80);k.calName=g.text;j=g.offset;if(g.warn){m=!0}k.x0=l.getInt32(j);k.x1=l.getInt32(j+4);k.eqType=l.getUint8(j+8);k.eqDesc=["Linear mapping","Base-e exponential mapping","Arbitrary-base exponential mapping","Hyperbolic mapping"][k.eqType]||null;k.paramCount=l.getUint8(j+9);j+=10;g=PngToy._getStr(l,j,10000);k.unitName=g.text;j=g.offset;if(g.warn){m=!0}f=k.paramCount-1;for(;e3){return{error:"Invalid equation type."}}if(m){return{error:"One or more text field contains illegal chars."}}}return k};PngToy.prototype.decode=function(){var a=this;return new Promise(function(Q,P){var C=a.getChunk("IHDR"),Z=C.width,B=C.height,Y=C.type,m=C.depth,g=m/8,D=g===2,M=[1,0,3,1,2,0,4][Y],G=D?65535:255,l=M*g,F=Z*l,E=F+Math.ceil(l),S=a.getChunk("IDAT").buffer,o=new Uint8Array(Math.max(1,Math.ceil(Z*l)*B)),z=[r,s,t,u,v],R=0,n=0,i=0,N=0,ab=0,O,f=S.byteLength,T=S.length,H=[0,4,0,2,0,1,0],I=[0,0,4,0,2,0,1],U=[8,8,4,4,2,2,1],V=[8,8,8,4,4,2,2],d=[8,4,4,2,2,1,1],c=[8,8,4,4,2,2,1],L,W,X,J,e,b,aa;a.debug={pixelWidth:M,byteWidth:g,delta:l,lineLen:F,lineDlt:E,filters:[],preFilt:-1,postFilt:-1,srcPos:-1,srcLen:S.length,pass:0,x:-1,stepX:0,stepY:0,stepsX:0,stepsY:0};if(C.interlaced){if(typeof console!=="undefined"){console.log("WARN: In current alpha interlaced PNGs will not decode properly unless all line-filters are filter 0.")}L=ab=J=0;W=X=e=b=8;setTimeout(k,PngToy._delay)}else{setTimeout(j,PngToy._delay)}function j(){try{var x,ad,y,ac=a.debug.filters,h=PngToy._blockSize;l=Math.ceil(l);while(ab0){y=S[R++];x=z[y];if(ac.indexOf(y)<0){ac.push(y)}ad=Math.min(f,R+F);i=n;while(R=T){ab=B;continue}ad=S[R++];if(ae.indexOf(ad)<0){ae.push(ad)}if(ad<0||ad>4){a.debug.preFilt=S[R-2];a.debug.postFilt=S[R];a.debug.srcPos=R;a.debug.pass=L;a.debug.x=aa;a.debug.stepX=W;a.debug.stepY=X;a.debug.stepsX=Z/W;a.debug.stepsY=B/X}ac=z[ad];lineEnd=Math.min(f,Math.ceil(R+F));i=ab*F;N=Math.max(0,i-F);aa=J;while(aa>>0}function q(h){return h>>0}function r(h){return h}function s(w,h){return w+p(h)}function t(x,h,w){return x+q(w)}function u(x,h,w){return x+((p(h)+q(w))>>>1)}function v(y,h,w,x){return y+K(p(h),q(w),q(x))}function K(h,w,x){var y=h+w-x,ac=Math.abs(y-h),ad=Math.abs(y-w),ae=Math.abs(y-x);if(ac<=ad&&ac<=ae){return h}if(ad<=ae){return w}return x}})};PngToy.prototype.convertToRGBA=function(a,c){var b=this;c=c||{};return new Promise(function(O,N){if(a.type===6&&a.depth===8&&!c.useGamma){var R=c.ignoreAspectRatio?null:b.getChunk("pHYs"),L=R?R.ppuY/(R.ppuX||1):1,M=R?R.ppuX/(R.ppuY||1):1;if(c.ignoreAspectRatio||(!c.ignoreAspectRatio&&L===1&&M===1)){O({bitmap:a.bitmap,width:a.width,height:a.height,ratioX:L,ratioY:M})}return}var J,S,H,k,G,d,U=a.width,D=a.height,T=a.type,g=a.depth,e=g/8,I=[1,0,3,1,2,0,4][T],l=p(T,g),Q=a.bitmap,j=new Uint8Array(U*D*4),P=0,i=0,E=Q.byteLength,K,F;if(a.type===3){J=b.getChunk("PLTE");G=J?J.palette:[]}S=b.getChunk("tRNS");d=S&&S.alphas?S.alphas:[];H=b.getChunk("pHYs");k=b.getChunk("gAMA");k=k?k.gamma:1;(function f(){var h=PngToy._blockSize,V=U*e*I,w=P+V;if(c.useGamma){F=F?F:b.getGammaLUT(k,c.gamma||1);while(P0){if(P>=w){P=Math.ceil(P);w=P+V}K=l();j[i++]=F[K[0]];j[i++]=F[K[1]];j[i++]=F[K[2]];j[i++]=K[3];h--}}else{while(P0){if(P>=w){P=Math.ceil(P);w=P+V}K=l();j[i++]=K[0];j[i++]=K[1];j[i++]=K[2];j[i++]=K[3];h--}}if(P>V))?255:0,h=d.length?((d[0]>>>8)&(128>>V)===W?0:255):255;P+=e;return[W,W,W,h]}function t(){var w=Q[P|0],V=((P-(P|0))/e)<<1,W=(((w>>>V)&3)*85)&255,h=d.length?((((d[0]>>>8)&3)*85)&255===W?0:255):255;P+=e;return[W,W,W,h]}function u(){var w=Q[P|0],V=((P-(P|0))/e),W=V?(w&15)<<4:w&240,h=d.length?(((d[0]&3840)>>>4)===W?0:255):255;P+=e;return[W,W,W,h]}function m(){var h=Q[P|0],w=(P-(P|0))/e,V=(h&(128>>>w))?1:0,W=V*3;P+=e;return[G[W],G[W+1],G[W+2],255]}function n(){var w=Q[P|0],V=((P-(P|0))/e)<<1,W=((w<>>6,X=W*3,h=W>>4,X=W*3,h=W>>8)?0:255;w&=255;return[w,w,w,h]}function s(){var w=Q[P++],h=d.length&&d[0]===w?0:255;w&=255;return[w,w,w,h]}function z(){var Z=Q[P++],Y=Q[P++],X=Q[P++],W,V,w,h=255;if(d.length){W=d[0]>>>8;V=d[1]>>>8;w=d[2]>>>8;if(W===Z&&V===Y&&w===X){h=0}}return[Z&255,Y&255,X&255,h]}function B(){return[Q[P++],Q[P++],Q[P++],Q[P++]]}function A(){var W=Q[P++],V=Q[P++],w=Q[P++],h=d.length&&d[0]===W&&d[1]===V&&d[2]===w?0:255;return[W&255,V&255,w&255,h]}function v(){var h=Q[P++];return[h,h,h,Q[P++]]}function x(){var h=Q[P++]&255;return[h,h,h,Q[P++]&255]}function y(){var w=Q[P++],h=w*3;return[G[h],G[h+1],G[h+2],w=1){o=g.width;k=(g.height*f.ratioY)|0}else{if(f.ratioY<1){o=(g.width*f.ratioX)|0;k=g.height}}m.width=o;m.height=k;n.drawImage(g,0,0,o,k);g=m}e(g)}catch(j){d(j)}},d)})};(function(a){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=a()}else{if(typeof define==="function"&&define.amd){define([],a)}else{var b;if(typeof window!=="undefined"){b=window}else{if(typeof global!=="undefined"){b=global}else{if(typeof self!=="undefined"){b=self}else{b=this}}}b.pako=a()}}})(function(){var a,d,c;return(function b(k,f,h){function j(p,q){if(!f[p]){if(!k[p]){var i=typeof require=="function"&&require;if(!q&&i){return i(p,!0)}if(e){return e(p,!0)}var m=new Error("Cannot find module '"+p+"'");throw m.code="MODULE_NOT_FOUND",m}var n=f[p]={exports:{}};k[p][0].call(n.exports,function(l){var o=k[p][1][l];return j(o?o:l)},n,n.exports,b,k,f,h)}return f[p].exports}var e=typeof require=="function"&&require;for(var g=0;g=252?6:j>=248?5:j>=240?4:j>=224?3:j>=192?2:1)}f[254]=f[254]=1;h.string2buf=function(u){var o,q,r,t,s,v=u.length,p=0;for(t=0;t>>6);o[s++]=128|(q&63)}else{if(q<65536){o[s++]=224|(q>>>12);o[s++]=128|(q>>>6&63);o[s++]=128|(q&63)}else{o[s++]=240|(q>>>18);o[s++]=128|(q>>>12&63);o[s++]=128|(q>>>6&63);o[s++]=128|(q&63)}}}}return o};function g(o,q){if(q<65537){if((o.subarray&&m)||(!o.subarray&&l)){return String.fromCharCode.apply(null,n.shrinkBuf(o,q))}}var r="";for(var p=0;p4){v[u++]=65533;r+=q-1;continue}p&=q===2?31:q===3?15:7;while(q>1&&r1){v[u++]=65533;continue}if(p<65536){v[u++]=p}else{p-=65536;v[u++]=55296|((p>>10)&1023);v[u++]=56320|(p&1023)}}return g(v,u)};h.utf8border=function(o,p){var q;p=p||o.length;if(p>o.length){p=o.length}q=p-1;while(q>=0&&(o[q]&192)===128){q--}if(q<0){return p}if(q===0){return p}return(q+f[o[q]]>p)?q:p}},{"./common":1}],3:[function(h,g,f){function e(i,j,k,m){var o=(i&65535)|0,p=((i>>>16)&65535)|0,l=0;while(k!==0){l=k>2000?2000:k;k-=l;do{o=(o+j[m++])|0;p=(p+o)|0}while(--l);o%=65521;p%=65521}return(o|(p<<16))|0}g.exports=e},{}],4:[function(g,f,e){f.exports={Z_NO_FLUSH:0,Z_PARTIAL_FLUSH:1,Z_SYNC_FLUSH:2,Z_FULL_FLUSH:3,Z_FINISH:4,Z_BLOCK:5,Z_TREES:6,Z_OK:0,Z_STREAM_END:1,Z_NEED_DICT:2,Z_ERRNO:-1,Z_STREAM_ERROR:-2,Z_DATA_ERROR:-3,Z_BUF_ERROR:-5,Z_NO_COMPRESSION:0,Z_BEST_SPEED:1,Z_BEST_COMPRESSION:9,Z_DEFAULT_COMPRESSION:-1,Z_FILTERED:1,Z_HUFFMAN_ONLY:2,Z_RLE:3,Z_FIXED:4,Z_DEFAULT_STRATEGY:0,Z_BINARY:0,Z_TEXT:1,Z_UNKNOWN:2,Z_DEFLATED:8}},{}],5:[function(i,h,g){var f=PngToy._getChunks.table;function e(k,j,n,o){var p=f,l=o+n;k^=-1;for(var m=o;m>>8)^p[(k^j[m])&255]}return(k^(-1))}h.exports=e},{}],6:[function(h,g,e){function f(){this.text=0;this.time=0;this.xflags=0;this.os=0;this.extra=null;this.extra_len=0;this.name="";this.comment="";this.hcrc=0;this.done=!1}g.exports=f},{}],7:[function(i,h,f){var e=30;var j=12;h.exports=function g(H,F){var G;var k;var y;var l;var m;var s;var r;var K;var I;var J;var E;var w;var n;var z;var o;var B;var q;var v;var C;var A;var p;var t;var u;var x,D;G=H.state;k=H.next_in;x=H.input;y=k+(H.avail_in-5);l=H.next_out;D=H.output;m=l-(F-H.avail_out);s=l+(H.avail_out-257);r=G.dmax;K=G.wsize;I=G.whave;J=G.wnext;E=G.window;w=G.hold;n=G.bits;z=G.lencode;o=G.distcode;B=(1<>>24;w>>>=C;n-=C;C=(v>>>16)&255;if(C===0){D[l++]=v&65535}else{if(C&16){A=v&65535;C&=15;if(C){if(n>>=C;n-=C}if(n<15){w+=x[k++]<>>24;w>>>=C;n-=C;C=(v>>>16)&255;if(C&16){p=v&65535;C&=15;if(nr){H.msg="invalid distance too far back";G.mode=e;break top}w>>>=C;n-=C;C=l-m;if(p>C){C=p-C;if(C>I){if(G.sane){H.msg="invalid distance too far back";G.mode=e;break top}}t=0;u=E;if(J===0){t+=K-C;if(C2){D[l++]=u[t++];D[l++]=u[t++];D[l++]=u[t++];A-=3}if(A){D[l++]=u[t++];if(A>1){D[l++]=u[t++]}}}else{t=l-p;do{D[l++]=D[t++];D[l++]=D[t++];D[l++]=D[t++];A-=3}while(A>2);if(A){D[l++]=D[t++];if(A>1){D[l++]=D[t++]}}}}else{if((C&64)===0){v=o[(v&65535)+(w&((1<>3;k-=A;n-=A<<3;w&=(1<>>24)&255)+((aB>>>8)&65280)+((aB&65280)<<8)+((aB&255)<<24))}function P(){this.mode=0;this.last=!1;this.wrap=0;this.havedict=!1;this.flags=0;this.dmax=0;this.check=0;this.total=0;this.head=null;this.wbits=0;this.wsize=0;this.whave=0;this.wnext=0;this.window=null;this.hold=0;this.bits=0;this.length=0;this.offset=0;this.extra=0;this.lencode=null;this.distcode=null;this.lenbits=0;this.distbits=0;this.ncode=0;this.nlen=0;this.ndist=0;this.have=0;this.next=null;this.lens=new am.Buf16(320);this.work=new am.Buf16(288);this.lendyn=null;this.distdyn=null;this.sane=0;this.back=0;this.was=0}function N(aC){var aB;if(!aC||!aC.state){return ay}aB=aC.state;aC.total_in=aC.total_out=aB.total=0;aC.msg="";if(aB.wrap){aC.adler=aB.wrap&1}aB.mode=D;aB.last=0;aB.havedict=0;aB.dmax=32768;aB.head=null;aB.hold=0;aB.bits=0;aB.lencode=aB.lendyn=new am.Buf32(w);aB.distcode=aB.distdyn=new am.Buf32(v);aB.sane=1;aB.back=-1;return aw}function L(aC){var aB;if(!aC||!aC.state){return ay}aB=aC.state;aB.wsize=0;aB.whave=0;aB.wnext=0;return N(aC)}function M(aC,aD){var aE;var aB;if(!aC||!aC.state){return ay}aB=aC.state;if(aD<0){aE=0;aD=-aD}else{aE=(aD>>4)+1;if(aD<48){aD&=15}}if(aD&&(aD<8||aD>15)){return ay}if(aB.window!==null&&aB.wbits!==aD){aB.window=null}aB.wrap=aE;aB.wbits=aD;return L(aC)}function K(aD,aE){var aB;var aC;if(!aD){return ay}aC=new P();aD.state=aC;aC.window=null;aB=M(aD,aE);if(aB!==aw){aD.state=null}return aB}function J(aB){return K(aB,n)}var an=!0;var T,s;function A(aB){if(an){var aC;T=new am.Buf32(512);s=new am.Buf32(32);aC=0;while(aC<144){aB.lens[aC++]=8}while(aC<256){aB.lens[aC++]=9}while(aC<280){aB.lens[aC++]=7}while(aC<288){aB.lens[aC++]=8}G(W,aB.lens,0,288,T,0,aB.work,{bits:9});aC=0;while(aC<32){aB.lens[aC++]=5}G(t,aB.lens,0,32,s,0,aB.work,{bits:5});an=!1}aB.lencode=T;aB.lenbits=9;aB.distcode=s;aB.distbits=5}function al(aG,aE,aD,aB){var aC;var aF=aG.state;if(aF.window===null){aF.wsize=1<=aF.wsize){am.arraySet(aF.window,aE,aD-aF.wsize,aF.wsize,0);aF.wnext=0;aF.whave=aF.wsize}else{aC=aF.wsize-aF.wnext;if(aC>aB){aC=aB}am.arraySet(aF.window,aE,aD-aB,aC,aF.wnext);aB-=aC;if(aB){am.arraySet(aF.window,aE,aD-aB,aB,0);aF.wnext=aB;aF.whave=aF.wsize}else{aF.wnext+=aC;if(aF.wnext===aF.wsize){aF.wnext=0}if(aF.whave>>8)&255;a2.check=m(a2.check,aJ,2,0);aO=0;aD=0;a2.mode=B;break}a2.flags=0;if(a2.head){a2.head.done=!1}if(!(a2.wrap&1)||(((aO&255)<<8)+(aO>>8))%31){a3.msg="incorrect header check";a2.mode=f;break}if((aO&15)!==ar){a3.msg="unknown compression method";a2.mode=f;break}aO>>>=4;aD-=4;aU=(aO&15)+8;if(a2.wbits===0){a2.wbits=aU}else{if(aU>a2.wbits){a3.msg="invalid window size";a2.mode=f;break}}a2.dmax=1<>8)&1)}if(a2.flags&512){aJ[0]=aO&255;aJ[1]=(aO>>>8)&255;a2.check=m(a2.check,aJ,2,0)}aO=0;aD=0;a2.mode=ai;case ai:while(aD<32){if(aI===0){break inf_leave}aI--;aO+=aP[aW++]<>>8)&255;aJ[2]=(aO>>>16)&255;aJ[3]=(aO>>>24)&255;a2.check=m(a2.check,aJ,4,0)}aO=0;aD=0;a2.mode=ad;case ad:while(aD<16){if(aI===0){break inf_leave}aI--;aO+=aP[aW++]<>8)}if(a2.flags&512){aJ[0]=aO&255;aJ[1]=(aO>>>8)&255;a2.check=m(a2.check,aJ,2,0)}aO=0;aD=0;a2.mode=x;case x:if(a2.flags&1024){while(aD<16){if(aI===0){break inf_leave}aI--;aO+=aP[aW++]<>>8)&255;a2.check=m(a2.check,aJ,2,0)}aO=0;aD=0}else{if(a2.head){a2.head.extra=null}}a2.mode=z;case z:if(a2.flags&1024){aE=a2.length;if(aE>aI){aE=aI}if(aE){if(a2.head){aU=a2.head.extra_len-a2.length;if(!a2.head.extra){a2.head.extra=new Array(a2.head.extra_len)}am.arraySet(a2.head.extra,aP,aW,aE,aU)}if(a2.flags&512){a2.check=m(a2.check,aP,aE,aW)}aI-=aE;aW+=aE;a2.length-=aE}if(a2.length){break inf_leave}}a2.length=0;a2.mode=ac;case ac:if(a2.flags&2048){if(aI===0){break inf_leave}aE=0;do{aU=aP[aW+aE++];if(a2.head&&aU&&(a2.length<65536)){a2.head.name+=String.fromCharCode(aU)}}while(aU&&aE>9)&1);a2.head.done=!0}a3.adler=a2.check=0;a2.mode=aj;break;case p:while(aD<32){if(aI===0){break inf_leave}aI--;aO+=aP[aW++]<>>=aD&7;aD-=aD&7;a2.mode=g;break}while(aD<3){if(aI===0){break inf_leave}aI--;aO+=aP[aW++]<>>=1;aD-=1;switch((aO&3)){case 0:a2.mode=af;break;case 1:A(a2);a2.mode=R;if(aF===az){aO>>>=2;aD-=2;break inf_leave}break;case 2:a2.mode=ah;break;case 3:a3.msg="invalid block type";a2.mode=f}aO>>>=2;aD-=2;break;case af:aO>>>=aD&7;aD-=aD&7;while(aD<32){if(aI===0){break inf_leave}aI--;aO+=aP[aW++]<>>16)^65535)){a3.msg="invalid stored block lengths";a2.mode=f;break}a2.length=aO&65535;aO=0;aD=0;a2.mode=l;if(aF===az){break inf_leave}case l:a2.mode=k;case k:aE=a2.length;if(aE){if(aE>aI){aE=aI}if(aE>aT){aE=aT}if(aE===0){break inf_leave}am.arraySet(aZ,aP,aW,aE,a0);aI-=aE;aW+=aE;aT-=aE;a0+=aE;a2.length-=aE;break}a2.mode=aj;break;case ah:while(aD<14){if(aI===0){break inf_leave}aI--;aO+=aP[aW++]<>>=5;aD-=5;a2.ndist=(aO&31)+1;aO>>>=5;aD-=5;a2.ncode=(aO&15)+4;aO>>>=4;aD-=4;if(a2.nlen>286||a2.ndist>30){a3.msg="too many length or distance symbols";a2.mode=f;break}a2.have=0;a2.mode=V;case V:while(a2.have>>=3;aD-=3}while(a2.have<19){a2.lens[aY[a2.have++]]=0}a2.lencode=a2.lendyn;a2.lenbits=7;aX={bits:a2.lenbits};a1=G(i,a2.lens,0,19,a2.lencode,0,a2.work,aX);a2.lenbits=aX.bits;if(a1){a3.msg="invalid code lengths set";a2.mode=f;break}a2.have=0;a2.mode=h;case h:while(a2.have>>24;aM=(aK>>>16)&255;aN=aK&65535;if((aL)<=aD){break}if(aI===0){break inf_leave}aI--;aO+=aP[aW++]<>>=aL;aD-=aL;a2.lens[a2.have++]=aN}else{if(aN===16){aV=aL+2;while(aD>>=aL;aD-=aL;if(a2.have===0){a3.msg="invalid bit length repeat";a2.mode=f;break}aU=a2.lens[a2.have-1];aE=3+(aO&3);aO>>>=2;aD-=2}else{if(aN===17){aV=aL+3;while(aD>>=aL;aD-=aL;aU=0;aE=3+(aO&7);aO>>>=3;aD-=3}else{aV=aL+7;while(aD>>=aL;aD-=aL;aU=0;aE=11+(aO&127);aO>>>=7;aD-=7}}if(a2.have+aE>a2.nlen+a2.ndist){a3.msg="invalid bit length repeat";a2.mode=f;break}while(aE--){a2.lens[a2.have++]=aU}}}if(a2.mode===f){break}if(a2.lens[256]===0){a3.msg="invalid code -- missing end-of-block";a2.mode=f;break}a2.lenbits=9;aX={bits:a2.lenbits};a1=G(W,a2.lens,0,a2.nlen,a2.lencode,0,a2.work,aX);a2.lenbits=aX.bits;if(a1){a3.msg="invalid literal/lengths set";a2.mode=f;break}a2.distbits=6;a2.distcode=a2.distdyn;aX={bits:a2.distbits};a1=G(t,a2.lens,a2.nlen,a2.ndist,a2.distcode,0,a2.work,aX);a2.distbits=aX.bits;if(a1){a3.msg="invalid distances set";a2.mode=f;break}a2.mode=R;if(aF===az){break inf_leave}case R:a2.mode=Q;case Q:if(aI>=6&&aT>=258){a3.next_out=a0;a3.avail_out=aT;a3.next_in=aW;a3.avail_in=aI;a2.hold=aO;a2.bits=aD;F(a3,aC);a0=a3.next_out;aZ=a3.output;aT=a3.avail_out;aW=a3.next_in;aP=a3.input;aI=a3.avail_in;aO=a2.hold;aD=a2.bits;if(a2.mode===aj){a2.back=-1}break}a2.back=0;for(;;){aK=a2.lencode[aO&((1<>>24;aM=(aK>>>16)&255;aN=aK&65535;if(aL<=aD){break}if(aI===0){break inf_leave}aI--;aO+=aP[aW++]<>aQ)];aL=aK>>>24;aM=(aK>>>16)&255;aN=aK&65535;if((aQ+aL)<=aD){break}if(aI===0){break inf_leave}aI--;aO+=aP[aW++]<>>=aQ;aD-=aQ;a2.back+=aQ}aO>>>=aL;aD-=aL;a2.back+=aL;a2.length=aN;if(aM===0){a2.mode=X;break}if(aM&32){a2.back=-1;a2.mode=aj;break}if(aM&64){a3.msg="invalid literal/length code";a2.mode=f;break}a2.extra=aM&15;a2.mode=S;case S:if(a2.extra){aV=a2.extra;while(aD>>=a2.extra;aD-=a2.extra;a2.back+=a2.extra}a2.was=a2.length;a2.mode=q;case q:for(;;){aK=a2.distcode[aO&((1<>>24;aM=(aK>>>16)&255;aN=aK&65535;if((aL)<=aD){break}if(aI===0){break inf_leave}aI--;aO+=aP[aW++]<>aQ)];aL=aK>>>24;aM=(aK>>>16)&255;aN=aK&65535;if((aQ+aL)<=aD){break}if(aI===0){break inf_leave}aI--;aO+=aP[aW++]<>>=aQ;aD-=aQ;a2.back+=aQ}aO>>>=aL;aD-=aL;a2.back+=aL;if(aM&64){a3.msg="invalid distance code";a2.mode=f;break}a2.offset=aN;a2.extra=(aM)&15;a2.mode=r;case r:if(a2.extra){aV=a2.extra;while(aD>>=a2.extra;aD-=a2.extra;a2.back+=a2.extra}if(a2.offset>a2.dmax){a3.msg="invalid distance too far back";a2.mode=f;break}a2.mode=Y;case Y:if(aT===0){break inf_leave}aE=aC-aT;if(a2.offset>aE){aE=a2.offset-aE;if(aE>a2.whave){if(a2.sane){a3.msg="invalid distance too far back";a2.mode=f;break}}if(aE>a2.wnext){aE-=a2.wnext;aG=a2.wsize-aE}else{aG=a2.wnext-aE}if(aE>a2.length){aE=a2.length}aH=a2.window}else{aH=aZ;aG=a0-a2.offset;aE=a2.length}if(aE>aT){aE=aT}aT-=aE;a2.length-=aE;do{aZ[a0++]=aH[aG++]}while(--aE);if(a2.length===0){a2.mode=Q}break;case X:if(aT===0){break inf_leave}aZ[a0++]=a2.length;aT--;a2.mode=Q;break;case g:if(a2.wrap){while(aD<32){if(aI===0){break inf_leave}aI--;aO|=aP[aW++]<=1;Q--){if(x[Q]!==0){break}}if(V>Q){V=Q}if(Q===0){X[Y++]=(1<<24)|(64<<16)|0;X[Y++]=(1<<24)|(64<<16)|0;U.bits=1;return 0}for(R=1;R0&&(Z===e||Q!==1)){return -1}T[1]=0;for(L=1;Lj)||(Z===h&&aa>i)){return 1}var I=0;for(;;){I++;E=L-z;if(ab[W]A){F=B[C+ab[W]];G=t[u+ab[W]]}else{F=32+64;G=0}}J=1<<(L-z);D=1<>z)+D]=(E<<24)|(F<<16)|G|0}while(D!==0);J=1<<(L-1);while(H&J){J>>=1}if(J!==0){H&=J-1;H+=J}else{H=0}W++;if(--x[L]===0){if(L===Q){break}L=M[N+ab[W]]}if(L>V&&(H&P)!==O){if(z===0){z=V}S+=R;y=L-z;K=1<j)||(Z===h&&aa>i)){return 1}O=H&P;X[O]=(V<<24)|(y<<16)|(S-Y)|0}}if(H!==0){X[S+H]=((L-z)<<24)|(64<<16)|0}U.bits=V;return 0}},{"../utils/common":1}],10:[function(g,f,e){f.exports={2:"need dictionary",1:"stream end",0:"","-1":"file error","-2":"stream error","-3":"data error","-4":"insufficient memory","-5":"buffer error","-6":"incompatible version"}},{}],11:[function(g,f,e){function h(){this.input=null;this.next_in=0;this.avail_in=0;this.total_in=0;this.output=null;this.next_out=0;this.avail_out=0;this.total_out=0;this.msg="";this.state=null;this.data_type=2;this.adler=0}f.exports=h},{}],"/lib/inflate.js":[function(m,k,f){var q=m("./zlib/inflate");var p=m("./utils/common");var n=m("./utils/strings");var e=m("./zlib/constants");var l=m("./zlib/messages");var r=m("./zlib/zstream");var g=m("./zlib/gzheader");var o=Object.prototype.toString;function i(t){if(!(this instanceof i)){return new i(t)}this.options=p.assign({chunkSize:16384,windowBits:0,to:""},t||{});var s=this.options;if(s.raw&&(s.windowBits>=0)&&(s.windowBits<16)){s.windowBits=-s.windowBits;if(s.windowBits===0){s.windowBits=-15}}if((s.windowBits>=0)&&(s.windowBits<16)&&!(t&&t.windowBits)){s.windowBits+=32}if((s.windowBits>15)&&(s.windowBits<48)){if((s.windowBits&15)===0){s.windowBits|=15}}this.err=0;this.msg="";this.ended=!1;this.chunks=[];this.strm=new r();this.strm.avail_out=0;var u=q.inflateInit2(this.strm,s.windowBits);if(u!==e.Z_OK){throw new Error(l[u])}this.header=new g();q.inflateGetHeader(this.strm,this.header)}i.prototype.push=function(v,y){var B=this.strm;var u=this.options.chunkSize;var x=this.options.dictionary;var A,s;var z,C,D;var w;var t=!1;if(this.ended){return !1}s=(y===~~y)?y:((y===!0)?e.Z_FINISH:e.Z_NO_FLUSH);if(typeof v==="string"){B.input=n.binstring2buf(v)}else{if(o.call(v)==="[object ArrayBuffer]"){B.input=new Uint8Array(v)}else{B.input=v}}B.next_in=0;B.avail_in=B.input.length;do{if(B.avail_out===0){B.output=new p.Buf8(u);B.next_out=0;B.avail_out=u}A=q.inflate(B,e.Z_NO_FLUSH);if(A===e.Z_NEED_DICT&&x){if(typeof x==="string"){w=n.string2buf(x)}else{if(o.call(x)==="[object ArrayBuffer]"){w=new Uint8Array(x)}else{w=x}}A=q.inflateSetDictionary(this.strm,w)}if(A===e.Z_BUF_ERROR&&t===!0){A=e.Z_OK;t=!1}if(A!==e.Z_STREAM_END&&A!==e.Z_OK){this.onEnd(A);this.ended=!0;return !1}if(B.next_out){if(B.avail_out===0||A===e.Z_STREAM_END||(B.avail_in===0&&(s===e.Z_FINISH||s===e.Z_SYNC_FLUSH))){if(this.options.to==="string"){z=n.utf8border(B.output,B.next_out);C=B.next_out-z;D=n.buf2string(B.output,z);B.next_out=C;B.avail_out=u-C;if(C){p.arraySet(B.output,B.output,z,C,0)}this.onData(D)}else{this.onData(p.shrinkBuf(B.output,B.next_out))}}}if(B.avail_in===0&&B.avail_out===0){t=!0}}while((B.avail_in>0||B.avail_out===0)&&A!==e.Z_STREAM_END);if(A===e.Z_STREAM_END){s=e.Z_FINISH}if(s===e.Z_FINISH){A=q.inflateEnd(this.strm);this.onEnd(A);this.ended=!0;return A===e.Z_OK}if(s===e.Z_SYNC_FLUSH){this.onEnd(e.Z_OK);B.avail_out=0;return !0}return !0};i.prototype.onData=function(s){this.chunks.push(s)};i.prototype.onEnd=function(s){if(s===e.Z_OK){if(this.options.to==="string"){this.result=this.chunks.join("")}else{this.result=p.flattenChunks(this.chunks)}}this.chunks=[];this.err=s;this.msg=this.strm.msg};function h(t,u){var s=new i(u);s.push(t,!0);if(s.err){throw s.msg}return s.result}function j(s,t){t=t||{};t.raw=!0;return h(s,t)}f.Inflate=i;f.inflate=h;f.inflateRaw=j;f.ungzip=h},{"./utils/common":1,"./utils/strings":2,"./zlib/constants":4,"./zlib/gzheader":6,"./zlib/inflate":8,"./zlib/messages":10,"./zlib/zstream":11}]},{},[])("/lib/inflate.js")});function PngImage(){var m="",i=this,j=new PngToy(),a,b,n=0,g=0,c=!1;this.onload=null;this.onerror=null;Object.defineProperty(this,"src",{get:function(){return m},set:function(h){m=h;k()}});Object.defineProperty(this,"width",{get:function(){return n}});Object.defineProperty(this,"height",{get:function(){return g}});Object.defineProperty(this,"naturalWidth",{get:function(){return n}});Object.defineProperty(this,"naturalHeight",{get:function(){return g}});Object.defineProperty(this,"image",{get:function(){return b}});Object.defineProperty(this,"pngtoy",{get:function(){return j}});Object.defineProperty(this,"complete",{get:function(){return c}});function k(){j.fetch(m).then(e,f)}function e(h){j.decode(h).then(d,f)}function d(h){a=h;n=h.width;g=h.height;j.convertToCanvas(h,{ignoreAspectRatio:!1,useGamma:!1}).then(l.bind(i),f.bind(i))}function l(h){b=h;c=!0;if(i.onload){i.onload({timeStamp:Date.now()})}}function f(h){if(i.onerror){i.onerror({message:h,timeStamp:Date.now()})}}}; 7 | window.PngToy = PngToy; 8 | --------------------------------------------------------------------------------