从png图像创建jpg图像时,将html画布黑色背景更改为白色背景

时间:2015-08-22 19:42:09

标签: javascript html canvas base64 png

我有// here I want to stop any calls coming back from .geocode if a //timer times out after say 3-5 seconds. var navbarGeocoder = new google.maps.Geocoder(); navbarGeocoder.geocode({ // call to geocode 'address': navbarInput.value }, function(results, status) { if (status == google.maps.GeocoderStatus.OK) { $("#latitude").val(results[0].geometry.location.G); $("#longitude").val(results[0].geometry.location.K); $("#mapType").val(results[0].types[0]); } $('form')[0].submit(); });,其中加载了canvas图像。我通过png方法得到jpg base64字符串,如下所示:

.toDataURL()

$('#base64str').val(canvas.toDataURL("image/jpeg")); 图片的透明部分在新png图片中显示为黑色。

将此颜色更改为白色的任何解决方案?提前谢谢。

8 个答案:

答案 0 :(得分:11)

这种黑化的发生是因为' image / jpeg'转换涉及将所有画布像素的alpha设置为完全不透明(alpha = 255)。问题是透明画布像素的颜色为fully-black-but-transparent。因此,当您将这些黑色像素变为不透明时,结果就是黑色的jpeg。

解决方法是手动将所有非不透明画布像素更改为所需的白色而不是黑色。

这样当它们变得不透明时,它们将显示为白色而不是黑色像素。

以下是:

// change non-opaque pixels to white
var imgData=ctx.getImageData(0,0,canvas.width,canvas.height);
var data=imgData.data;
for(var i=0;i<data.length;i+=4){
    if(data[i+3]<255){
        data[i]=255;
        data[i+1]=255;
        data[i+2]=255;
        data[i+3]=255;
    }
}
ctx.putImageData(imgData,0,0);

答案 1 :(得分:9)

这个答案有点长,但我觉得它更“正确”,因为它处理这些事情而不直接修改原始画布数据。我发现这是一个非常混乱和理论上不满意的解决方案。有内置的功能来实现它,它们应该被使用。这是我发现/偷窃的解决方案:

function canvasToImage(backgroundColor){

var context = document.getElementById('canvas').getContext('2d');

canvas = context.canvas;
//cache height and width        
var w = canvas.width;
var h = canvas.height;

var data;

//get the current ImageData for the canvas.
data = context.getImageData(0, 0, w, h);

//store the current globalCompositeOperation
var compositeOperation = context.globalCompositeOperation;

//set to draw behind current content
context.globalCompositeOperation = "destination-over";

//set background color
context.fillStyle = backgroundColor;

//draw background / rect on entire canvas
context.fillRect(0,0,w,h);

//get the image data from the canvas
var imageData = this.canvas.toDataURL("image/jpeg");

//clear the canvas
context.clearRect (0,0,w,h);

//restore it with original / cached ImageData
context.putImageData(data, 0,0);

//reset the globalCompositeOperation to what it was
context.globalCompositeOperation = compositeOperation;

//return the Base64 encoded data url string
return imageData;
}

基本上,您创建一个白色背景图像并将其置于画布下,然后打印出来。这个函数主要是从某人的博客中抄袭的,但它需要进行一些修改 - 比如实际获取上下文 - 并直接从我的(工作)代码中复制,所以只要你的canvas元素有id'canvas',你应该能够复制/粘贴它并让它工作。

这是我修改过的博文:

http://www.mikechambers.com/blog/2011/01/31/setting-the-background-color-when-generating-images-from-canvas-todataurl/

我的功能在这方面的一大优势是它输出到jpeg而不是png,这更有可能在chrome中运行良好,其数据库限制为2MB,它实际上抓住了上下文,这是一个明显的遗漏原来的功能。

答案 2 :(得分:2)

标记答案是正确的,但是当图片应用了一些抗锯齿时,导出的图像将不会像它应该的那样好(主要是文本)。我想提高他的解决方案:

AutoField

答案 3 :(得分:1)

如果你只想移动到白色全透明像素,只需检查(data [i + 3] == 0)而不是(data [i + 3] <255)。

答案 4 :(得分:0)

    private static final int SAMPLE_INTERVAL = 20;
    private static final int SAMPLE_SIZE = 2;
    private static final int BUF_SIZE = SAMPLE_INTERVAL * SAMPLE_INTERVAL * SAMPLE_SIZE * 2; //1600Bytes

    // the audio recording options
    private static final int RECORDING_RATE = 8000; // Hertz
    private static final int CHANNEL = AudioFormat.CHANNEL_IN_MONO;//16
    private static final int FORMAT = AudioFormat.ENCODING_PCM_16BIT;//2


    AudioRecord  audioRecorder = new AudioRecord(MediaRecorder.AudioSource.MIC,
                RECORDING_RATE, CHANNEL, FORMAT, BUF_SIZE * 10);

答案 5 :(得分:0)

专门花了很多时间在这篇文章和这篇文章上之后,这些解决方案有点奏效了,期望我只是无法让画布看起来正确。无论如何,我在其他地方找到了该解决方案,并希望将其发布在此处,以防其他人花费大量时间尝试将黑色背景变为白色,并看起来像原始的背景。

public getURI(): string {
    let canvas = <HTMLCanvasElement>document.getElementById('chartcanvas');
    var newCanvas = <HTMLCanvasElement>canvas.cloneNode(true);
    var ctx = newCanvas.getContext('2d');
    ctx.fillStyle = "#FFF";
    ctx.fillRect(0, 0, newCanvas.width, newCanvas.height);
    ctx.drawImage(canvas, 0, 0);
    return newCanvas.toDataURL("image/jpeg");
}

答案 6 :(得分:0)

这是我调整照片尺寸并处理黑色透明背景问题的功能:

resizeImage({ file, maxSize, backgroundColor }) {
    const fr = new FileReader();
    const img = new Image();

    const dataURItoBlob = (dataURI) => {
        const bytes = (dataURI.split(',')[0].indexOf('base64') >= 0)
            ? window.atob(dataURI.split(',')[1])
            : window.unescape(dataURI.split(',')[1]);
        const mime = dataURI.split(',')[0].split(':')[1].split(';')[0];
        const max = bytes.length;
        const ia = new Uint8Array(max);
        for (let i = 0; i < max; i += 1) {
            ia[i] = bytes.charCodeAt(i);
        }
        return new Blob([ia], { type: mime });
    };

    const resize = () => {
        // create a canvas element to manipulate
        const canvas = document.createElement('canvas');
        canvas.setAttribute('id', 'canvas');
        const context = canvas.getContext('2d');

        // setup some resizing definitions
        let { width, height } = img;
        const isTooWide = ((width > height) && (width > maxSize));
        const isTooTall = (height > maxSize);

        // resize according to `maxSize`
        if (isTooWide) {
            height *= maxSize / width;
            width = maxSize;
        } else if (isTooTall) {
            width *= maxSize / height;
            height = maxSize;
        }

        // resize the canvas
        canvas.width = width;
        canvas.height = height;

        // place the image on the canvas
        context.drawImage(img, 0, 0, width, height);

        // get the current ImageData for the canvas
        const data = context.getImageData(0, 0, width, height);

        // store the current globalCompositeOperation
        const compositeOperation = context.globalCompositeOperation;

        // set to draw behind current content
        context.globalCompositeOperation = 'destination-over';

        // set background color
        context.fillStyle = backgroundColor;

        // draw background / rect on entire canvas
        context.fillRect(0, 0, width, height);

        // get the image data from the canvas
        const imageData = canvas.toDataURL('image/jpeg');

        // clear the canvas
        context.clearRect(0, 0, width, height);

        // restore it with original / cached ImageData
        context.putImageData(data, 0, 0);

        // reset the globalCompositeOperation to what it was
        context.globalCompositeOperation = compositeOperation;

        // return the base64-encoded data url string
        return dataURItoBlob(imageData);
    };

    return new Promise((resolve, reject) => {
        if (!file.type.match(/image.*/)) {
            reject(new Error('VImageInput# Problem resizing image: file must be an image.'));
        }

        fr.onload = (readerEvent) => {
            img.onload = () => resolve(resize());
            img.src = readerEvent.target.result;
        };

        fr.readAsDataURL(file);
    });
},

这是Vue JS实例方法,可以这样使用:

// this would be the user-uploaded file from the input element
const image = file;

const settings = {
    file: image,
    maxSize: 192, // to make 192x192 image
    backgroundColor: '#FFF',
};

// this will output a base64 string you can dump into your database
const resizedImage = await this.resizeImage(settings);

我的解决方案是将大约74个不同的StackOverflow答案组合在一起,这些答案与在客户端调整图像大小有关,而最终的老板是处理透明的PNG文件。

没有Laereom的回答,我的回答是不可能的。

答案 7 :(得分:0)

为什么不将其另存为 PNG?

canvas.toDataURL("image/png");