【问题标题】:Fill with background color, and then overlay a PNG填充背景色,然后叠加一个PNG
【发布时间】:2016-06-19 11:16:47
【问题描述】:

我正在尝试使画布具有背景颜色,然后在其上方叠加一个 PNG。

这是 png 纹理:

例如,如果backgroundColor = #D95753(亮红色),画布填充级别将为:

1.- 设置背景色

2.- 在背景颜色上方叠加png

这是我拥有的代码,但它不起作用。你看不到背景颜色,只有 png 图像。

var background = new Image();
background.src = "http://i.stack.imgur.com/LF1P0.png";
background.height = y; // set it before
background.width = x; // set it before

ctx.fillStyle = backgroundColor;
ctx.fillRect(0, 0, background.width, background.height);

background.onload = function() {
    ctx.drawImage(background,0,0, background.width, background.height);
    memeEverything();
}

谢谢!

【问题讨论】:

    标签: javascript jquery html canvas


    【解决方案1】:

    ctx.globalCompositeOperation = "乘"

    您可以使用复合方法并避免丢失动态范围,因为 frnt 的答案会这样做,0.6 的 alpha 会显着降低与原始蒙版的对比度。

    有许多混合选项,但对于您不想让白色透出而只想添加暗度的图像,请使用混合模式“正片叠底”,此方法将为您提供最佳对比度。

    ctx.fillStyle = ???  // the background colour you want
    ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height); // fill canvas with colour. This is now the destination
    // Destination pixels get multiplied by source pixels 
    // nC = dC  * (sC / 255);  ( seperate for each channel RGB. no effect on alpha )
    // where nC is the new colour, dC is destination colour and, sC is source colour;
    // White pixels make no change, all others reduce the the colour
    ctx.globalCompositeOperation = "multiply"; 
    // draw the image over the top.
    ctx.drawImage(img, 0, 0, canvas.width, cvtx.canvas.height);
    ctx.globalCompositeOperation = "source-over";  // reset to default
    

    最佳质量

    有一种更好的方法,但它涉及获取像素数据并在乘法之前对每个像素进行光子计数。这将更接近原始蒙版。公式为nC =Math.sqrt(dC * dC * (sC * sC / (255*255)));,其中nC 是新颜色,dC 是目标颜色,sC 是源颜色。应用到每个通道 RGB 忽略 alpha。

    // r,g,b are the background colour
    // img is a loaded image to convert
    var r = ?, g = ?, b = ?;
    // create canvas and context for image
    var c = document.createElement("canvas");
    c.width = img.width;
    c.height = img.height;
    var ctx = c.getContext("2d");
    // draw image onto the canvas
    ctx.drawImage(img, 0,0);
    // get the pixel data
    var data = ctx.getImageData(0,0,c.width,c.height);
    var d = data.data;
    // Convert background colour to photon count and normalise 
    r = r * r / (255 * 255);
    g = g * g / (255 * 255);
    b = b * b / (255 * 255);
    var i = 0, len = d.length;;
    // for each pixel do the multiply using photon counts
    while(i < len){
        d[i] = Math.sqrt(d[i] * d[i++] * r);
        d[i] = Math.sqrt(d[i] * d[i++] * g);
        d[i] = Math.sqrt(d[i] * d[i++] * b);
        i ++;
    }
    // put the image data back onto the canvas.
    ctx.putImageData(data,0,0);
    

    通过 alpha 复合。

    在您还可以使用 alpha 合成之后,乘法可能不是您想要的效果。有两种方法,简单和光子计数。请注意,这两种方法都假定背景颜色的 alpha 值为 255,并且适用于该值。

    获取像素的 alpha 的简单方法。如果您设置了 alpha 通道并且只使用了 canvas source-over(默认)混合功能,它是如何完成的。

    // assume you have got the pixel data etc.. see above snipets
    var amount = ?; // the mixing amount
    var i = 0, len = d.length;;
    // for each pixel do the alpha blend
    while(i < len){
        var rr = d[i];   // get source channels
        var gg = d[i + 1]; 
        var bb = d[i + 2]; 
        var alpha = (rr + gg + bb) / ( 3 * 255); // calculate alpha by finding the mean 
        // alpha is inverted  but also need to clamp alpha as floating point may give a value too high so clamp and invert
        alpha = Math.min(1, 1 - alpha);
        alpha *= amount;   // set the mix amount
        var aInv = 1 - alpha; // invert again
        // each channel is the sum of background and image colour time their respective mix amounts
        d[i++] = aInv * r + alpha * rr;  
        d[i++] = aInv * g + alpha * gg;
        d[i++] = aInv * b + alpha * bb;
        d[i++] = 255;  // in this case alpha is always 255
    }
    // put the image data back onto the canvas.
    ctx.putImageData(data,0,0);
    

    以及正确的方法(高品质专业混音的做法)

    // assume you have got the pixel data etc.. see above snipets
    var amount = ?; // the mixing amount
    // Convert background colour to photon count and normalise 
    r = r * r / (255 * 255);
    g = g * g / (255 * 255);
    b = b * b / (255 * 255);
    var i = 0, len = d.length;;
    // for each pixel do the alpha blend using photon counts
    while(i < len){
        var rr = d[i];   // get source channels
        var gg = d[i + 1]; 
        var bb = d[i + 2]; 
        rr *= rr;
        gg *= gg;
        bb *= bb;
        var alpha = (rr * + gg + bb) / ( 3 * 255 * 255); // calculate alpha by finding the mean in photon space
        // alpha is inverted but also need to clamp alpha as floating point may give a value too high so clamp and invert
        alpha = Math.min(1, 1 - alpha);
        alpha *= amount;   // set the mix amount
        var aInv = 1 - alpha; // invert again
        // each channel is the sum of background and image colour time their respective mix amounts
        d[i++] = Math.sqrt(aInv * r + alpha * rr);  
        d[i++] = Math.sqrt(aInv * g + alpha * gg);
        d[i++] = Math.sqrt(aInv * b + alpha * bb);
        d[i++] = 255;  // in this case alpha is always 255
    }
    // put the image data back onto the canvas.
    ctx.putImageData(data,0,0);
    

    为什么要使用光子计数颜色模型。

    我使用的颜色模型基于设备显示屏发射的光子数。每个颜色通道都有一个从 0 到 255 的值,但这些值与显示器的实际输出不匹配,也不代表相机(输入设备)捕获的光子数。它们是光子通量的平方根。如果您通过简单的线性平均值混合颜色并且不考虑这一点,则生成的颜色将比应有的颜色更暗(这在图像具有高色调对比度时尤其明显)并且对比度曲线将变宽。当您直接操作像素以获得最佳结果时,始终将 r、g、b 值平方,进行混合、混合等。准备好后,通过计算结果的平方根将值转换回对数表示。

    本视频将详细讲解Computer Color is Broken

    【讨论】:

      【解决方案2】:

      尝试通过如下定义 rgba 格式的 fillRect() 和颜色,

      <canvas id="canvs" width="500" height="500">
      <img src="http://i.stack.imgur.com/LF1P0.png" id="img">
      </canvas>
      
      function onld(){
      var can = document.getElementById('canvs');
      var img = document.getElementById('img');
      var ctx = can.getContext('2d');
      ctx.fillStyle = 'rgba(217, 87, 83, 1)';
      ctx.fillRect(10,10,500,500);
      ctx.globalAlpha=0.6;
      ctx.drawImage(img,10,10);
      }
      window.addEventListener('load',onld);
      

      【讨论】:

      • 这里我们使用了 fillRect 来填充从十六进制转换为 rgba 的鲜红色背景,这样我们就可以减少颜色的不透明度。您可以尝试将其从 0.8 更改为 1。为了使您的图像与背景颜色合并,它需要是透明的。
      • 如@Kaiido ctx.globalAlpha=0.6 所说,将其添加到代码中;
      猜你喜欢
      • 1970-01-01
      • 2015-02-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-01-29
      • 2023-03-24
      • 2019-01-05
      • 1970-01-01
      相关资源
      最近更新 更多