Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // TinyVG Renderer
- // Copyright (C) by honey the codewitch
- // MIT License
- // To use, pass am ArrayBuffer with the
- // TVG document to tvgDimensions
- // or tvgRender. With render,
- // you place an empty SVG element,
- // ex: <svg id="..." xmlms="..." />
- // and pass the id of it to the render
- // method. At that point, the SVG tag
- // will be populated and rendered by
- // the browser.
- const tvgInit = (data, id) => {
- return {
- data: data,
- view: new DataView(data),
- cursor: 0,
- scale: 0,
- color_encoding: 0,
- coord_range: 0,
- width: 0, height: 0,
- colors_size: 0,
- colors: [],
- doc: document.getElementById(id), //SVGSVGElement
- elem: undefined, // SVGElement
- gradIndex: 0
- };
- }
- const tvgDistance = (pointLhs, pointRhs) => {
- const xd = pointRhs.x - pointLhs.x;
- const yd = pointRhs.y - pointLhs.y;
- return Math.sqrt((xd * xd) + (yd * yd));
- }
- const tvgAdvCoord = (rangeOrCtx) => {
- let range = rangeOrCtx;
- if (rangeOrCtx.coord_range) {
- range = rangeOrCtx.range;
- }
- switch (range) {
- case 0://"default"
- return 2;
- case 1://"reduced":
- return 1;
- case 2://"extended"
- return 4;
- }
- }
- const tvgMapZeroToMax = (rangeOrCtx, value) => {
- let range = rangeOrCtx;
- if (rangeOrCtx.coord_range) {
- range = rangeOrCtx.range;
- }
- if (0 == value) {
- switch (range) {
- case 0: //"default"
- return 0xFFFF;
- case 1: //"reduced"
- return 0xFF;
- case 2: //"extended"
- return 0xFFFFFFFF;
- }
- return undefined;
- }
- return value;
- }
- const tvgReadCoordBI = (range, startIndex, data) => {
- const view = new DataView(data);
- switch (range) {
- case 0: //"default"
- return view.getUint16(startIndex, true);
- case 1: //"reduced"
- return view.getUint8(startIndex);
- case 2: //"extended"
- return view.getUint32(startIndex, true);
- }
- return undefined;
- }
- const tvgReadCoord = (ctx) => {
- let result = undefined;
- switch (ctx.coord_range) {
- case 0: //"default"
- result = ctx.view.getUint16(ctx.cursor, true);
- ctx.cursor += 2;
- break;
- case 1: //"reduced"
- result = ctx.view.getUint8(ctx.cursor);
- ctx.cursor += 1;
- break;
- case 2: //"extended"
- result = ctx.view.getUint32(ctx.cursor, true);
- ctx.cursor += 4;
- break;
- }
- return result;
- }
- const tvgReadU32 = (ctx) => {
- let count = 0;
- let result = 0;
- var byte;
- while (true) {
- byte = ctx.view.getUint8(ctx.cursor++);
- const val = (byte & 0x7F) << (7 * count);
- result |= val;
- if ((byte & 0x80) === 0)
- break;
- ++count;
- }
- return result;
- }
- const tvgDownscaleCoord = (ctx, coord) => {
- const factor = (1) << ctx.scale;
- return coord / factor;
- }
- const tvgReadUnit = (ctx) => {
- const val = tvgReadCoord(ctx);
- return tvgDownscaleCoord(ctx, val);
- }
- const tvgReadPoint = (ctx) => {
- const x = tvgReadUnit(ctx);
- const y = tvgReadUnit(ctx);
- return { x: x, y: y };
- }
- const tvgReadColor = (ctx) => {
- switch (ctx.color_encoding) {
- case 2: { // TVG_COLOR_F32:
- // read four values
- const data = [];
- data.push(ctx.view.getFloat32(ctx.cursor, true)); ctx.cursor += 4;
- data.push(ctx.view.getFloat32(ctx.cursor, true)); ctx.cursor += 4;
- data.push(ctx.view.getFloat32(ctx.cursor, true)); ctx.cursor += 4;
- data.push(ctx.view.getFloat32(ctx.cursor, true)); ctx.cursor += 4;
- return { r: data[0], g: data[1], b: data[2], a: data[3] };
- }
- case 1: { // TVG_COLOR_U565:
- const data = ctx.view.getUint16(ctx.cursor, true);
- ctx.cursor += 2;
- return {
- r: (data & 0x1F) / 15.0,
- g: ((data >>> 5) & 0x3F) / 31.0,
- b: ((data >>> 11) & 0x1F) / 15.0,
- a: 1.0
- };
- }
- case 0: { // TVG_COLOR_U8888:
- // read four values
- const data = [];
- data.push(ctx.view.getUint8(ctx.cursor++));
- data.push(ctx.view.getUint8(ctx.cursor++));
- data.push(ctx.view.getUint8(ctx.cursor++));
- data.push(ctx.view.getUint8(ctx.cursor++));
- return { r: data[0] / 255.0, g: data[1] / 255.0, b: data[2] / 255.0, a: data[3] / 255.0 };
- }
- case 3: // TVG_COLOR_CUSTOM
- throw "TinyVG: Custom color table not supported";
- default:
- throw "TinyVG: Invalid color format";
- }
- }
- const tvgParseGradient = (ctx) => {
- const point0 = tvgReadPoint(ctx);
- const point1 = tvgReadPoint(ctx);
- const color0 = tvgReadU32(ctx);
- const color1 = tvgReadU32(ctx);
- return { point0: point0, point1: point1, color0: color0, color1: color1 };
- }
- const tvgParseStyle = (ctx, kind) => {
- switch (kind) {
- case 0: // TVG_STYLE_FLAT:
- return { kind: kind, flat: tvgReadU32(ctx) };
- case 1: // TVG_STYLE_LINEAR:
- return { kind: kind, linear: tvgParseGradient(ctx) };
- case 2: //TVG_STYLE_RADIAL:
- return { kind: kind, radial: tvgParseGradient(ctx) };
- default:
- throw "TinyVG: Invalid format parsing style";
- }
- }
- const tvgParseFillHeader = (ctx, kind) => {
- const u32 = tvgReadU32(ctx);
- const size = u32 + 1;
- //out_header->size = count;
- const style = tvgParseStyle(ctx, kind);
- return { size: size, style: style };
- }
- const tvgParseLineHeader = (ctx, kind) => {
- const u32 = tvgReadU32(ctx);
- const size = u32 + 1;
- const style = tvgParseStyle(ctx, kind);
- const line_width = tvgReadUnit(ctx);
- return { size: size, style: style, line_width: line_width };
- }
- const tvgParseLineFillHeader = (ctx, kind) => {
- var d = ctx.view.getUint8(ctx.cursor++);
- const size = (d & 0x3F) + 1;
- const fill_style = tvgParseStyle(ctx, kind);
- const line_style = tvgParseStyle(ctx, (d >>> 6) & 0x03);
- const line_width = tvgReadUnit(ctx);
- return { size: size, fill_style: fill_style, line_style: line_style, line_width: line_width };
- }
- const tvgParsePathD = (ctx, size) => {
- var st, cur;
- var pt;
- var u32;
- var f32;
- var d;
- let result = "";
- pt = tvgReadPoint(ctx);
- result += `M${pt.x} ${pt.y}`;
- st = pt;
- cur = pt;
- for (let j = 0; j < size; ++j) {
- d = ctx.view.getUint8(ctx.cursor++);
- if (((d >>> 4) & 1) !== 0) { // has line
- tvgReadUnit(ctx); // throw away line width (future use)
- }
- switch (d & 7) {
- case 0: // TVG_PATH_LINE:
- pt = tvgReadPoint(ctx);
- result += ` L${pt.x} ${pt.y}`
- cur = pt;
- break;
- case 1: // TVG_PATH_HLINE:
- pt.x = tvgReadUnit(ctx);;
- pt.y = cur.y;
- result += ` H${pt.x}`;
- cur = pt;
- break;
- case 2: // TVG_PATH_VLINE:
- pt.x = cur.x;
- pt.y = tvgReadUnit(ctx);
- result += ` V${pt.y}`;
- cur = pt;
- break;
- case 3: { // TVG_PATH_CUBIC:
- const ctrl1 = tvgReadPoint(ctx);
- const ctrl2 = tvgReadPoint(ctx);
- const endp = tvgReadPoint(ctx);
- result += ` C${ctrl1.x} ${ctrl1.y} ${ctrl2.x} ${ctrl2.y} ${endp.x} ${endp.y}`;
- cur = endp;
- } break;
- case 4: { // TVG_PATH_ARC_CIRCLE: {
- d = ctx.view.getUint8(ctx.cursor++);
- const radius = tvgReadUnit(ctx);
- pt = tvgReadPoint(ctx);
- result += ` A${radius} ${radius} 0 ${d & 1} ${1 - ((d >>> 1) & 1)} ${pt.x} ${pt.y}`;
- cur = pt;
- } break;
- case 5: { // TVG_PATH_ARC_ELLIPSE:
- d = ctx.view.getUint8(ctx.cursor++);
- const radius_x = tvgReadUnit(ctx);
- const radius_y = tvgReadUnit(ctx);
- const rotation = tvgReadUnit(ctx);
- pt = tvgReadPoint(ctx);
- result += ` A${radius_x} ${radius_y} ${rotation} ${d & 1} ${1 - ((d >>> 1) & 1)} ${pt.x} ${pt.y}`;
- cur = pt;
- } break;
- case 6: // TVG_PATH_CLOSE:
- result += ' Z';
- cur = st;
- break;
- case 7: { // TVG_PATH_QUAD:
- const ctrl = tvgReadPoint(ctx);
- const endp = tvgReadPoint(ctx);
- result += ` Q${ctrl.x} ${ctrl.y} ${endp.x} ${endp.y}`
- cur = endp;
- } break;
- default:
- throw "TinyVG: Unrecognized command parsing path";
- }
- }
- return result;
- }
- const tvgParseRect = (ctx) => {
- const pt = tvgReadPoint(ctx);
- const w = tvgReadUnit(ctx);
- const h = tvgReadUnit(ctx);
- return { x: pt.x, y: pt.y, width: w, height: h };
- }
- const tvgToHex = (code) => {
- let result = code.toString(16);
- if (result.length === 1) {
- return "0" + result;
- }
- return result;
- }
- const tvgColorToSvgColorAndOpacity = (col) => {
- return { color: `#${tvgToHex(col.r * 255)}${tvgToHex(col.g * 255)}${tvgToHex(col.b * 255)}`, opacity: col.a };
- }
- const tvgCreateSvgNode = (n, v) => {
- n = document.createElementNS("http://www.w3.org/2000/svg", n);
- if (v) {
- for (let p in v) {
- n.setAttributeNS(null, p.replace(/[A-Z]/g, function (m, p, o, s) { return "-" + m.toLowerCase(); }), v[p]);
- }
- }
- return n;
- }
- const tvgAddSvgAttribute = (n, a, v) => {
- n.setAttributeNS(null, a, v);
- }
- const tvgCreateSvgGradient = (ctx, style) => {
- let da = ctx.doc.getElementsByTagNameNS("http://www.w3.org/2000/svg", "defs");
- var defs;
- if (da.length == 0) {
- defs = tvgCreateSvgNode("defs");
- ctx.doc.prepend(defs);
- } else {
- defs = da[0];
- }
- if (style.kind === 1) {
- const node = tvgCreateSvgNode("linearGradient",
- {
- id: `TvgGradient${ctx.gradIndex + 1}`,
- x1: style.linear.point0.x,
- y1: style.linear.point0.y,
- x2: style.linear.point1.x,
- y2: style.linear.point1.y
- });
- node.setAttributeNS(null, "gradientUnits", "userSpaceOnUse");
- node.setAttributeNS(null, "spreadMethod", "pad");
- let col = tvgColorToSvgColorAndOpacity(ctx.colors[style.linear.color0]);
- const stop1 = tvgCreateSvgNode("stop", { offset: "0%", stopColor: col.color });//, stopOpacity: col.opacity});
- node.appendChild(stop1);
- col = tvgColorToSvgColorAndOpacity(ctx.colors[style.linear.color1]);
- const stop2 = tvgCreateSvgNode("stop", { offset: "100%", stopColor: col.color });//, stopOpacity: col.opacity});
- node.appendChild(stop2);
- defs.appendChild(node);
- ++ctx.gradIndex;
- return node.getAttributeNS(null, "id");
- } else if (style.kind === 2) {
- const r = tvgDistance(style.radial.point0, style.radial.point1);
- const node = tvgCreateSvgNode("radialGradient",
- {
- id: `TvgGradient${ctx.gradIndex + 1}`,
- cx: style.radial.point0.x,
- cy: style.radial.point0.y,
- fx: style.radial.point0.x,
- fy: style.radial.point0.y,
- r: r
- });
- node.setAttributeNS(null, "gradientUnits", "userSpaceOnUse");
- node.setAttributeNS(null, "spreadMethod", "pad");
- let col = tvgColorToSvgColorAndOpacity(ctx.colors[style.radial.color0]);
- const stop1 = tvgCreateSvgNode("stop", { offset: "0%", stopColor: col.color, stopOpacity: col.opacity });
- node.appendChild(stop1);
- col = tvgColorToSvgColorAndOpacity(ctx.colors[style.radial.color1]);
- const stop2 = tvgCreateSvgNode("stop", { offset: "100%", stopColor: col.color, stopOpacity: col.opacity });
- node.appendChild(stop2);
- defs.appendChild(node);
- ++ctx.gradIndex;
- return node.getAttributeNS(null, "id");
- } else if (style.kind === 0) throw "TinyVG: attempt to pass flat style to create gradient";
- else throw "TinyVG: attempt to pass an invalid style to create gradient";
- }
- const tvgApplyStyle = (ctx, style, isFill) => {
- if (style.kind === 0) { // flat
- const col = tvgColorToSvgColorAndOpacity(ctx.colors[style.flat]);
- if (isFill) {
- tvgAddSvgAttribute(ctx.elem, "fill", col.color);
- tvgAddSvgAttribute(ctx.elem, "fill-opacity", col.opacity);
- } else {
- tvgAddSvgAttribute(ctx.elem, "stroke", col.color);
- tvgAddSvgAttribute(ctx.elem, "stroke-opacity", col.opacity);
- }
- } else if (style.kind === 1 || style.kind === 2) { // linear
- const grad = tvgCreateSvgGradient(ctx, style);
- if (isFill) {
- tvgAddSvgAttribute(ctx.elem, "fill", `url(#${grad})`);
- } else {
- tvgAddSvgAttribute(ctx.elem, "stroke", `url(#${grad})`);
- }
- } else throw "TinyVG: attempt to apply invalid style";
- }
- const tvgParseFillRectangles = (ctx, size, fill_style) => {
- let count = size;
- if (count === 0) throw "TinyVG: Invalid zero length filled rectangles entry";
- let rect = tvgParseRect(ctx);
- let r = tvgCreateSvgNode("rect", rect);
- ctx.doc.appendChild(r);
- ctx.elem = r;
- tvgAddSvgAttribute(ctx.elem, "fill-rule", "evenodd");
- tvgApplyStyle(ctx, fill_style, true);
- const attrs = {};
- attrs.fillRule = "evenodd";
- if (fill_style.kind !== 0) {
- attrs.fill = r.getAttributeNS(null, "fill")
- } else {
- attrs.fill = r.getAttributeNS(null, "fill")
- attrs.fillOpacity = r.getAttributeNS(null, "fill-opacity");
- }
- --count;
- while (count--) {
- rect = tvgParseRect(ctx);
- const localAttrs = { ...attrs, ...rect };
- r = tvgCreateSvgNode("rect", localAttrs);
- ctx.doc.appendChild(r);
- ctx.elem = r;
- }
- }
- const tvgParseLineFillRectangles = (ctx, size, fill_style, line_style, line_width) => {
- let count = size;
- if (count === 0) throw "TinyVG: Invalid zero length line filled rectangles entry";
- if (line_width === 0) { // 0 width is invalid
- line_width = .001;
- }
- let rect = tvgParseRect(ctx);
- let r = tvgCreateSvgNode("rect", rect);
- ctx.doc.appendChild(r);
- ctx.elem = r;
- tvgAddSvgAttribute(ctx.elem, "fill-rule", "evenodd");
- tvgAddSvgAttribute(ctx.elem, "stroke-width", line_width);
- tvgApplyStyle(ctx, fill_style, true);
- tvgApplyStyle(ctx, line_style, false);
- const attrs = {};
- attrs.fillRule = "evenodd";
- if (fill_style.kind !== 0) {
- attrs.fill = r.getAttributeNS(null, "fill");
- } else {
- attrs.fill = r.getAttributeNS(null, "fill");
- attrs.fillOpacity = r.getAttributeNS(null, "fill-opacity");
- }
- if (line_style.kind !== 0) {
- attrs.stroke = r.getAttributeNS(null, "stroke");
- } else {
- attrs.stroke = r.getAttributeNS(null, "stroke");
- attrs.strokeOpacity = r.getAttributeNS(null, "stroke-opacity");
- }
- attrs.strokeWidth = line_width;
- --count;
- while (count--) {
- rect = tvgParseRect(ctx);
- const localAttrs = { ...attrs, ...rect };
- r = tvgCreateSvgNode("rect", localAttrs);
- ctx.doc.appendChild(r);
- ctx.elem = r;
- }
- }
- const tvgParseFillPaths = (ctx, size, style) => {
- if (size === 0) throw "TinyVG: Invalid zero filled paths entry";
- const attrs = {};
- attrs.fillRule = "evenodd";
- attrs.strokeOpacity = 0;
- attrs.strokeWidth = 0;
- const sizes = [];
- for (let i = 0; i < size; ++i) {
- sizes.push(tvgReadU32(ctx) + 1);
- }
- let p = tvgCreateSvgNode("path", attrs);
- ctx.doc.appendChild(p);
- ctx.elem = p;
- tvgApplyStyle(ctx, style, true);
- if (style.kind !== 0) {
- attrs.fill = p.getAttributeNS(null, "fill");
- } else {
- attrs.fill = p.getAttributeNS(null, "fill");
- attrs.fillOpacity = p.getAttributeNS(null, "fill-opacity");
- }
- let d = tvgParsePathD(ctx, sizes[0]);
- for (let i = 1; i < size; ++i) {
- d+= ` ${tvgParsePathD(ctx, sizes[i])}`;
- }
- tvgAddSvgAttribute(p, "d", d);
- }
- const tvgParseLinePaths = (ctx, size, line_style, line_width) => {
- if (size === 0) throw "TinyVG: Invalid zero line paths entry";
- if (line_width === 0) { // 0 width is invalid
- line_width = .001;
- }
- const attrs = {};
- const sizes = [];
- for (let i = 0; i < size; ++i) {
- sizes.push(tvgReadU32(ctx) + 1);
- }
- let p = tvgCreateSvgNode("path", attrs);
- ctx.doc.appendChild(p);
- ctx.elem = p;
- tvgAddSvgAttribute(ctx.elem, "fill-opacity", 0);
- tvgAddSvgAttribute(ctx.elem, "stroke-width", line_width);
- tvgApplyStyle(ctx, line_style, false);
- if (line_style.kind !== 0) {
- attrs.stroke = p.getAttributeNS(null, "stroke");
- } else {
- attrs.stroke = p.getAttributeNS(null, "stroke");
- attrs.strokeOpacity = p.getAttributeNS(null, "stroke-opacity");
- }
- attrs.strokeWidth = line_width;
- attrs.fillOpacity = 0;
- let d = tvgParsePathD(ctx, sizes[0]);
- for (let i = 1; i < size; ++i) {
- d+= ` ${tvgParsePathD(ctx, sizes[i])}`;
- }
- tvgAddSvgAttribute(p, "d", d);
- }
- const tvgParseLineFillPaths = (ctx, size, fill_style, line_style, line_width) => {
- if (size === 0) throw "TinyVG: Invalid zero line filled paths entry";
- if (line_width === 0) { // 0 width is invalid
- line_width = .001;
- }
- const attrs = {};
- attrs.fillRule = "evenodd";
- const sizes = [];
- for (let i = 0; i < size; ++i) {
- sizes.push(tvgReadU32(ctx) + 1);
- }
- let p = tvgCreateSvgNode("path", attrs);
- ctx.doc.appendChild(p);
- ctx.elem = p;
- tvgApplyStyle(ctx, fill_style, true);
- if (fill_style.kind !== 0) {
- attrs.fill = p.getAttributeNS(null, "fill");
- } else {
- attrs.fill = p.getAttributeNS(null, "fill");
- attrs.fillOpacity = p.getAttributeNS(null, "fill-opacity");
- }
- tvgApplyStyle(ctx, line_style, false);
- if (line_style.kind !== 0) {
- attrs.stroke = p.getAttributeNS(null, "stroke");
- } else {
- attrs.stroke = p.getAttributeNS(null, "stroke");
- attrs.strokeOpacity = p.getAttributeNS(null, "stroke-opacity");
- }
- attrs.strokeWidth = line_width;
- tvgAddSvgAttribute(p, "stroke-width", line_width);
- let d = tvgParsePathD(ctx, sizes[0]);
- for (let i = 1; i < size; ++i) {
- d+= ` ${tvgParsePathD(ctx, sizes[i])}`;
- }
- tvgAddSvgAttribute(p, "d", d);
- }
- const tvgParseFillPolygon = (ctx, size, fill_style) => {
- if (size === 0) throw "TinyVG: Invalid zero polygon entry";
- let count = size;
- let points = "";
- let pt = tvgReadPoint(ctx);
- points += `${pt.x},${pt.y}`;
- while (--count) {
- pt = tvgReadPoint(ctx);
- points += ` ${pt.x},${pt.y}`;
- }
- const attrs = { fillRule: "evenodd", points: points };
- let p = tvgCreateSvgNode("polygon", attrs);
- ctx.doc.appendChild(p);
- ctx.elem = p;
- tvgApplyStyle(ctx, fill_style, true);
- }
- const tvgParsePolyline = (ctx, size, line_style, line_width, close) => {
- if (size === 0) throw "TinyVG: Invalid zero polyline entry";
- if (line_width === 0) { // 0 width is invalid
- line_width = .001;
- }
- let count = size;
- let points = "";
- let pt = tvgReadPoint(ctx);
- points += `${pt.x},${pt.y}`;
- while (--count) {
- pt = tvgReadPoint(ctx);
- points += ` ${pt.x},${pt.y}`;
- }
- const attrs = { points: points, lineWidth: line_width, fillOpacity: 0 };
- let p = tvgCreateSvgNode(close ? "polygon" : "polyline", attrs);
- ctx.doc.appendChild(p);
- ctx.elem = p;
- tvgApplyStyle(ctx, line_style, false);
- }
- const tvgParseLineFillPolyline = (ctx, size, fill_style, line_style, line_width, close) => {
- if (size === 0) throw "TinyVG: Invalid zero line fill polyline entry";
- if (line_width === 0) { // 0 width is invalid
- line_width = .001;
- }
- let count = size;
- let points = "";
- let pt = tvgReadPoint(ctx);
- points += `${pt.x},${pt.y}`;
- while (--count) {
- pt = tvgReadPoint(ctx);
- points += ` ${pt.x},${pt.y}`;
- }
- const attrs = { points: points, lineWidth: line_width, fillRule: "evenodd" };
- let p = tvgCreateSvgNode(close ? "polygon" : "polyline", attrs);
- ctx.doc.appendChild(p);
- ctx.elem = p;
- tvgApplyStyle(ctx, fill_style, true);
- tvgApplyStyle(ctx, line_style, false);
- }
- const tvgParseLines = (ctx, size, line_style, line_width) => {
- if (size === 0) throw "TinyVG: Invalid zero lines entry";
- for (let i = 0; i < size; ++i) {
- const pt1 = tvgReadPoint(ctx);
- const pt2 = tvgReadPoint(ctx);
- const attrs = { x1: pt1.x, y1: pt1.y, x2: pt2.x, y2: pt2.y, strokeWidth: line_width };
- let l = tvgCreateSvgNode("line", attrs);
- ctx.doc.appendChild(l);
- ctx.elem = l;
- tvgApplyStyle(ctx, line_style, false);
- }
- }
- const tvgParseCommands = (ctx) => {
- let cmd = 255;
- while (cmd != 0) {
- cmd = ctx.view.getUint8(ctx.cursor++);
- switch (cmd & 0x3F) {
- case 0: // TVG_CMD_END_DOCUMENT:
- // console.log("TVG END");
- break;
- case 1: { // TVG_CMD_FILL_POLYGON:
- // console.log("TVG FILL POLYGON");
- const data = tvgParseFillHeader(ctx, (cmd >>> 6) & 3);
- tvgParseFillPolygon(ctx, data.size, data.style);
- } break;
- case 2: { // TVG_CMD_FILL_RECTANGLES:
- // console.log("TVG FILL RECTANGLES");
- const data = tvgParseFillHeader(ctx, (cmd >>> 6) & 3);
- tvgParseFillRectangles(ctx, data.size, data.style);
- } break;
- case 3: { // TVG_CMD_FILL_PATH:
- // console.log("TVG FILL PATH");
- const data = tvgParseFillHeader(ctx, (cmd >>> 6) & 3);
- tvgParseFillPaths(ctx, data.size, data.style);
- } break;
- case 4: { // TVG_CMD_DRAW_LINES:
- // console.log("TVG LINES");
- const data = tvgParseLineHeader(ctx, (cmd >>> 6) & 3);
- tvgParseLines(ctx, data.size, data.style, data.line_width);
- } break;
- case 5: { // TVG_CMD_DRAW_LINE_LOOP:
- // console.log("TVG LINE LOOP");
- const data = tvgParseLineHeader(ctx, (cmd >>> 6) & 3);
- tvgParsePolyline(ctx, data.size, data.style, data.line_width, true);
- } break;
- case 6: { // TVG_CMD_DRAW_LINE_STRIP:
- // console.log("TVG LINE STRIP");
- const data = tvgParseLineHeader(ctx, (cmd >>> 6) & 3);
- tvgParsePolyline(ctx, data.size, data.style, data.line_width, false);
- } break;
- case 7: { // TVG_CMD_DRAW_LINE_PATH:
- // console.log("TVG LINE PATH");
- const data = tvgParseLineHeader(ctx, (cmd >>> 6) & 3);
- tvgParseLinePaths(ctx, data.size, data.style, data.line_width);
- } break;
- case 8: { // TVG_CMD_OUTLINE_FILL_POLYGON:
- // console.log("TVG OUTLINE FILL POLYGON");
- const data = tvgParseLineFillHeader(ctx, (cmd >>> 6) & 3);
- tvgParseLineFillPolyline(ctx, data.size, data.fill_style, data.line_style, data.line_width, true);
- } break;
- case 9: { // TVG_CMD_OUTLINE_FILL_RECTANGLES:
- // console.log("TVG OUTLINE FILL RECTANGLES");
- const data = tvgParseLineFillHeader(ctx, (cmd >>> 6) & 3);
- tvgParseLineFillRectangles(ctx, data.size, data.fill_style, data.line_style, data.line_width);
- } break;
- case 10: { // TVG_CMD_OUTLINE_FILL_PATH:
- // console.log("TVG OUTLINE FILL PATH");
- const data = tvgParseLineFillHeader(ctx, (cmd >>> 6) & 3);
- tvgParseLineFillPaths(ctx, data.size, data.fill_style, data.line_style, data.line_width);
- } break;
- default:
- throw `TinyVG: Invalid command in document (0x${tvgToHex(cmd)})`;
- }
- }
- }
- // get the {width, height} of a TVG in an arraybuffer
- export const tvgDimensions = (data) => {
- if (data) {
- const view = new DataView(data);
- if (view.byteLength > 5) {
- // check for TVG v 1.0 header
- if (view.getUint8(0) == 0x72 && view.getUint8(1) == 0x56 && view.getUint8(2) == 1) {
- const flags = view.getUint8(3);
- const range = (flags >>> 6) & 0x03;
- const w = tvgReadCoordBI(range, 4, data);
- const h = tvgReadCoordBI(range, 4 + tvgAdvCoord(range), data);
- const dim = {
- width: tvgMapZeroToMax(range, w),
- height: tvgMapZeroToMax(range, h)
- };
- return dim;
- }
- }
- }
- return undefined;
- }
- // Render a TVG in an arraybuffer (data) to an SVG tag indicated by the id
- export const tvgRender = (id, data) => {
- if (!id) throw "TinyVG: Must specify the id of an SVG element";
- if (!data) throw "TinyVG: Must provide an ArrayBuffer with TVG data";
- const view = new DataView(data);
- if (view.byteLength > 5) {
- if (view.getUint8(0) == 0x72 && view.getUint8(1) == 0x56 && view.getUint8(2) == 1) {
- const ctx = tvgInit(data, id);
- if (ctx.doc) {
- const flags = view.getUint8(3);
- ctx.scale = (flags & 0xF);
- ctx.color_encoding = ((flags >>> 4) & 0x3);
- ctx.coord_range = (flags >>> 6) & 0x03;
- ctx.cursor = 4;
- const w = tvgReadCoord(ctx);
- const h = tvgReadCoord(ctx);
- ctx.width = tvgMapZeroToMax(ctx, w);
- ctx.height = tvgMapZeroToMax(ctx, h);
- const colcount = tvgReadU32(ctx);
- if (!colcount || colcount === 0) throw "TinyVG: invalid format - color table contains nothing";
- for (let i = 0; i < colcount; ++i) {
- ctx.colors.push(tvgReadColor(ctx));
- }
- while (ctx.doc.firstChild) {
- ctx.doc.removeChild(ctx.doc.lastChild);
- }
- tvgAddSvgAttribute(ctx.doc, "width", w.toString(10));
- tvgAddSvgAttribute(ctx.doc, "height", h.toString(10));
- tvgAddSvgAttribute(ctx.doc, "viewBox", `0 0 ${w} ${h}`);
- tvgParseCommands(ctx);
- return;
- }
- }
- }
- throw "TinyVG: Not a valid TinyVG file";
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement