1
0
mirror of https://github.com/sehugg/8bitworkshop.git synced 2024-06-03 04:29:33 +00:00

pixel editor lazy updates

This commit is contained in:
Steven Hugg 2019-03-26 14:29:47 -04:00
parent 2889ef33bd
commit 6cea0772bf
5 changed files with 121 additions and 37 deletions

View File

@ -502,4 +502,11 @@ div.asset_editor {
padding:16px; padding:16px;
margin:16px; margin:16px;
background-color:#999; background-color:#999;
display:flex;
flex-direction:column;
align-items:center;
}
div.asset_toolbar {
padding:8px;
margin:8px;
} }

View File

@ -108,6 +108,7 @@ TODO:
- metasprites - metasprites
- update nested data, palette/tile refs properly - update nested data, palette/tile refs properly
- throw errors when bad/no refs - throw errors when bad/no refs
- careful with mouse capture out of frame
WEB WORKER FORMAT WEB WORKER FORMAT

View File

@ -317,6 +317,33 @@ var PREDEF_LAYOUTS : {[id:string]:PixelEditorPaletteLayout} = {
///// /////
function equalArrays(a:UintArray, b:UintArray) : boolean {
if (a == null || b == null)
return false;
if (a.length !== b.length)
return false;
if (a === b)
return true;
for (var i=0; i<a.length; i++) {
if (a[i] !== b[i])
return false;
}
return true;
}
function equalNestedArrays(a:UintArray[], b:UintArray[]) : boolean {
if (a == null || b == null)
return false;
if (a.length !== b.length)
return false;
if (a === b)
return true;
for (var i=0; i<a.length; i++) {
if (!equalArrays(a[i], b[i]))
return false;
}
return true;
}
export abstract class PixNode { export abstract class PixNode {
left : PixNode; // toward text editor left : PixNode; // toward text editor
right : PixNode; // toward pixel editor right : PixNode; // toward pixel editor
@ -326,8 +353,8 @@ export abstract class PixNode {
rgbimgs? : Uint32Array[]; // array of rgba imgages rgbimgs? : Uint32Array[]; // array of rgba imgages
palette? : Uint32Array; // array of rgba palette? : Uint32Array; // array of rgba
abstract updateLeft(); // update coming from right abstract updateLeft() : boolean; // update coming from right
abstract updateRight(); // update coming from left abstract updateRight() : boolean; // update coming from left
refreshLeft() { refreshLeft() {
var p : PixNode = this; var p : PixNode = this;
@ -364,20 +391,25 @@ abstract class CodeProjectDataNode extends PixNode {
export class FileDataNode extends CodeProjectDataNode { export class FileDataNode extends CodeProjectDataNode {
constructor(project:ProjectWindows, fileid:string, data:Uint8Array) { constructor(project:ProjectWindows, fileid:string) {
super(); super();
this.project = project; this.project = project;
this.fileid = fileid; this.fileid = fileid;
this.label = fileid; this.label = fileid;
this.words = data;
} }
updateLeft() { updateLeft() {
//if (equalArrays(this.words, this.right.words)) return false;
this.words = this.right.words; this.words = this.right.words;
if (this.project) { if (this.project) {
this.project.updateFile(this.fileid, this.words as Uint8Array); this.project.updateFile(this.fileid, this.words as Uint8Array);
} }
return true;
} }
updateRight() { updateRight() {
if (this.project) {
this.words = this.project.project.getFile(this.fileid) as Uint8Array;
}
return true;
} }
} }
@ -386,12 +418,12 @@ export class TextDataNode extends CodeProjectDataNode {
start : number; start : number;
end : number; end : number;
constructor(project:ProjectWindows, fileid:string, label:string, text:string, start:number, end:number) { // TODO: what if file size/layout changes?
constructor(project:ProjectWindows, fileid:string, label:string, start:number, end:number) {
super(); super();
this.project = project; this.project = project;
this.fileid = fileid; this.fileid = fileid;
this.label = label; this.label = label;
this.text = text;
this.start = start; this.start = start;
this.end = end; this.end = end;
} }
@ -407,12 +439,17 @@ export class TextDataNode extends CodeProjectDataNode {
this.project.updateFile(this.fileid, this.text); this.project.updateFile(this.fileid, this.text);
//this.project.replaceTextRange(this.fileid, this.start, this.end, datastr); //this.project.replaceTextRange(this.fileid, this.start, this.end, datastr);
} }
return true;
} }
updateRight() { updateRight() {
if (this.project) {
this.text = this.project.project.getFile(this.fileid) as string;
}
var datastr = this.text.substring(this.start, this.end); var datastr = this.text.substring(this.start, this.end);
datastr = convertToHexStatements(datastr); // TODO? datastr = convertToHexStatements(datastr); // TODO?
var words = parseHexWords(datastr); var words = parseHexWords(datastr);
this.words = words; //new Uint8Array(words); // TODO: 16/32? this.words = words; //new Uint8Array(words); // TODO: 16/32?
return true;
} }
} }
@ -422,9 +459,11 @@ export class Compressor extends PixNode {
updateLeft() { updateLeft() {
// TODO: can't modify length of rle bytes // TODO: can't modify length of rle bytes
return false;
} }
updateRight() { updateRight() {
this.words = rle_unpack(new Uint8Array(this.left.words)); this.words = rle_unpack(new Uint8Array(this.left.words));
return true;
} }
} }
@ -440,13 +479,17 @@ export class Mapper extends PixNode {
this.fmt = fmt; this.fmt = fmt;
} }
updateLeft() { updateLeft() {
//if (equalNestedArrays(this.images, this.right.images)) return false;
this.images = this.right.images; this.images = this.right.images;
this.words = convertImagesToWords(this.images, this.fmt); this.words = convertImagesToWords(this.images, this.fmt);
return true;
} }
updateRight() { updateRight() {
if (equalArrays(this.words, this.left.words)) return false;
// convert each word array to images // convert each word array to images
this.words = this.left.words; this.words = this.left.words;
this.images = convertWordsToImages(this.words, this.fmt); this.images = convertWordsToImages(this.words, this.fmt);
return true;
} }
} }
@ -481,18 +524,21 @@ export class Palettizer extends PixNode {
updateLeft() { updateLeft() {
this.rgbimgs = this.right.rgbimgs; this.rgbimgs = this.right.rgbimgs;
var pal = new RGBAPalette(this.palette); var pal = new RGBAPalette(this.palette);
this.images = this.rgbimgs.map( (im:Uint32Array) => { var newimages = this.rgbimgs.map( (im:Uint32Array) => {
var out = new Uint8Array(im.length); var out = new Uint8Array(im.length);
for (var i=0; i<im.length; i++) { for (var i=0; i<im.length; i++) {
out[i] = pal.indexOf(im[i]); out[i] = pal.indexOf(im[i]);
} }
return out; return out;
}); });
// have to do it this way b/c pixel editor modifies arrays
//if (equalNestedArrays(newimages, this.images)) return false;
this.images = newimages;
return true;
} }
updateRight() { updateRight() {
this.updateRefs(); if (!this.updateRefs() && equalNestedArrays(this.images, this.left.images)) return false;
this.images = this.left.images; this.images = this.left.images;
if (!this.palette || !this.images) return;
var mask = this.palette.length - 1; // must be power of 2 var mask = this.palette.length - 1; // must be power of 2
// for each image, map bytes to RGB colors // for each image, map bytes to RGB colors
this.rgbimgs = this.images.map( (im:Uint8Array) => { this.rgbimgs = this.images.map( (im:Uint8Array) => {
@ -502,20 +548,25 @@ export class Palettizer extends PixNode {
} }
return out; return out;
}); });
return true;
} }
updateRefs() { updateRefs() {
var newpalette;
if (this.context != null) { if (this.context != null) {
this.paloptions = this.context.getPalettes(this.ncolors); this.paloptions = this.context.getPalettes(this.ncolors);
if (this.paloptions && this.paloptions.length > 0) { if (this.paloptions && this.paloptions.length > 0) {
this.palette = this.paloptions[this.palindex].palette; newpalette = this.paloptions[this.palindex].palette;
} }
} }
if (this.palette == null) { if (newpalette == null) {
if (this.ncolors <= 2) if (this.ncolors <= 2)
this.palette = new Uint32Array([0xff000000, 0xffffffff]); newpalette = new Uint32Array([0xff000000, 0xffffffff]);
else else
this.palette = new Uint32Array([0xff000000, 0xffff00ff, 0xffffff00, 0xffffffff]); // TODO: more palettes newpalette = new Uint32Array([0xff000000, 0xffff00ff, 0xffffff00, 0xffffffff]); // TODO: more palettes
} }
if (equalArrays(this.palette, newpalette)) return false;
this.palette = newpalette;
return true;
} }
} }
@ -533,9 +584,9 @@ function dedupPalette(cols : UintArray) : Uint32Array {
} }
return res; return res;
} }
export class PaletteFormatToRGB extends PixNode { export class PaletteFormatToRGB extends PixNode {
words : UintArray; words : UintArray;
rgbimgs : Uint32Array[]; rgbimgs : Uint32Array[];
palette : Uint32Array; palette : Uint32Array;
@ -548,8 +599,10 @@ export class PaletteFormatToRGB extends PixNode {
} }
updateLeft() { updateLeft() {
//TODO //TODO
return true;
} }
updateRight() { updateRight() {
if (equalArrays(this.words, this.left.words)) return false;
this.words = this.left.words; this.words = this.left.words;
this.palette = dedupPalette(convertPaletteFormat(this.words, this.palfmt)); this.palette = dedupPalette(convertPaletteFormat(this.words, this.palfmt));
this.layout = PREDEF_LAYOUTS[this.palfmt.layout]; this.layout = PREDEF_LAYOUTS[this.palfmt.layout];
@ -557,6 +610,7 @@ export class PaletteFormatToRGB extends PixNode {
this.palette.forEach( (rgba:number) => { this.palette.forEach( (rgba:number) => {
this.rgbimgs.push(new Uint32Array([rgba])); this.rgbimgs.push(new Uint32Array([rgba]));
}); });
return true;
} }
getAllColors() { getAllColors() {
var arr = []; var arr = [];
@ -581,13 +635,15 @@ export abstract class Compositor extends PixNode {
super(); super();
this.context = context; this.context = context;
} }
updateRefs() { updateRefs() : boolean {
var oldtilemap = this.tilemap;
if (this.context != null) { if (this.context != null) {
this.tileoptions = this.context.getTilemaps(256); this.tileoptions = this.context.getTilemaps(256);
if (this.tileoptions && this.tileoptions.length > 0) { if (this.tileoptions && this.tileoptions.length > 0) {
this.tilemap = this.tileoptions[this.tileindex].images; this.tilemap = this.tileoptions[this.tileindex].images;
} }
} }
return !equalNestedArrays(oldtilemap, this.tilemap);
} }
} }
@ -605,6 +661,7 @@ export class MetaspriteCompositor extends Compositor {
} }
updateLeft() { updateLeft() {
// TODO // TODO
return false;
} }
updateRight() { updateRight() {
this.updateRefs(); this.updateRefs();
@ -615,6 +672,7 @@ export class MetaspriteCompositor extends Compositor {
this.metadefs.forEach((meta) => { this.metadefs.forEach((meta) => {
// TODO // TODO
}); });
return true;
} }
} }
@ -628,11 +686,11 @@ export class NESNametableConverter extends Compositor {
} }
updateLeft() { updateLeft() {
// TODO // TODO
return false;
} }
updateRight() { updateRight() {
if (!this.updateRefs() && equalArrays(this.words, this.left.words)) return false;
this.words = this.left.words; this.words = this.left.words;
this.updateRefs();
if (!this.words || !this.tilemap) return;
this.cols = 32; this.cols = 32;
this.rows = 30; this.rows = 30;
this.width = this.cols * 8; this.width = this.cols * 8;
@ -665,6 +723,7 @@ export class NESNametableConverter extends Compositor {
} }
} }
// TODO // TODO
return true;
} }
} }
@ -682,7 +741,6 @@ export class ImageChooser {
var cscale = Math.max(2, Math.ceil(16/this.width)); // TODO var cscale = Math.max(2, Math.ceil(16/this.width)); // TODO
var imgsperline = this.width <= 8 ? 16 : 8; // TODO var imgsperline = this.width <= 8 ? 16 : 8; // TODO
var span = null; var span = null;
if (!this.rgbimgs) return;
this.rgbimgs.forEach((imdata, i) => { this.rgbimgs.forEach((imdata, i) => {
var viewer = new Viewer(); var viewer = new Viewer();
viewer.width = this.width; viewer.width = this.width;
@ -730,9 +788,11 @@ export class CharmapEditor extends PixNode {
} }
updateLeft() { updateLeft() {
return true;
} }
updateRight() { updateRight() {
if (equalNestedArrays(this.rgbimgs, this.left.rgbimgs)) return false;
this.rgbimgs = this.left.rgbimgs; this.rgbimgs = this.left.rgbimgs;
var adual = newDiv(this.parentdiv.empty(), "asset_dual"); // contains grid and editor var adual = newDiv(this.parentdiv.empty(), "asset_dual"); // contains grid and editor
var agrid = newDiv(adual); var agrid = newDiv(adual);
@ -765,6 +825,7 @@ export class CharmapEditor extends PixNode {
palizer.refreshRight(); palizer.refreshRight();
}); });
} }
return true;
} }
createEditor(aeditor : JQuery, viewer : Viewer, escale : number) : PixEditor { createEditor(aeditor : JQuery, viewer : Viewer, escale : number) : PixEditor {
@ -901,7 +962,7 @@ class PixEditor extends Viewer {
createPaletteButtons() { createPaletteButtons() {
this.palbtns = []; this.palbtns = [];
var span = $(document.createElement('div')); var span = newDiv(null, "asset_toolbar");
for (var i=0; i<this.palette.length; i++) { for (var i=0; i<this.palette.length; i++) {
var btn = $(document.createElement('button')).addClass('palbtn'); var btn = $(document.createElement('button')).addClass('palbtn');
var rgb = this.palette[i] & 0xffffff; var rgb = this.palette[i] & 0xffffff;

View File

@ -974,8 +974,9 @@ export class AssetEditorView implements ProjectView, pixed.EditorContext {
} }
if (div) { if (div) {
this.cureditordiv = div; this.cureditordiv = div;
this.cureditordiv.show(timeout); this.cureditordiv.show();
this.cureditordiv[0].scrollIntoView({behavior: "smooth", block: "center"}); this.cureditordiv[0].scrollIntoView({behavior: "smooth", block: "center"});
//setTimeout(() => { this.cureditordiv[0].scrollIntoView({behavior: "smooth", block: "center"}) }, timeout);
} }
} }
if (this.cureditelem) { if (this.cureditelem) {
@ -1039,37 +1040,42 @@ export class AssetEditorView implements ProjectView, pixed.EditorContext {
return result; return result;
} }
addPaletteEditorViews(parentdiv:JQuery, words, palette, layout, allcolors, callback) { addPaletteEditorViews(parentdiv:JQuery, pal2rgb:pixed.PaletteFormatToRGB, callback) {
var adual = $('<div class="asset_dual"/>').appendTo(parentdiv); var adual = $('<div class="asset_dual"/>').appendTo(parentdiv);
var aeditor = $('<div class="asset_editor"/>').hide(); // contains editor, when selected var aeditor = $('<div class="asset_editor"/>').hide(); // contains editor, when selected
// TODO: they need to update when refreshed from right // TODO: they need to update when refreshed from right
var allrgbimgs = []; var allrgbimgs = [];
allcolors.forEach((rgba) => { allrgbimgs.push(new Uint32Array([rgba])); }); // array of array of 1 rgb color (for picker) pal2rgb.getAllColors().forEach((rgba) => { allrgbimgs.push(new Uint32Array([rgba])); }); // array of array of 1 rgb color (for picker)
var atable = $('<table/>').appendTo(adual); var atable = $('<table/>').appendTo(adual);
aeditor.appendTo(adual); aeditor.appendTo(adual);
// make default layout if not exists // make default layout if not exists
var layout = pal2rgb.layout;
if (!layout) { if (!layout) {
var len = palette.length; var len = pal2rgb.palette.length;
var imgsperline = len > 32 ? 8 : 4; // TODO: use 'n'? var imgsperline = len > 32 ? 8 : 4; // TODO: use 'n'?
layout = []; layout = [];
for (var i=0; i<len; i+=imgsperline) { for (var i=0; i<len; i+=imgsperline) {
layout.push(["", i, Math.min(len-i,imgsperline)]); layout.push(["", i, Math.min(len-i,imgsperline)]);
} }
} }
function updateCell(cell, j) {
var val = pal2rgb.words[j];
var rgb = pal2rgb.palette[j];
var hexcol = '#'+hex(rgb2bgr(rgb),6);
var textcol = (rgb & 0x008000) ? 'black' : 'white';
cell.text(hex(val,2)).css('background-color',hexcol).css('color',textcol);
}
// iterate over each row of the layout // iterate over each row of the layout
layout.forEach( ([name, start, len]) => { layout.forEach( ([name, start, len]) => {
if (start < palette.length) { // skip row if out of range if (start < pal2rgb.palette.length) { // skip row if out of range
var arow = $('<tr/>').appendTo(atable); var arow = $('<tr/>').appendTo(atable);
$('<td/>').text(name).appendTo(arow); $('<td/>').text(name).appendTo(arow);
var inds = []; var inds = [];
for (var k=start; k<start+len; k++) for (var k=start; k<start+len; k++)
inds.push(k); inds.push(k);
inds.forEach( (i) => { inds.forEach( (i) => {
var val = words[i]; var cell = $('<td/>').addClass('asset_cell asset_editable').appendTo(arow);
var rgb = palette[i]; updateCell(cell, i);
var hexcol = '#'+hex(rgb2bgr(rgb),6);
var textcol = (rgb & 0x008000) ? 'black' : 'white';
var cell = $('<td/>').addClass('asset_cell asset_editable').text(hex(val,2)).css('background-color',hexcol).css('color',textcol).appendTo(arow);
cell.click((e) => { cell.click((e) => {
var chooser = new pixed.ImageChooser(); var chooser = new pixed.ImageChooser();
chooser.rgbimgs = allrgbimgs; chooser.rgbimgs = allrgbimgs;
@ -1077,6 +1083,7 @@ export class AssetEditorView implements ProjectView, pixed.EditorContext {
chooser.height = 1; chooser.height = 1;
chooser.recreate(aeditor, (index, newvalue) => { chooser.recreate(aeditor, (index, newvalue) => {
callback(i, index); callback(i, index);
updateCell(cell, i);
}); });
this.setCurrentEditor(aeditor, cell); this.setCurrentEditor(aeditor, cell);
}); });
@ -1106,13 +1113,14 @@ export class AssetEditorView implements ProjectView, pixed.EditorContext {
firstnode.refreshRight(); firstnode.refreshRight();
// TODO: add view objects // TODO: add view objects
// TODO: show which one is selected? // TODO: show which one is selected?
this.addPaletteEditorViews(parentdiv, firstnode.words, this.addPaletteEditorViews(parentdiv, pal2rgb,
pal2rgb.palette, pal2rgb.layout, pal2rgb.getAllColors(),
(index, newvalue) => { (index, newvalue) => {
console.log('set entry', index, '=', newvalue); console.log('set entry', index, '=', newvalue);
// TODO: this forces update of palette rgb colors and file data
firstnode.words[index] = newvalue; firstnode.words[index] = newvalue;
//firstnode.refreshRight(); pal2rgb.words = null;
firstnode.refreshLeft(); pal2rgb.updateRight();
pal2rgb.refreshLeft();
}); });
} }
@ -1124,16 +1132,17 @@ export class AssetEditorView implements ProjectView, pixed.EditorContext {
// TODO: only refresh when needed // TODO: only refresh when needed
if (fileid.endsWith('.chr') && data instanceof Uint8Array) { if (fileid.endsWith('.chr') && data instanceof Uint8Array) {
// is this a NES CHR? // is this a NES CHR?
let node = new pixed.FileDataNode(projectWindows, fileid, data); let node = new pixed.FileDataNode(projectWindows, fileid);
const neschrfmt = {w:8,h:8,bpp:1,count:(data.length>>4),brev:true,np:2,pofs:8,remap:[0,1,2,4,5,6,7,8,9,10,11,12]}; // TODO const neschrfmt = {w:8,h:8,bpp:1,count:(data.length>>4),brev:true,np:2,pofs:8,remap:[0,1,2,4,5,6,7,8,9,10,11,12]}; // TODO
this.addPixelEditor(filediv, node, neschrfmt); this.addPixelEditor(filediv, node, neschrfmt);
this.registerAsset("charmap", node, true); this.registerAsset("charmap", node, true);
nassets++;
} else if (typeof data === 'string') { } else if (typeof data === 'string') {
let textfrags = this.scanFileTextForAssets(fileid, data); let textfrags = this.scanFileTextForAssets(fileid, data);
for (let frag of textfrags) { for (let frag of textfrags) {
if (frag.fmt) { if (frag.fmt) {
let label = fileid; // TODO: label let label = fileid; // TODO: label
let node : pixed.PixNode = new pixed.TextDataNode(projectWindows, fileid, label, data, frag.start, frag.end); let node : pixed.PixNode = new pixed.TextDataNode(projectWindows, fileid, label, frag.start, frag.end);
let first = node; let first = node;
// rle-compressed? // rle-compressed?
if (frag.fmt.comp == 'rletag') { if (frag.fmt.comp == 'rletag') {
@ -1194,6 +1203,10 @@ export class AssetEditorView implements ProjectView, pixed.EditorContext {
console.log("Found " + this.rootnodes.length + " assets"); console.log("Found " + this.rootnodes.length + " assets");
this.deferrednodes.forEach((node) => { node.refreshRight(); }); this.deferrednodes.forEach((node) => { node.refreshRight(); });
this.deferrednodes = []; this.deferrednodes = [];
} else {
for (var node of this.rootnodes) {
node.refreshRight();
}
} }
} }

View File

@ -13,13 +13,15 @@ describe('Pixel editor', function() {
var palfmt = {pal:332,n:16}; var palfmt = {pal:332,n:16};
var paldatastr = " 0x00, 0x03, 0x19, 0x50, 0x52, 0x07, 0x1f, 0x37, 0xe0, 0xa4, 0xfd, 0xff, 0x38, 0x70, 0x7f, 0xf8, "; var paldatastr = " 0x00, 0x03, 0x19, 0x50, 0x52, 0x07, 0x1f, 0x37, 0xe0, 0xa4, 0xfd, 0xff, 0x38, 0x70, 0x7f, 0xf8, ";
var node4 = new pixed.TextDataNode(null, null, null, paldatastr, 0, paldatastr.length); var node4 = new pixed.TextDataNode(null, null, null, 0, paldatastr.length);
node4.text = paldatastr;
var node5 = new pixed.PaletteFormatToRGB(palfmt); var node5 = new pixed.PaletteFormatToRGB(palfmt);
node4.addRight(node5); node4.addRight(node5);
node4.refreshRight(); node4.refreshRight();
var datastr = "1,2, 0x00,0x00,0xef,0xef,0xe0,0x00,0x00, 0x00,0xee,0xee,0xfe,0xee,0xe0,0x00, 0x0e,0xed,0xef,0xef,0xed,0xee,0x00, 0x0e,0xee,0xdd,0xdd,0xde,0xee,0x00, 0x0e,0xee,0xed,0xde,0xee,0xee,0x00, 0x00,0xee,0xee,0xde,0xee,0xe0,0x00, 0x00,0xee,0xee,0xde,0xee,0xe0,0x00, 0x00,0x00,0xed,0xdd,0xe0,0x00,0x0d, 0xdd,0xdd,0xee,0xee,0xed,0xdd,0xd0, 0x0d,0xee,0xee,0xee,0xee,0xee,0x00, 0x0e,0xe0,0xee,0xee,0xe0,0xee,0x00, 0x0e,0xe0,0xee,0xee,0xe0,0xee,0x00, 0x0e,0xe0,0xdd,0xdd,0xd0,0xde,0x00, 0x0d,0x00,0xee,0x0e,0xe0,0x0d,0x00, 0x00,0x00,0xed,0x0e,0xe0,0x00,0x00, 0x00,0x0d,0xdd,0x0d,0xdd,0x00,0x18,"; var datastr = "1,2, 0x00,0x00,0xef,0xef,0xe0,0x00,0x00, 0x00,0xee,0xee,0xfe,0xee,0xe0,0x00, 0x0e,0xed,0xef,0xef,0xed,0xee,0x00, 0x0e,0xee,0xdd,0xdd,0xde,0xee,0x00, 0x0e,0xee,0xed,0xde,0xee,0xee,0x00, 0x00,0xee,0xee,0xde,0xee,0xe0,0x00, 0x00,0xee,0xee,0xde,0xee,0xe0,0x00, 0x00,0x00,0xed,0xdd,0xe0,0x00,0x0d, 0xdd,0xdd,0xee,0xee,0xed,0xdd,0xd0, 0x0d,0xee,0xee,0xee,0xee,0xee,0x00, 0x0e,0xe0,0xee,0xee,0xe0,0xee,0x00, 0x0e,0xe0,0xee,0xee,0xe0,0xee,0x00, 0x0e,0xe0,0xdd,0xdd,0xd0,0xde,0x00, 0x0d,0x00,0xee,0x0e,0xe0,0x0d,0x00, 0x00,0x00,0xed,0x0e,0xe0,0x00,0x00, 0x00,0x0d,0xdd,0x0d,0xdd,0x00,0x18,";
var node1 = new pixed.TextDataNode(null, null, null, datastr, 0, datastr.length); var node1 = new pixed.TextDataNode(null, null, null, 0, datastr.length);
node1.text = datastr;
var node2 = new pixed.Mapper(fmt); var node2 = new pixed.Mapper(fmt);
node1.addRight(node2); node1.addRight(node2);
var node3 = new pixed.Palettizer(null, fmt); var node3 = new pixed.Palettizer(null, fmt);