"function procImg(isLvl, isRed) {\r\n"
    "if (document.getElementsByClassName('sourceImage').length == 0) {\r\n"
        "alert('First select image');\r\n"
        "return;\r\n"
    "}\r\n"
    "var palInd = epdArr[epdInd][2];\r\n"
    "if (isRed && (palInd & 1) == 0) {\r\n"
        "alert('This white-black display');\r\n"
        "return;\r\n"
    "}\r\n"
    "if (!isRed) palInd = palInd & 0xFE;\r\n"
    "curPal = palArr[palInd];\r\n"
    "getElm('dstBox').innerHTML=\r\n"
    "'<span class=\title\>Processed image</span><br><canvas id=\canvas\></canvas>';\r\n"
    "var canvas=getElm('canvas');\r\n"
    "var source = getElm('source');\r\n"
    "var sW = srcImg.width;\r\n"
    "var sH = srcImg.height;\r\n"
    "source.width = sW;\r\n"
    "source.height = sH;\r\n"
    "source.getContext('2d').drawImage(srcImg, 0, 0, sW, sH);\r\n"
    "var dX = parseInt(getElm('nud_x').value);\r\n"
    "var dY = parseInt(getElm('nud_y').value);\r\n"
    "var dW = parseInt(getElm('nud_w').value);\r\n"
    "var dH = parseInt(getElm('nud_h').value);\r\n"
    "if (dW < 3 || dH < 3) {\r\n"
        "alert('Image is too small');\r\n"
        "return;\r\n"
    "}\r\n"
    "canvas.width = dW;\r\n"
    "canvas.height = dH;\r\n"
    "var pSrc = source.getContext('2d').getImageData(0, 0, sW, sH);\r\n"
    "var croppedImageData = cropImage(pSrc, dX, dY, dW, dH);\r\n"
    "const width = croppedImageData.width;\r\n"
    "const height = croppedImageData.height;\r\n"
    "const data = croppedImageData.data;\r\n"
    "const outputData = new Uint8ClampedArray(dW * dH * 4);\r\n"
    "const buffer = [];\r\n"
    "const PALETTE = [\r\n"
        "{ r: 0, g: 0, b: 0 },\r\n"
        "{ r: 1, g: 0, b: 0 },\r\n"
        "{ r: 1, g: 1, b: 1 }\r\n"
    "];\r\n"
    "function valueOf(r, g, b) { return { r, g, b }; }\r\n"
    "function add(a, b) { return valueOf(a.r + b.r, a.g + b.g, a.b + b.b); }\r\n"
    "function sub(a, b) { return valueOf(a.r - b.r, a.g - b.g, a.b - b.b); }\r\n"
    "function mul(a, d) { return valueOf(a.r * d, a.g * d, a.b * d); }\r\n"
    "function diff(a, b) {\r\n"
        "const Rdiff = b.r - a.r;\r\n"
        "const Gdiff = b.g - a.g;\r\n"
        "const Bdiff = b.b - a.b;\r\n"
        "return Rdiff * Rdiff + Gdiff * Gdiff + Bdiff * Bdiff;\r\n"
    "}\r\n"
    "function findClosestPaletteColor(match) {\r\n"
        "let closest = PALETTE[0];\r\n"
        "for (let i = 0; i < PALETTE.length; i++) {\r\n"
            "if (diff(PALETTE[i], match) < diff(closest, match)) {\r\n"
                "closest = PALETTE[i];\r\n"
            "}\r\n"
        "}\r\n"
        "return closest;\r\n"
    "}\r\n"
    "function cropImage(imageData, x, y, w, h) {\r\n"
        "const data = imageData.data;\r\n"
        "const srcWidth = imageData.width;\r\n"
        "const srcHeight = imageData.height;\r\n"
        "x = Math.max(0, Math.min(x, srcWidth - 1));\r\n"
        "y = Math.max(0, Math.min(y, srcHeight - 1));\r\n"
        "w = Math.min(w, srcWidth - x);\r\n"
        "h = Math.min(h, srcHeight - y);\r\n"
        "w = Math.max(1, w);\r\n"
        "h = Math.max(1, h);\r\n"
        "const outputData = new Uint8ClampedArray(w * h * 4);\r\n"
        "for (let row = 0; row < h; row++) {\r\n"
            "for (let col = 0; col < w; col++) {\r\n"
                "const srcIdx = ((y + row) * srcWidth + (x + col)) * 4;\r\n"
                "const dstIdx = (row * w + col) * 4;\r\n"
                "outputData[dstIdx] = data[srcIdx];\r\n"
                "outputData[dstIdx + 1] = data[srcIdx + 1];\r\n"
                "outputData[dstIdx + 2] = data[srcIdx + 2];\r\n"
                "outputData[dstIdx + 3] = data[srcIdx + 3];\r\n"
            "}\r\n"
        "}\r\n"
        "return new ImageData(outputData, w, h);\r\n"
    "}\r\n"
    "for (let y = 0; y < height; y++) {\r\n"
        "buffer[y] = [];\r\n"
        "for (let x = 0; x < width; x++) {\r\n"
            "const idx = (y * width + x) * 4;\r\n"
            "buffer[y][x] = valueOf(\r\n"
                "data[idx] / 255,\r\n"
                "data[idx + 1] / 255,\r\n"
                "data[idx + 2] / 255\r\n"
            ");\r\n"
        "}\r\n"
    "}\r\n"
    "for (let y = 0; y < height; y++) {\r\n"
        "for (let x = 0; x < width; x++) {\r\n"
            "const old = buffer[y][x];\r\n"
            "const nem = findClosestPaletteColor(old);\r\n"
            "const outIdx = (y * width + x) * 4;\r\n"
            "outputData[outIdx] = nem.r * 255;\r\n"
            "outputData[outIdx + 1] = nem.g * 255;\r\n"
            "outputData[outIdx + 2] = nem.b * 255;\r\n"
            "outputData[outIdx + 3] = 255;\r\n"
            "const error = sub(old, nem);\r\n"
            "if (x + 1 < width) {\r\n"
                "buffer[y][x + 1] = add(buffer[y][x + 1], mul(error, 7 / 16));\r\n"
            "}\r\n"
            "if (x - 1 >= 0 && y + 1 < height) {\r\n"
                "buffer[y + 1][x - 1] = add(buffer[y + 1][x - 1], mul(error, 3 / 16));\r\n"
            "}\r\n"
            "if (y + 1 < height) {\r\n"
                "buffer[y + 1][x] = add(buffer[y + 1][x], mul(error, 5 / 16));\r\n"
            "}\r\n"
            "if (x + 1 < width && y + 1 < height) {\r\n"
                "buffer[y + 1][x + 1] = add(buffer[y + 1][x + 1], mul(error, 1 / 16));\r\n"
            "}\r\n"
        "}\r\n"
    "}\r\n"
    "var pDst = new ImageData(outputData, width, height);\r\n"
    "canvas.getContext('2d').putImageData(pDst, 0, 0);\r\n"
"}\r\n"