2015-11-06 25 views
0

我有两个画布 - 两个尺寸相同。 MainCanvas完全充满了背景图像。在SecondaryCanvas上,我有一个小图像 - 一个公司的标志,有一些透明的区域(基本上所有的白色像素的alpha都减少到0)。将效果应用到画布上的透明区域的边缘

我想对SecondaryCanvas应用某种倒角或内阴影,但只有存在实际图像的地方。通常情况下,我会看到人们为此做出了行程,但我无法跟踪非透明区域的边缘。

我该怎么做?我想到我可以逐个扫描整个SecondaryCanvas并检查相邻像素是否透明(尝试查找“边缘”)......并将适当的颜色应用于像素如果是...但似乎非常CPU密集。

+0

您可以找到使用 “行进广场” 算法外边缘。此前的SO帖子显示了它的使用示例:http://stackoverflow.com/questions/25873601/how-can-i-get-all-coordinates-of-shapes-edge-in-canvas/25875512#25875512 – markE

+0

啊!发现它:http://stackoverflow.com/questions/25467349/in-a-2d-canvas-is-there-a-way-to-give-a-sprite-an-outline – Kaiido

+0

由于这是一次性操作1)处理时间无关紧要,你甚至可以采取“巨大”的时间,比如...... 50ms来处理一次图像。但是2)如何在图像处理软件中进行一次更改? Rq还指出一些公司希望保持对视觉效果的控制,并且更愿意为您提供一个徽标*,以便他们更改以符合特定的显示要求。 – GameAlchemist

回答

3

这是我最熟悉的使用卷积滤波器的最快速度。

从源图像复制到目的地。我也为这个解决方案添加了一些标志。

功能(抱歉拼写)。

embose(imageDataSource,imageDataDestination,方法,emboseImageEdge,perceptualCorrect,edgeOnly)

  • imageDataSource:像素数据源图像
  • imageDataDestination:对目标的像素数据。必须与源代码不同 或结果将不正确。必须是相同的尺寸
  • 方法:(字符串)“3像素软”,“3像素”,“1像素”// 之一忘记默认,所以如果通过未知的方法将崩溃。我相信你 可以修复
  • emboseImageEdge:(布尔)如果在图像 边缘的真像素将浮雕。
  • perceptualcorrect:(布尔)如果是真的使用人类认知计算为 辉度。运行时间稍慢。
  • edgeOnly:(布尔)如果为true,则只在靠近透明的 边缘浮雕像素。否则按照亮度压印所有不透明的像素。

函数如果处理完成则返回true,否则返回false。如果为false,则不更改像素。

Todo。 我忘了添加反向,但很容易做到,只需将反转,然后将中心值设置为1(必须是1或者看起来不太好)。 您还可以通过旋转embos阵列中的负/正线来旋转灯光的方向,但稍微复杂一些。 对于较软的插槽创建更大的卷积embos阵列。设置sizehalfSize以匹配。 EG 3像素浮雕需要7×7阵列,其中size=7halfSize = 3具有相似的一组值。 embos数组越大,函数越慢。 对于非常大的图像,这会阻止页面。如果将此功能用于大图像,请将此功能移至网络工作人员。

因为它使用卷积滤波器,所以它可以适应做其他过滤器。虽然这只使用像素亮度,但很容易修改为每个颜色通道滤镜。所以过滤器类型可以适应。高斯模糊,模糊,锐化,边缘检测等等。

查看代码示例底部如何使用它。

希望这可以满足您的需求或可以进行调整。任何问题随时问。很抱歉,评论目前很少,但时间很短。将尽快恢复。

var canvas = document.getElementById("canV"); 
 
var ctx = canvas.getContext("2d"); 
 

 
// Groover API log dependency replacement 
 
function log(data){ 
 
    // console.log(data); // just suck it up 
 
} 
 

 
// Groover Bitmaps API dependency replacement 
 
// Extracted from Groover.Bitmaps 
 
var createImage= function(w,h){ // create a image of requier size 
 
    var image = document.createElement("canvas"); 
 
    image.width = w; 
 
    image.height =h; 
 
    image.ctx = image.getContext("2d"); // tack the context onto the image 
 
    return image; 
 
}  
 
    
 

 
function embose(imageDataSource,imageDataDestination, method, emboseImageEdge, perceptualCorrect,edgeOnly){ 
 
    "use strict"; 
 
    var dataS = imageDataSource.data; 
 
    var dataD = imageDataDestination.data; 
 
    var w = imageDataSource.width; 
 
    var h = imageDataSource.height; 
 
    if(dataS.length !== dataD.length){ 
 
     return false; // failed due to size mismatch 
 
    } 
 
    var embos,size,halfSize; 
 
    var lMethod = method.toLowerCase(); // some JS engines flag reasignment of 
 
             // arguments as unoptimised as this 
 
             // is computationally intensive create a 
 
             // new var for lowercase 
 

 
    if(lMethod === "2 pixel soft"){ 
 
     embos = [ 
 
      -0.25 , -0.5 , -1, -1 , 0.5, 
 
      -0.5 , -1 , -1, 1 , 1, 
 
      -1 , -1 , 1, 1 , 1, 
 
      -1 , -1 , 1, 1 , 0.5, 
 
      -0.5 , 1, 1, 0.5 , 0.25 
 
     ]; 
 
     size = 5; 
 
     halfSize = 2; 
 
    }else 
 
    if(lMethod === "2 pixel"){ 
 
     embos = [ 
 
      -1 , -1 , -1, -1 , 1, 
 
      -1 , -1 , -1, 1 , 1, 
 
      -1 , -1 , 1, 1 , 1, 
 
      -1 , -1 , 1, 1 , 1, 
 
      -1 , 1, 1, 1 , 1 
 
     ]; 
 
     size = 5; 
 
     halfSize = 2; 
 
    }else 
 
    if(lMethod === "1 pixel"){ 
 
     embos = [ 
 
      -1 , -1, -1, 
 
      -1, 1, 1, 
 
      1 , 1, 1 
 
     ]; 
 
     size = 3; 
 
     halfSize = 1; 
 
    } 
 
var deb = 0 
 
    var x,y,l,pl,g,b,a,ind,scx,scy,cx,cy,cInd,nearEdge,pc; 
 
    pc = perceptualCorrect; // just for readability 
 
    for(y = 0; y < h; y++){ 
 
     for(x = 0; x < w; x++){ 
 
      ind = y*4*w+x*4; 
 
      l = 0; 
 
      nearEdge = false; 
 
      if(dataS[ind+3] !== 0){ // ignor transparent pixels 
 
       for (cy=0; cy<size; cy++) { 
 
        for (cx=0; cx<size; cx++) { 
 
         scy = y + cy - halfSize; 
 
         scx = x + cx - halfSize; 
 
         if (scy >= 0 && scy < h && scx >= 0 && scx < w) { 
 
          cInd = (scy*w+scx)*4; 
 
          if(dataS[cInd+3] === 0){ 
 
           nearEdge = true; 
 
          } 
 
          l += pc?(embos[cy*size+cx] * 
 
             ((Math.pow(dataS[cInd++]*0.2126,2)+ 
 
             Math.pow(dataS[cInd++]*0.7152,2)+ 
 
             Math.pow(dataS[cInd++]*0.0722,2) 
 
             )/3)): 
 
            (embos[cy*size+cx] * 
 
             ((dataS[cInd++]+ 
 
             dataS[cInd++]+ 
 
             dataS[cInd++] 
 
             )/3)); 
 
             
 
         }else 
 
         if(emboseImageEdge){ 
 
          nearEdge = true; 
 
         } 
 
        } 
 
       } 
 
         
 
       if((nearEdge && edgeOnly) || ! edgeOnly){ 
 
        if(pc){ 
 
         pl = Math.sqrt((Math.pow(dataS[ind]*0.2126,2) + Math.pow(dataS[ind+1]*0.7152,2) + Math.pow(dataS[ind+2]*0.0722,2))/3); 
 
         l = Math.sqrt(Math.max(0,l)); 
 

 
         if(pl > 0){ 
 
          pl = l/pl; 
 
          dataD[ind] = Math.sqrt(dataS[ind]*dataS[ind++]*pl); 
 
          dataD[ind] = Math.sqrt(dataS[ind]*dataS[ind++]*pl); 
 
          dataD[ind] = Math.sqrt(dataS[ind]*dataS[ind++]*pl); 
 
          dataD[ind] = dataS[ind]; // alpha not effected 
 
         }else{ // black pixel 
 
          dataD[ind++] = 0; 
 
          dataD[ind++] = 0; 
 
          dataD[ind++] = 0; 
 
          dataD[ind] = dataS[ind]; 
 
         } 
 
        }else{ 
 
         l = Math.max(0,l); 
 
         pl = (dataS[ind]+dataS[ind+1]+dataS[ind+2])/3; 
 
         if(pl > 0){ 
 
          pl = l/pl; 
 
          dataD[ind] = dataS[ind++]*pl; 
 
          dataD[ind] = dataS[ind++]*pl; 
 
          dataD[ind] = dataS[ind++]*pl; 
 
          dataD[ind] = dataS[ind]; // alpha not effected 
 
          
 
         }else{ // black pixel 
 
          dataD[ind++] = 0; 
 
          dataD[ind++] = 0; 
 
          dataD[ind++] = 0; 
 
          dataD[ind] = dataS[ind]; 
 
         } 
 
        } 
 
       }else{ // if not edge then just copy image pixel to dest 
 
        dataD[ind] = dataS[ind++]; 
 
        dataD[ind] = dataS[ind++]; 
 
        dataD[ind] = dataS[ind++]; 
 
        dataD[ind] = dataS[ind]; 
 
       } 
 
      }else{ // set transparent pixel to zero 
 
       dataD[ind+3] = 0; 
 
      } 
 
     } 
 
    
 
    } 
 
    // all done 
 
    return true; // return success 
 
} 
 
var img = createImage(128,128); 
 
img.ctx.font = "32px arial black"; 
 
img.ctx.textAlign = "center"; 
 
img.ctx.textBaseline = "middle"; 
 
img.ctx.lineCap = "round"; 
 
img.ctx.lineJoin = "round"; 
 
img.ctx.lineWidth = 4 
 
img.ctx.strokeStyle = "#3AD"; 
 
img.ctx.fillStyle = "#334"; 
 
img.ctx.strokeText("LOGO!",64,64); 
 
img.ctx.fillText("LOGO!",64,64); 
 
ctx.drawImage(img,0,0); 
 
var img1 = createImage(128,128); 
 
var imgData = img.ctx.getImageData(0,0,128,128); 
 
var imgData1 = img1.ctx.getImageData(0,0,128,128); 
 
if(embose(imgData,imgData1,"2 pixel soft",false, true, false)){ 
 
    img1.ctx.putImageData(imgData1,0,0); 
 
    log("ONe") 
 
    ctx.drawImage(img1,128,0); 
 
} 
 
img.ctx.fillStyle = "#DA3"; // make is look better for the sell ;) 
 
img.ctx.fillText("LOGO!",64,64); 
 
var imgData = img.ctx.getImageData(0,0,128,128); 
 
var img1 = createImage(128,128); 
 
var imgData1 = img1.ctx.getImageData(0,0,128,128); 
 
if(embose(imgData,imgData1,"2 pixel",false, true, true)){ 
 
    img1.ctx.putImageData(imgData1,0,0); 
 
    ctx.drawImage(img1,0,128); 
 
} 
 

 
var img1 = createImage(128,128); 
 
var imgData1 = img1.ctx.getImageData(0,0,128,128); 
 
if(embose(imgData,imgData1,"1 pixel",false, false, false)){ 
 
    img1.ctx.putImageData(imgData1,0,0); 
 
    ctx.drawImage(img1,128,128); 
 
} 
 
.canC { 
 
    width:256px; 
 
    height:256px; 
 
}
<canvas class="canC" id="canV" width=256 height=256></canvas>