Way to prevent canvas context.putImageData() from manipulating image data?

23 Views Asked by At

I am trying to manipulate pixel data as seen in the following example. And my goal is to safe the manipulated data into a new image.

Manipulating already works, but when I try to save the image data with context.putImageData() it seems like the data is being manipulated internally.

Lets start with my initializing Code:

class MultiColorTexture{
    private initialized: boolean = false; 
    private multiColorImage: HTMLImageElement; 

    private canvas!: HTMLCanvasElement; 
    private context!: CanvasRenderingContext2D|null;

    constructor(textureUrl:string){
        this.multiColorImage = new Image(); 
        this.multiColorImage.src = textureUrl;
        this.multiColorImage.crossOrigin = "anonymous";
    }

    async Initialize(){
        await this.multiColorImage.decode(); 
        this.canvas = document.createElement("canvas");
        // this.canvas.style.imageRendering = "pixelated";
        this.canvas.style.imageRendering = "-moz-crisp-edges";
    
        this.canvas.width = this.multiColorImage.width;
        this.canvas.height = this.multiColorImage.height;

        this.context = this.canvas.getContext("2d");
        this.context?.drawImage(this.multiColorImage,0,0);

        this.initialized = true;
    }
}

You can see, that I already tried to change the canvas behaviour by setting styling options for imageRendering. But it didnt work.

Here my current code for manipulating - see comments for more information:

Manipulate(){ // this function is part of the MultiColorTexture class 
    if(!this.initialized){
        alert("Not initialized")
        debugger;
    }

    var imageData = this.context?.getImageData(0, 0, this.canvas.width, this.canvas.height);
    if(imageData === undefined){
        return;
    }
    
    // manipulating loop and putImageData()
    // I only want certain blue values in my final image and with my current example image the following values need to be manipulated. 
    // So it is not necessarily about the exact manipulation since this is already giving me the desired output. 
    // See first example image below
    for (var i=0, length=imageData?.data.length; i<length; i+=4){          
        if((imageData.data[i] !== imageData.data[i+1]) && (imageData.data[i+1] !== imageData.data[i+2]) && imageData.data[i+3] !==0){
        
            var blue = imageData.data[i+2];
            if( blue === 125 || blue === 124 ){
                imageData.data[i] = 0; 
                imageData.data[i+1] = 0; 
                imageData.data[i+2] = 123; 
                imageData.data[i+3] = 0;
            }
            if( blue === 171){
                imageData.data[i] = 0; 
                imageData.data[i+1] = 0; 
                imageData.data[i+2] = 170; 
                imageData.data[i+3] = 0;
            }

            if( blue === 32 || blue === 251 || blue === 81 || blue === 84){              
                imageData.data[i] = 0; 
                imageData.data[i+1] = 0; 
                imageData.data[i+2] = 0; 
                imageData.data[i+3] = 0;
            }

        }
    }
    this.context?.putImageData(imageData, 0,0);

    // first validating loop
    var counter = 0; 
    var tmpMap = new Map<number, number>(); 
    for (var i=0, length=imageData?.data.length; i<length; i+=4){
        if((imageData.data[i] !== imageData.data[i+1]) && (imageData.data[i+1] !== imageData.data[i+2]) && imageData.data[i+3] !==0){
            var blue = imageData.data[i+2];
            if(!tmpMap.has(blue)){
                tmpMap.set(blue, 1);
            }    
            else{
                var cnt = tmpMap.get(blue);
                cnt! += 1; 
                tmpMap.set(blue, cnt!);
            }
            counter++;
        }
    }

    console.log(tmpMap);
    console.log(counter);

    // second validating loop after putImageData()
    var imageData2 = this.context?.getImageData(0, 0, this.canvas.width, this.canvas.height);
    if(imageData2 === undefined){
        return;
    }

    var counter2 = 0; 
    var tmpMap2 = new Map<number, number>(); 
    for (var i=0, length=imageData2?.data.length; i<length; i+=4){
        if((imageData2.data[i] !== imageData2.data[i+1]) && (imageData2.data[i+1] !== imageData2.data[i+2]) && imageData2.data[i+3] !==0){
            var blue = imageData2.data[i+2];
            if(!tmpMap2.has(blue)){
                tmpMap2.set(blue, 1);
            }    
            else{
                var cnt = tmpMap2.get(blue);
                cnt! += 1; 
                tmpMap2.set(blue, cnt!);
            }
            counter2++;
        }
    }
    console.log(tmpMap2);
    console.log(counter2);
}

Output after first validating loop which shows exactly what I want:

enter image description here

Output after second validation loop which show different values. This exactly what I currently dont understand: enter image description here

From my understanding it should be the exact same output. Does anyone of you know what is going on here and how to fix my problem?

0

There are 0 best solutions below