123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643 |
- /**
- * @licstart The following is the entire license notice for the
- * Javascript code in this page
- *
- * Copyright 2019 Mozilla Foundation
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * @licend The above is the entire license notice for the
- * Javascript code in this page
- */
- "use strict";
- var _function = require("../../core/function");
- var _ps_parser = require("../../core/ps_parser");
- var _stream = require("../../core/stream");
- describe('function', function () {
- beforeEach(function () {
- jasmine.addMatchers({
- toMatchArray: function toMatchArray(util, customEqualityTesters) {
- return {
- compare: function compare(actual, expected) {
- var result = {};
- if (actual.length !== expected.length) {
- result.pass = false;
- result.message = 'Array length: ' + actual.length + ', expected: ' + expected.length;
- return result;
- }
- result.pass = true;
- for (var i = 0; i < expected.length; i++) {
- var a = actual[i],
- b = expected[i];
- if (Array.isArray(b)) {
- if (a.length !== b.length) {
- result.pass = false;
- break;
- }
- for (var j = 0; j < a.length; j++) {
- var suba = a[j],
- subb = b[j];
- if (suba !== subb) {
- result.pass = false;
- break;
- }
- }
- } else {
- if (a !== b) {
- result.pass = false;
- break;
- }
- }
- }
- return result;
- }
- };
- }
- });
- });
- describe('PostScriptParser', function () {
- function parse(program) {
- var stream = new _stream.StringStream(program);
- var parser = new _ps_parser.PostScriptParser(new _ps_parser.PostScriptLexer(stream));
- return parser.parse();
- }
- it('parses empty programs', function () {
- var output = parse('{}');
- expect(output.length).toEqual(0);
- });
- it('parses positive numbers', function () {
- var number = 999;
- var program = parse('{ ' + number + ' }');
- var expectedProgram = [number];
- expect(program).toMatchArray(expectedProgram);
- });
- it('parses negative numbers', function () {
- var number = -999;
- var program = parse('{ ' + number + ' }');
- var expectedProgram = [number];
- expect(program).toMatchArray(expectedProgram);
- });
- it('parses negative floats', function () {
- var number = 3.3;
- var program = parse('{ ' + number + ' }');
- var expectedProgram = [number];
- expect(program).toMatchArray(expectedProgram);
- });
- it('parses operators', function () {
- var program = parse('{ sub }');
- var expectedProgram = ['sub'];
- expect(program).toMatchArray(expectedProgram);
- });
- it('parses if statements', function () {
- var program = parse('{ { 99 } if }');
- var expectedProgram = [3, 'jz', 99];
- expect(program).toMatchArray(expectedProgram);
- });
- it('parses ifelse statements', function () {
- var program = parse('{ { 99 } { 44 } ifelse }');
- var expectedProgram = [5, 'jz', 99, 6, 'j', 44];
- expect(program).toMatchArray(expectedProgram);
- });
- it('handles missing brackets', function () {
- expect(function () {
- parse('{');
- }).toThrow(new Error('Unexpected symbol: found undefined expected 1.'));
- });
- it('handles junk after the end', function () {
- var number = 3.3;
- var program = parse('{ ' + number + ' }#');
- var expectedProgram = [number];
- expect(program).toMatchArray(expectedProgram);
- });
- });
- describe('PostScriptEvaluator', function () {
- function evaluate(program) {
- var stream = new _stream.StringStream(program);
- var parser = new _ps_parser.PostScriptParser(new _ps_parser.PostScriptLexer(stream));
- var code = parser.parse();
- var evaluator = new _function.PostScriptEvaluator(code);
- var output = evaluator.execute();
- return output;
- }
- it('pushes stack', function () {
- var stack = evaluate('{ 99 }');
- var expectedStack = [99];
- expect(stack).toMatchArray(expectedStack);
- });
- it('handles if with true', function () {
- var stack = evaluate('{ 1 {99} if }');
- var expectedStack = [99];
- expect(stack).toMatchArray(expectedStack);
- });
- it('handles if with false', function () {
- var stack = evaluate('{ 0 {99} if }');
- var expectedStack = [];
- expect(stack).toMatchArray(expectedStack);
- });
- it('handles ifelse with true', function () {
- var stack = evaluate('{ 1 {99} {77} ifelse }');
- var expectedStack = [99];
- expect(stack).toMatchArray(expectedStack);
- });
- it('handles ifelse with false', function () {
- var stack = evaluate('{ 0 {99} {77} ifelse }');
- var expectedStack = [77];
- expect(stack).toMatchArray(expectedStack);
- });
- it('handles nested if', function () {
- var stack = evaluate('{ 1 {1 {77} if} if }');
- var expectedStack = [77];
- expect(stack).toMatchArray(expectedStack);
- });
- it('abs', function () {
- var stack = evaluate('{ -2 abs }');
- var expectedStack = [2];
- expect(stack).toMatchArray(expectedStack);
- });
- it('adds', function () {
- var stack = evaluate('{ 1 2 add }');
- var expectedStack = [3];
- expect(stack).toMatchArray(expectedStack);
- });
- it('boolean and', function () {
- var stack = evaluate('{ true false and }');
- var expectedStack = [false];
- expect(stack).toMatchArray(expectedStack);
- });
- it('bitwise and', function () {
- var stack = evaluate('{ 254 1 and }');
- var expectedStack = [254 & 1];
- expect(stack).toMatchArray(expectedStack);
- });
- it('calculates the inverse tangent of a number', function () {
- var stack = evaluate('{ 90 atan }');
- var expectedStack = [Math.atan(90)];
- expect(stack).toMatchArray(expectedStack);
- });
- it('handles bitshifting ', function () {
- var stack = evaluate('{ 50 2 bitshift }');
- var expectedStack = [200];
- expect(stack).toMatchArray(expectedStack);
- });
- it('calculates the ceiling value', function () {
- var stack = evaluate('{ 9.9 ceiling }');
- var expectedStack = [10];
- expect(stack).toMatchArray(expectedStack);
- });
- it('copies', function () {
- var stack = evaluate('{ 99 98 2 copy }');
- var expectedStack = [99, 98, 99, 98];
- expect(stack).toMatchArray(expectedStack);
- });
- it('calculates the cosine of a number', function () {
- var stack = evaluate('{ 90 cos }');
- var expectedStack = [Math.cos(90)];
- expect(stack).toMatchArray(expectedStack);
- });
- it('converts to int', function () {
- var stack = evaluate('{ 9.9 cvi }');
- var expectedStack = [9];
- expect(stack).toMatchArray(expectedStack);
- });
- it('converts negatives to int', function () {
- var stack = evaluate('{ -9.9 cvi }');
- var expectedStack = [-9];
- expect(stack).toMatchArray(expectedStack);
- });
- it('converts to real', function () {
- var stack = evaluate('{ 55.34 cvr }');
- var expectedStack = [55.34];
- expect(stack).toMatchArray(expectedStack);
- });
- it('divides', function () {
- var stack = evaluate('{ 6 5 div }');
- var expectedStack = [1.2];
- expect(stack).toMatchArray(expectedStack);
- });
- it('maps division by zero to infinity', function () {
- var stack = evaluate('{ 6 0 div }');
- var expectedStack = [Infinity];
- expect(stack).toMatchArray(expectedStack);
- });
- it('duplicates', function () {
- var stack = evaluate('{ 99 dup }');
- var expectedStack = [99, 99];
- expect(stack).toMatchArray(expectedStack);
- });
- it('accepts an equality', function () {
- var stack = evaluate('{ 9 9 eq }');
- var expectedStack = [true];
- expect(stack).toMatchArray(expectedStack);
- });
- it('rejects an inequality', function () {
- var stack = evaluate('{ 9 8 eq }');
- var expectedStack = [false];
- expect(stack).toMatchArray(expectedStack);
- });
- it('exchanges', function () {
- var stack = evaluate('{ 44 99 exch }');
- var expectedStack = [99, 44];
- expect(stack).toMatchArray(expectedStack);
- });
- it('handles exponentiation', function () {
- var stack = evaluate('{ 10 2 exp }');
- var expectedStack = [100];
- expect(stack).toMatchArray(expectedStack);
- });
- it('pushes false onto the stack', function () {
- var stack = evaluate('{ false }');
- var expectedStack = [false];
- expect(stack).toMatchArray(expectedStack);
- });
- it('calculates the floor value', function () {
- var stack = evaluate('{ 9.9 floor }');
- var expectedStack = [9];
- expect(stack).toMatchArray(expectedStack);
- });
- it('handles greater than or equal to', function () {
- var stack = evaluate('{ 10 9 ge }');
- var expectedStack = [true];
- expect(stack).toMatchArray(expectedStack);
- });
- it('rejects less than for greater than or equal to', function () {
- var stack = evaluate('{ 8 9 ge }');
- var expectedStack = [false];
- expect(stack).toMatchArray(expectedStack);
- });
- it('handles greater than', function () {
- var stack = evaluate('{ 10 9 gt }');
- var expectedStack = [true];
- expect(stack).toMatchArray(expectedStack);
- });
- it('rejects less than or equal for greater than', function () {
- var stack = evaluate('{ 9 9 gt }');
- var expectedStack = [false];
- expect(stack).toMatchArray(expectedStack);
- });
- it('divides to integer', function () {
- var stack = evaluate('{ 2 3 idiv }');
- var expectedStack = [0];
- expect(stack).toMatchArray(expectedStack);
- });
- it('divides to negative integer', function () {
- var stack = evaluate('{ -2 3 idiv }');
- var expectedStack = [0];
- expect(stack).toMatchArray(expectedStack);
- });
- it('duplicates index', function () {
- var stack = evaluate('{ 4 3 2 1 2 index }');
- var expectedStack = [4, 3, 2, 1, 3];
- expect(stack).toMatchArray(expectedStack);
- });
- it('handles less than or equal to', function () {
- var stack = evaluate('{ 9 10 le }');
- var expectedStack = [true];
- expect(stack).toMatchArray(expectedStack);
- });
- it('rejects greater than for less than or equal to', function () {
- var stack = evaluate('{ 10 9 le }');
- var expectedStack = [false];
- expect(stack).toMatchArray(expectedStack);
- });
- it('calculates the natural logarithm', function () {
- var stack = evaluate('{ 10 ln }');
- var expectedStack = [Math.log(10)];
- expect(stack).toMatchArray(expectedStack);
- });
- it('calculates the base 10 logarithm', function () {
- var stack = evaluate('{ 100 log }');
- var expectedStack = [2];
- expect(stack).toMatchArray(expectedStack);
- });
- it('handles less than', function () {
- var stack = evaluate('{ 9 10 lt }');
- var expectedStack = [true];
- expect(stack).toMatchArray(expectedStack);
- });
- it('rejects greater than or equal to for less than', function () {
- var stack = evaluate('{ 10 9 lt }');
- var expectedStack = [false];
- expect(stack).toMatchArray(expectedStack);
- });
- it('performs the modulo operation', function () {
- var stack = evaluate('{ 4 3 mod }');
- var expectedStack = [1];
- expect(stack).toMatchArray(expectedStack);
- });
- it('multiplies two numbers (positive result)', function () {
- var stack = evaluate('{ 9 8 mul }');
- var expectedStack = [72];
- expect(stack).toMatchArray(expectedStack);
- });
- it('multiplies two numbers (negative result)', function () {
- var stack = evaluate('{ 9 -8 mul }');
- var expectedStack = [-72];
- expect(stack).toMatchArray(expectedStack);
- });
- it('accepts an inequality', function () {
- var stack = evaluate('{ 9 8 ne }');
- var expectedStack = [true];
- expect(stack).toMatchArray(expectedStack);
- });
- it('rejects an equality', function () {
- var stack = evaluate('{ 9 9 ne }');
- var expectedStack = [false];
- expect(stack).toMatchArray(expectedStack);
- });
- it('negates', function () {
- var stack = evaluate('{ 4.5 neg }');
- var expectedStack = [-4.5];
- expect(stack).toMatchArray(expectedStack);
- });
- it('boolean not', function () {
- var stack = evaluate('{ true not }');
- var expectedStack = [false];
- expect(stack).toMatchArray(expectedStack);
- });
- it('bitwise not', function () {
- var stack = evaluate('{ 12 not }');
- var expectedStack = [-13];
- expect(stack).toMatchArray(expectedStack);
- });
- it('boolean or', function () {
- var stack = evaluate('{ true false or }');
- var expectedStack = [true];
- expect(stack).toMatchArray(expectedStack);
- });
- it('bitwise or', function () {
- var stack = evaluate('{ 254 1 or }');
- var expectedStack = [254 | 1];
- expect(stack).toMatchArray(expectedStack);
- });
- it('pops stack', function () {
- var stack = evaluate('{ 1 2 pop }');
- var expectedStack = [1];
- expect(stack).toMatchArray(expectedStack);
- });
- it('rolls stack right', function () {
- var stack = evaluate('{ 1 3 2 2 4 1 roll }');
- var expectedStack = [2, 1, 3, 2];
- expect(stack).toMatchArray(expectedStack);
- });
- it('rolls stack left', function () {
- var stack = evaluate('{ 1 3 2 2 4 -1 roll }');
- var expectedStack = [3, 2, 2, 1];
- expect(stack).toMatchArray(expectedStack);
- });
- it('rounds a number', function () {
- var stack = evaluate('{ 9.52 round }');
- var expectedStack = [10];
- expect(stack).toMatchArray(expectedStack);
- });
- it('calculates the sine of a number', function () {
- var stack = evaluate('{ 90 sin }');
- var expectedStack = [Math.sin(90)];
- expect(stack).toMatchArray(expectedStack);
- });
- it('calculates a square root (integer)', function () {
- var stack = evaluate('{ 100 sqrt }');
- var expectedStack = [10];
- expect(stack).toMatchArray(expectedStack);
- });
- it('calculates a square root (float)', function () {
- var stack = evaluate('{ 99 sqrt }');
- var expectedStack = [Math.sqrt(99)];
- expect(stack).toMatchArray(expectedStack);
- });
- it('subtracts (positive result)', function () {
- var stack = evaluate('{ 6 4 sub }');
- var expectedStack = [2];
- expect(stack).toMatchArray(expectedStack);
- });
- it('subtracts (negative result)', function () {
- var stack = evaluate('{ 4 6 sub }');
- var expectedStack = [-2];
- expect(stack).toMatchArray(expectedStack);
- });
- it('pushes true onto the stack', function () {
- var stack = evaluate('{ true }');
- var expectedStack = [true];
- expect(stack).toMatchArray(expectedStack);
- });
- it('truncates a number', function () {
- var stack = evaluate('{ 35.004 truncate }');
- var expectedStack = [35];
- expect(stack).toMatchArray(expectedStack);
- });
- it('calculates an exclusive or value', function () {
- var stack = evaluate('{ 3 9 xor }');
- var expectedStack = [10];
- expect(stack).toMatchArray(expectedStack);
- });
- });
- describe('PostScriptCompiler', function () {
- function check(code, domain, range, samples) {
- var compiler = new _function.PostScriptCompiler();
- var compiledCode = compiler.compile(code, domain, range);
- if (samples === null) {
- expect(compiledCode).toBeNull();
- } else {
- expect(compiledCode).not.toBeNull();
- var fn = new Function('src', 'srcOffset', 'dest', 'destOffset', compiledCode);
- for (var i = 0; i < samples.length; i++) {
- var out = new Float32Array(samples[i].output.length);
- fn(samples[i].input, 0, out, 0);
- expect(Array.prototype.slice.call(out, 0)).toMatchArray(samples[i].output);
- }
- }
- }
- it('check compiled add', function () {
- check([0.25, 0.5, 'add'], [], [0, 1], [{
- input: [],
- output: [0.75]
- }]);
- check([0, 'add'], [0, 1], [0, 1], [{
- input: [0.25],
- output: [0.25]
- }]);
- check([0.5, 'add'], [0, 1], [0, 1], [{
- input: [0.25],
- output: [0.75]
- }]);
- check([0, 'exch', 'add'], [0, 1], [0, 1], [{
- input: [0.25],
- output: [0.25]
- }]);
- check([0.5, 'exch', 'add'], [0, 1], [0, 1], [{
- input: [0.25],
- output: [0.75]
- }]);
- check(['add'], [0, 1, 0, 1], [0, 1], [{
- input: [0.25, 0.5],
- output: [0.75]
- }]);
- check(['add'], [0, 1], [0, 1], null);
- });
- it('check compiled sub', function () {
- check([0.5, 0.25, 'sub'], [], [0, 1], [{
- input: [],
- output: [0.25]
- }]);
- check([0, 'sub'], [0, 1], [0, 1], [{
- input: [0.25],
- output: [0.25]
- }]);
- check([0.5, 'sub'], [0, 1], [0, 1], [{
- input: [0.75],
- output: [0.25]
- }]);
- check([0, 'exch', 'sub'], [0, 1], [-1, 1], [{
- input: [0.25],
- output: [-0.25]
- }]);
- check([0.75, 'exch', 'sub'], [0, 1], [-1, 1], [{
- input: [0.25],
- output: [0.5]
- }]);
- check(['sub'], [0, 1, 0, 1], [-1, 1], [{
- input: [0.25, 0.5],
- output: [-0.25]
- }]);
- check(['sub'], [0, 1], [0, 1], null);
- check([1, 'dup', 3, 2, 'roll', 'sub', 'sub'], [0, 1], [0, 1], [{
- input: [0.75],
- output: [0.75]
- }]);
- });
- it('check compiled mul', function () {
- check([0.25, 0.5, 'mul'], [], [0, 1], [{
- input: [],
- output: [0.125]
- }]);
- check([0, 'mul'], [0, 1], [0, 1], [{
- input: [0.25],
- output: [0]
- }]);
- check([0.5, 'mul'], [0, 1], [0, 1], [{
- input: [0.25],
- output: [0.125]
- }]);
- check([1, 'mul'], [0, 1], [0, 1], [{
- input: [0.25],
- output: [0.25]
- }]);
- check([0, 'exch', 'mul'], [0, 1], [0, 1], [{
- input: [0.25],
- output: [0]
- }]);
- check([0.5, 'exch', 'mul'], [0, 1], [0, 1], [{
- input: [0.25],
- output: [0.125]
- }]);
- check([1, 'exch', 'mul'], [0, 1], [0, 1], [{
- input: [0.25],
- output: [0.25]
- }]);
- check(['mul'], [0, 1, 0, 1], [0, 1], [{
- input: [0.25, 0.5],
- output: [0.125]
- }]);
- check(['mul'], [0, 1], [0, 1], null);
- });
- it('check compiled max', function () {
- check(['dup', 0.75, 'gt', 7, 'jz', 'pop', 0.75], [0, 1], [0, 1], [{
- input: [0.5],
- output: [0.5]
- }]);
- check(['dup', 0.75, 'gt', 7, 'jz', 'pop', 0.75], [0, 1], [0, 1], [{
- input: [1],
- output: [0.75]
- }]);
- check(['dup', 0.75, 'gt', 5, 'jz', 'pop', 0.75], [0, 1], [0, 1], null);
- });
- it('check pop/roll/index', function () {
- check([1, 'pop'], [0, 1], [0, 1], [{
- input: [0.5],
- output: [0.5]
- }]);
- check([1, 3, -1, 'roll'], [0, 1, 0, 1], [0, 1, 0, 1, 0, 1], [{
- input: [0.25, 0.5],
- output: [0.5, 1, 0.25]
- }]);
- check([1, 3, 1, 'roll'], [0, 1, 0, 1], [0, 1, 0, 1, 0, 1], [{
- input: [0.25, 0.5],
- output: [1, 0.25, 0.5]
- }]);
- check([1, 3, 1.5, 'roll'], [0, 1, 0, 1], [0, 1, 0, 1, 0, 1], null);
- check([1, 1, 'index'], [0, 1], [0, 1, 0, 1, 0, 1], [{
- input: [0.5],
- output: [0.5, 1, 0.5]
- }]);
- check([1, 3, 'index', 'pop'], [0, 1], [0, 1], null);
- check([1, 0.5, 'index', 'pop'], [0, 1], [0, 1], null);
- });
- it('check input boundaries', function () {
- check([], [0, 0.5], [0, 1], [{
- input: [1],
- output: [0.5]
- }]);
- check([], [0.5, 1], [0, 1], [{
- input: [0],
- output: [0.5]
- }]);
- check(['dup'], [0.5, 0.75], [0, 1, 0, 1], [{
- input: [0],
- output: [0.5, 0.5]
- }]);
- check([], [100, 1001], [0, 10000], [{
- input: [1000],
- output: [1000]
- }]);
- });
- it('check output boundaries', function () {
- check([], [0, 1], [0, 0.5], [{
- input: [1],
- output: [0.5]
- }]);
- check([], [0, 1], [0.5, 1], [{
- input: [0],
- output: [0.5]
- }]);
- check(['dup'], [0, 1], [0.5, 1, 0.75, 1], [{
- input: [0],
- output: [0.5, 0.75]
- }]);
- check([], [0, 10000], [100, 1001], [{
- input: [1000],
- output: [1000]
- }]);
- });
- it('compile optimized', function () {
- var compiler = new _function.PostScriptCompiler();
- var code = [0, 'add', 1, 1, 3, -1, 'roll', 'sub', 'sub', 1, 'mul'];
- var compiledCode = compiler.compile(code, [0, 1], [0, 1]);
- expect(compiledCode).toEqual('dest[destOffset + 0] = Math.max(0, Math.min(1, src[srcOffset + 0]));');
- });
- });
- });
|