view multipaint.pde @ 92:4161d798cb64

Clean up/refactor image (jpeg/png) importing a bit and rename function.
author Matti Hamalainen <ccr@tnsp.org>
date Fri, 06 Jul 2018 00:43:19 +0300
parents dffafe05d520
children aadcd5f7e9dc
line wrap: on
line source

//
// Multipaint Metal Edition 22.5.2017
// Tero Heikkinen (Dr. TerrorZ)
// ProcessingJS port and changes by Matti Hämäläinen (ccr/TNSP)
// processing setup draw and file things here
//

PrintWriter exwriter;
final int C64 = 0, MSX = 5, SPECTRUM = 6, C64M = 10, PLUS4 = 9, PLUS4M = 19, CPC = 2; // supported
final int TIMEX = 7, JR200 = 8, AMIGA = 11; // experimental or incomplete
final int C64FLI = 20, C64FLIM = 21; // experimental
final int PICO8 = 88;
final int FAKEX = 32; // Commodore 64 without attribute limitations
final int UNIA = 33;
final int PEESEE = 0, MACOSX = 1, PSJS = 2;

//-1 & -1 for normal operation
int machine = -1; // change this to autoselect
int platform = PSJS;

final int COMMAND = 157; // Marq's cmd/ctrl solution

int g_active, g_tooltrigger, g_prevtool;
int g_rband, g_rbang, g_boxreconstruct, g_sorsaa, g_charlimit;
int g_animx, g_animy, g_animframes, g_animno, g_animtime, g_animspeed;
int g_omag, g_bordh, g_bordv, g_keymacpos, g_dirty;

boolean fileselect = false, saveas = false, refselect = false,
    g_control = false, g_shift = false, exportsrc = false,
    importfmt = false, exportemu = false, exportfmt = false;

String path = "", refpath = "", filename = "", sfilename = "",
    elename = "", refname = "",
    g_exportext = "", g_exportname = "", g_keymacro = "";


int[] g_r = new int[266];
int[] g_g = new int[266];
int[] g_b = new int[266];
int[] g_rgb = new int[266];

byte[] g_map = new byte[88000];
byte[] g_fillmap = new byte[88000];
byte[] g_sparepage = new byte[88000];
byte[] g_swappage = new byte[88000];
byte[] g_rmap = new byte[88000];
byte[] g_icons = new byte[88000];
byte[] g_brush = new byte[88000];
byte[][] g_undob = new byte[11][88000];
byte[][] g_undobs = new byte[11][88000];
byte[] g_template = new byte[16384];
byte[] g_redo = new byte[1024];
byte[] g_remdo = new byte[1024];
int[] g_chaup = new int[64];
byte[] g_data = new byte[1024];
int[] g_uindex = new int[8];
int[] g_ubottom = new int[8];
int[] g_utop = new int[8];
int[] g_magpix = new int[64];
int[] g_magpiy = new int[64];


byte g_realfront, g_realback;


//dimensions
int X, Y, MX, MY;
//generic mouse
int g_ofx, g_ofy, g_button, g_realbutton, g_klikkeri, g_mx, g_my, g_orx, g_ory, g_msx, g_msy;
//rubberband,mouse
int g_phase, g_rx, g_ry, g_rx2, g_ry2, g_rubbermode;
int g_prex, g_prey, g_storedcoordx, g_storedcoordy;
//window positioning
int g_windowx, g_windowy, g_maglocx, g_maglocy;
int g_hedge, g_vedge, g_uizoom, g_wzoom;
//icons & gui stuff
int g_iconx, g_icony, g_piconx, g_picony, g_iconmode;
int g_gridx, g_gridy, g_spare;
int g_farge, g_backg, g_ofarge, g_repanel, g_msgctr;
//machine related color properties
int g_attrimode, g_britemode, g_multic, g_backmode, g_maxcolors, g_hzoomer;
float g_palsteps;
//brush, tool parameters
int g_bsourcex, g_bsourcey, g_bsourcex2, g_bsourcey2;
int g_bsize, g_btype;


void setup()
{
    g_magpix[0] = 3;
    g_magpix[1] = 8;
    g_magpix[2] = 16;
    g_magpix[10] = 2;
    g_magpix[11] = 6;
    g_magpix[12] = 16;
    g_magpix[20] = 4;
    g_magpix[21] = 16;
    g_magpix[22] = 32;

    g_magpiy[0] = 3;
    g_magpiy[1] = 8;
    g_magpiy[2] = 16;
    g_magpiy[10] = 2;
    g_magpiy[11] = 6;
    g_magpiy[12] = 16;
    g_magpiy[20] = 4;
    g_magpiy[21] = 16;
    g_magpiy[22] = 32;

    g_omag = 1;
    g_bordh = 64;
    g_bordv = 32;

    // Set up UI
    g_uizoom = 2;
    g_animspeed = 1;

    // Get machine from Javascript runner, or default to C64
    if (mpMachine)
        machine = mpMachine;
    else
        machine = C64;

    switch (g_uizoom) {
        case 1:
            g_wzoom = 2;
            size(700, 468);
            break;
        case 2:
            g_wzoom = 3;
            size(1058, 708);
            break;
        case 3:
            g_wzoom = 4;
            size(1460, 950);
            break;
    }

    g_hedge = 32 * g_uizoom;
    g_vedge = 32 * g_uizoom;

    g_uindex[0] = 0;
    g_utop[0] = 0;
    g_ubottom[0] = 0;
    g_uindex[1] = 0;
    g_utop[1] = 0;
    g_ubottom[1] = 0;
    g_spare = 0;
    g_bsize = 0;
    g_btype = 0;
    g_phase = 0;
    g_button = LEFT;
    g_data[int('f')] = 1;
    g_data[int('g')] = 1;
    g_icons = mpLoadBinaryFile("icons.bin");
    set_tool(3);

    g_gridx = 8;
    g_gridy = 8;
    g_multic = 0;
    g_repanel = -2;

    //UI colors
    makecolor(256, 0xff, 0, 0xff);
    makecolor(257, 180, 180, 180);
    g_rgb[257] = 0xff808080;
    makecolor(258, 0, 0, 0);
    makecolor(259, 48, 48, 48);
    //R,G,B
    makecolor(260, 255, 0, 0);
    makecolor(261, 0, 255, 0);
    makecolor(262, 0, 0, 255);

    mpSetupMachine(machine);
    g_windowx = width - (g_hedge) - (g_wzoom * X);
    g_windowx = int(g_windowx / 2);
    g_windowy = height - (g_vedge) - (g_wzoom * Y);
    g_windowy = int(g_windowy / 2);

    switcher(3);

    g_map[4] = byte(g_maxcolors - 1);
    g_ofarge = g_farge;
    MX = int(X / 8);
    MY = int(Y / 8);
    g_realfront = byte(g_farge);
    g_realback = byte(g_backg);

    for (int y = 0; y < Y; y++)
    for (int x = 0; x < X; x++)
        absolute_clearpoint(x, y);

    switcher(2);
    noStroke();
    background(g_r[259], g_g[259], g_b[259]);
    sussborder();
    setup_raster();

    message("Multipaint|Metal|v22.5.2017");
    message("*");
    g_dirty = true;
}


void mpSetTitle(String str) {
    console.log("TITLE: '" + str + "'");
}


boolean fexists(String fname) {
    //  File f = new File(fname);
    //  if (f.exists())return true;
    return true;
}


function mpLoadFileSelector(fmtname, fmtexts, fcallback)
{
    var mpUI = stGE("mpUI");
    if (mpUI)
    {
        stClearChildren(mpUI);
        mpUI.style.background = "red";
        mpUI.style.padding = "0.5em";

        mobj = stCE("input", "mpFileSelector");
        mobj.type = "file";
        mobj.name = "name";
        mobj.multiple = false;

        if (fmtexts != null)
            mobj.accept = fmtexts;

        stAddEventOb(mobj.name, mobj, "change",
            function(evt)
            {
                var files = evt.target.files;
                if (files.length > 0)
                {
                    var freader = new FileReader();

                    freader.onloadend = (function(theFile) {
                        fcallback(theFile, new Uint8Array(freader.result));
                    });

                    freader.readAsArrayBuffer(files[0]);
                }
                stClearChildren(mpUI);
                mpUI.style.background = null;
            });

        mpUI.appendChild(mobj);

        mobj = stCE("span", "mpFileInfo");
        mobj.innerHTML = "Load / import an '<b>"+ fmtname +"</b>' file.";
        mpUI.appendChild(mobj);
    }
    else
        return null;
}


//
// Basically the same as Processing loadBytes(), but it seems
// that Processing.JS's loadBytes() is broken at least in v1.4.8
// and does not return byte-clean data. So roll a replacement of
// our own design. --ccr
//
byte[] mpLoadBinaryFile(String url)
{
    var xhr = new XMLHttpRequest();
    xhr.open("GET", url, false);
    xhr.overrideMimeType("text/plain; charset=x-user-defined");
    xhr.setRequestHeader("If-Modified-Since", "Fri, 01 Jan 1960 00:00:00 GMT");
    xhr.send(null);

    if (xhr.status !== 200 && xhr.status !== 0)
        return null;

    var string = xhr.responseText;
    byte[] ret = new byte[string.length];

    for (var i = 0; i < string.length; i++)
    {
        ret[i] = string.charCodeAt(i) & 0xff;
    }

    return ret;
}


//
// "Save" a byte array to file. Basically creates a blob URI
// and dumps it in the DOM, giving user a download.
//
void mpSaveBinaryFile(String name, byte[] data)
{
    var blob = new Blob([new Uint8Array(data)], {type: "application/octet-stream"}),
        url = window.URL.createObjectURL(blob);

    var alink = stCE("a");
    var mpUI = stGE("mpUI");
    stClearChildren(mpUI);
    mpUI.appendChild(alink);

    alink.style = "display: none";
    alink.href = url;
    alink.download = name;
    alink.click();

    window.URL.revokeObjectURL(url);
}


void mpSavePNGImage(String name)
{
    PImage simg;
    if (g_data[int('Q')] == 0)
        simg = mpRenderImageWithoutBorder();
    else
        simg = mpRenderImageWithBorder();

    // XXX TODO .. actually save the image
}


bool mpLoadPNGImage(String name)
{
    PImage simg = null;
    if (simg == null)
    {
        message("Error 1");
        return false;
    }

    int lefth = g_farge;
    int righth = g_backg;
    storeparameters();

    g_data[int('d')] = 0;
    g_data[int('t')] = 0;
    g_data[int('b')] = 1; //old IQ

    if (!mpImportFromImage(simg))
    {
        message("Error 2");
        return false;
    }

    restoreparameters();
    refreshpalette();
    refresh();
    g_boxreconstruct = 2;
    message("Image|loaded");
    selectcolor(0, lefth);
    selectcolor(1, righth);
    return true;
}


void mpSaveNativeImage(String name)
{
    //save the picture page g_map[], make sure some essential parameters are correct
    g_map[3] = byte(machine);
    g_map[5] = byte(MX);
    g_map[7] = byte(MY);

    mpSaveBinaryFile(name, g_map);
}


void mpSetNativeImage(byte[] data, bool noError)
{
    if (data == null) {
        message("NO FILE");
        return false;
    }

    if (data[3] != machine && !noError) {
        message("Wrong|machine!");
        return false;
    }

    g_map = data;

    refreshpalette();
    consistency();
    g_farge = int(g_realfront);
    g_ofarge = g_farge;
    g_backg = int(g_realback);
    sussborder();

    message("Page|loaded");

    return true;
}


bool mpLoadNativeImage(String name, bool noError)
{
    return mpSetNativeImage(
        mpLoadBinaryFile(name), noError);
}


void draw()
{
    if (!focused) {
        g_control = false;
        g_shift = false;
    }

    // If there should be no update, do not update.
    if (!g_dirty && !g_rubbermode)
        return;
    g_dirty = false;

    // Animate animbrush
    if (g_data[int('n')] == 1) {
        if (++g_animtime > 8 - g_animspeed * 2) {
            animbrush_do();
            g_animtime = 0;
        }
    }


    // Update some mouse variables
    if (g_data[int('m')] == 0 && g_data[int('M')] == 0) {
        g_mx = g_msx - g_windowx;
        g_my = g_msy - g_windowy;
    } else {
        g_mx = mouseX;
        g_my = mouseY;
    }

    g_msx = mouseX;
    g_msy = mouseY;

    // Actual tool drawing
    if (g_tooltrigger == 1) { // comes from mousepressed, or mousedragged
        if (g_mx <= width - g_hedge && g_my <= height - g_vedge)
            do_tool(g_mx, g_my, g_button);
    }

    switcher(0);

    for (int i = 0; i < 1024; i++) {
        g_remdo[i] = 0;
    }

    // Tool drawing for shows
    int lipo = g_button;
    if (tool() != 5) {
        if (g_phase == 0)
            g_button = LEFT;
        do_tool(g_mx, g_my, LEFT);
    }
    if (tool() == 6) doline(g_rx, g_ry, g_rx2, g_ry2, 0);
    if (tool() == 7) docircle(g_rx, g_ry, g_rx2, g_ry2);
    if (tool() == 8) rectangle(g_rx, g_ry, g_rx2, g_ry2);
    g_button = lipo;

    // Screen update stuff
    loadPixels();
    viewport();
    //magport();
    switcher(1);
    update_ui();
    updatePixels();

    if (g_msgctr > 0)
        g_msgctr--;

    // UI file operations
    if (fileselect) // Fileselect "event" for Load
    {
        mpLoadFileSelector("Multipaint image", ".mp",
        function (fh, fdata)
        {
            if (mpSetNativeImage(fdata))
            {
                store_undo();
                mpSetTitle(fh.name);
                if (g_spare)
                    sfilename = fh.name;
                else
                    filename = fh.name;

                refresh();
            }
        });

        fileselect = false;
        g_shift = false;
        g_control = false;
    }

    if (saveas) // Fileselect "event" for Save as
    {
        mpSaveNativeImage(filename +"_"+ (g_spare ? "spare" : "main") +".mp");

        saveas = false;
        g_shift = false;
        g_control = false;
    }

    if (exportsrc)
    {
        message("Export TXT|is disabled");
/*
        exportemuname = g_spare ? sfilename : filename;
        machine_export(exportemuname, 1);
*/
        exportsrc = false;
        g_shift = false;
        g_control = false;
    }

    if (exportemu)
    {
        message("Export EMU|is disabled");
/*
        exportemuname = g_spare ? sfilename : filename;
        machine_export(exportemuname, 0);
*/
        exportemu = false;
        g_shift = false;
        g_control = false;
    }

    if (importfmt)
    {
        mpLoadFileSelector(g_formatname, g_formatext,
        function (fh, fdata)
        {
            store_undo();
            filename = fh.name;
            mpImportFormat(fdata);
            refresh();
        });

        importfmt = false;
        g_shift = false;
        g_control = false;
    }

    if (exportfmt)
    {
        tmp = mpExportFormat(0); // XXX TODO
        if (tmp != null)
        {
            mpSaveBinaryFile(filename +"_"+
                (g_spare ? "spare" : "main") +"."+ g_formatext,
                tmp);
            message("EXPORTED");
        }
        else
            message("COULD NOT|EXPORT");

        exportfmt = false;
        g_shift = false;
        g_control = false;
    }

    if (g_keymacro.length() > g_keymacpos) {
        macro_command(g_keymacro.charAt(g_keymacpos));
        g_keymacpos++;
    }
}