URI:
       implement atkinson 1bpp - webgbcam - [fork] gameboy webcam
  HTML git clone git://src.adamsgaard.dk/webgbcam
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit 9a7d5872d5148d5fec5ca7dc0ff12c8bc87f90da
   DIR parent deab66d76bc081b63432956c3e7f9c213824f448
  HTML Author: maple syrup <maple@maple.pet>
       Date:   Mon,  6 Nov 2023 22:47:57 +0000
       
       implement atkinson 1bpp
       
       Diffstat:
         M app.js                              |     115 +++++++++++++++++++++++++------
         M ui/ui-settings.png                  |       0 
       
       2 files changed, 95 insertions(+), 20 deletions(-)
       ---
   DIR diff --git a/app.js b/app.js
       @@ -244,7 +244,7 @@ var currentFacingMode = 'user';
        var appScale;
        var frameDrawing;
        const gifLength = 50;
       -const outputScale = 5;
       +var outputScale = 6;
        var gifRecording,
                        gifEncoder,
                        gifFrames,
       @@ -385,6 +385,12 @@ var buttons = {
                        y:131,
                        width:16,
                        height:13
       +        },
       +        bppSwitch: {
       +                x:45,
       +                y:133,
       +                width:50,
       +                height:9
                }
        };
        
       @@ -526,6 +532,10 @@ var screens = {
                                                if(currentPalette >= palettes.length) currentPalette = 0;
                                                savePrefs();
                                        }
       +                        },
       +                        {
       +                                bounding: buttons.bppSwitch,
       +                                action: bppSwitch
                                }
                        ]
                },
       @@ -552,8 +562,8 @@ var screens = {
        };
        
        // global settings for gbcamera
       -var renderWidth = 160,
       -                renderHeight = 144,
       +var renderWidth = 320,
       +                renderHeight = 288,
                        currentPalette = 0,
                        currentUI = screens.uiMain;
        
       @@ -567,7 +577,8 @@ var cameraVars = {
                xOffset: 0,
                yOffset: 0,
                xScale: 1,
       -        yScale: 1
       +        yScale: 1,
       +        bppSwitch: 2 // 2bpp = gameboy, 1bpp = atkinson, doubleres
        };
        
        // function to check if phone is portrait oriented
       @@ -595,7 +606,8 @@ function getMousePos(canvas, event) {
        }
        //Function to check whether a point is inside a rectangle
        function isInside(pos, rect){
       -        return pos.x > rect.x && pos.x < rect.x+rect.width && pos.y < rect.y+rect.height && pos.y > rect.y
       +        const scale = 2;
       +        return pos.x > rect.x * scale && pos.x < (rect.x+rect.width) * scale && pos.y < (rect.y+rect.height) * scale && pos.y > rect.y * scale;
        }
        
        function switchCameras() {
       @@ -639,9 +651,12 @@ function loadPrefs() {
                let localGamma = parseInt(localStorage.getItem("cameraGamma"));
                let localPalette = parseInt(localStorage.getItem("cameraPalette"));
                let localSharpness = parseInt(localStorage.getItem("cameraSharpness"));
       +        let localBpp = parseInt(localStorage.getItem("cameraBpp"));
                cameraVars.contrast = (localContrast ? localContrast : 3);
                cameraVars.gamma = (localGamma ? localGamma : 3);
                cameraVars.sharpness = (localSharpness ? localSharpness : 3);
       +        cameraVars.bppSwitch = (localBpp ? localBpp : 2);
       +        outputScale = (cameraVars.bppSwitch == 2 ? 6 : 3);
                currentPalette = (localPalette ? localPalette : 0);
        }
        
       @@ -649,9 +664,29 @@ function savePrefs() {
                localStorage.setItem("cameraContrast", cameraVars.contrast);
                localStorage.setItem("cameraGamma", cameraVars.gamma);
                localStorage.setItem("cameraSharpness", cameraVars.sharpness);
       +        localStorage.setItem("cameraBpp", cameraVars.bppSwitch);
                localStorage.setItem("cameraPalette", currentPalette);
        }
        
       +function bppSwitch() {
       +        if (cameraVars.bppSwitch == 2) {
       +                // to 1bpp
       +                cameraVars.bppSwitch = 1;
       +                cameraVars.width = 256;
       +                cameraVars.height = 224;
       +                outputScale = 3;
       +        } else {
       +                // to 2bpp
       +                cameraVars.bppSwitch = 2;
       +                cameraVars.width = 128;
       +                cameraVars.height = 112;
       +                outputScale = 6;
       +        }
       +        cameraView.width = cameraVars.width;
       +        cameraView.height = cameraVars.height;
       +        initCameraDrawing(false);
       +}
       +
        function applyLevels(value, brightness, contrast, gamma) {
                let newValue = value / 255.0;
                newValue = (newValue - 0.5) * contrast + 0.5;
       @@ -748,6 +783,23 @@ Filters.gbcamera = function(pixels, ditherFactor) {
                return pixels;
        }
        
       +// thanks to https://beyondloom.com/blog/dither.html
       +Filters.atkinson = function(pixels) {
       +        let w = pixels.width;
       +        let d = pixels.data;
       +        const e=Array(2*w).fill(0), m=[0,1,w-2,w-1,w,2*w-1];
       +        for (i = 0; i < d.length; i=i+4) {
       +                let x = d[i]/256.0;
       +                const pix=x+(e.push(0),e.shift()), col=pix>.5, err=(pix-col)/8;
       +                m.forEach(x => e[x]+=err);
       +                let c = col ? 255 : 0;
       +                d[i] = c;
       +                d[i+1] = c;
       +                d[i+2] = c;
       +        }
       +        return pixels;
       +}
       +
        // takes grayscale and paints it with palette
        Filters.paletteSwap = function(pixels, palette) {
                let d = pixels.data;
       @@ -897,6 +949,11 @@ function initAppScaling() {
                cameraView.height = cameraVars.height;
                appView.width = renderWidth;
                appView.height = renderHeight;
       +
       +        let ctx = appView.getContext("2d");
       +        ctx.imageSmoothingEnabled = false;
       +        ctx = cameraView.getContext("2d");
       +        ctx.imageSmoothingEnabled = false;
        }
        
        // https://github.com/webrtc/samples/blob/gh-pages/src/content/devices/input-output/js/main.js
       @@ -941,14 +998,14 @@ function initCameraStream() {
                        .catch(handleError);
        }
        
       -function initCameraDrawing() {
       +function initCameraDrawing(start = true) {
                // if cameraStream has vertical or horizontal resolution of 0 then it's not initialized, we retry until the browser decides to properly work
                if (cameraStream.videoHeight == 0) setTimeout(restartCamera, 500);
        
                const track = window.stream.getVideoTracks()[0];
                let settings = track.getSettings();
       -        let str = JSON.stringify(settings, null, 4);
       -        console.log('settings ' + str);
       +        //let str = JSON.stringify(settings, null, 4);
       +        //console.log('settings ' + str);
        
                // calculate scale and offset to render camera stream to camera view canvas
                if(cameraStream.videoWidth >= cameraStream.videoHeight) {
       @@ -971,17 +1028,18 @@ function initCameraDrawing() {
                } else {
                        cameraView.getContext('2d').setTransform(1, 0, 0, 1, 0, 0);
                }
       -        console.log(cameraVars);
       +        //console.log(cameraVars);
                
                cameraOutput.width = cameraVars.width * outputScale;
                cameraOutput.height = cameraVars.height * outputScale;
                let ctx = cameraOutput.getContext("2d");
                ctx.imageSmoothingEnabled = false;
        
       -        cameraStream.play();
       -        
       -        clearInterval(frameDrawing)
       -        frameDrawing = setInterval(drawFrame, 100);
       +        if (start) {
       +                cameraStream.play();
       +                clearInterval(frameDrawing)
       +                frameDrawing = setInterval(drawFrame, 100);
       +        }
        }
        
        function showGifModal() {
       @@ -1039,34 +1097,51 @@ function gifFrame() {
                if(--gifFrames == 0) gifEnd();
        }
        
       +function scaledFillRect(ctx, x, y, width, height) {
       +        const scale = 2;
       +        ctx.fillRect(x * scale, y * scale, width * scale, height * scale);
       +}
       +
        function drawFrame() {
                let camctx = cameraView.getContext('2d');
                camctx.drawImage(cameraStream, cameraVars.xOffset, cameraVars.yOffset, cameraVars.xScale, cameraVars.yScale, 0, 0, cameraVars.width, cameraVars.height);
                
                Filters.filterImage(Filters.grayscale, cameraView, []);
                Filters.filterImage(Filters.sharpen, cameraView, [sliderSharpness[cameraVars.sharpness]]);
       -        Filters.filterImage(Filters.gbcamera, cameraView, [cameraVars.dither]);
       +        
       +        if (cameraVars.bppSwitch == 2) {
       +                Filters.filterImage(Filters.gbcamera, cameraView, [cameraVars.dither]);
       +        } else {
       +                Filters.filterImage(Filters.atkinson, cameraView, []);
       +        }
                
                let ctx = appView.getContext("2d");
       -        ctx.drawImage(cameraView, 16, 16);
       -        ctx.drawImage(currentUI.elem, 0, 0);
       +        ctx.drawImage(cameraView, 32, 32, 256, 224);
       +        ctx.drawImage(currentUI.elem, 0, 0, 160, 144, 0, 0, 320, 288);
        
                if (currentUI === screens.uiSettings) {
                        // update settings values        
                        ctx.fillStyle = "rgb(192,192,192)";
                        for(let i = 1; i <= cameraVars.contrast; i++) {
       -                        ctx.fillRect(42, 22 - (i*3), 4, 2);
       +                        scaledFillRect(ctx, 42, 22 - (i*3), 4, 2);
                        }
                        for(let i = 1; i <= cameraVars.gamma; i++) {
       -                        ctx.fillRect(97, 22 - (i*3), 4, 2);
       +                        scaledFillRect(ctx, 97, 22 - (i*3), 4, 2);
                        }
                        for(let i = 1; i <= cameraVars.sharpness; i++) {
       -                        ctx.fillRect(152, 135 - (i*3), 4, 2);
       +                        scaledFillRect(ctx, 152, 135 - (i*3), 4, 2);
       +                }
       +                
       +                ctx.fillStyle = "rgb(130,130,130)";
       +                if (cameraVars.bppSwitch == 2) {
       +                        scaledFillRect(ctx, 70, 135, 4, 4);
       +                } else {
       +                        scaledFillRect(ctx, 65, 135, 4, 4);
                        }
                } else if (currentUI === screens.uiRecord) {
                        // update record length
                        ctx.fillStyle = "rgb(64,64,64)";
       -                ctx.fillRect(25, 134, 110 - (gifFrames / gifLength * 110), 6);
       +                scaledFillRect(ctx, 25, 134, 110 - (gifFrames / gifLength * 110), 6);
                }
        
                try {
   DIR diff --git a/ui/ui-settings.png b/ui/ui-settings.png
       Binary files differ.