Javascript 绘制算法在着色时在边缘保留白色像素

Javascript 绘制算法在着色时在边缘保留白色像素,javascript,jquery,algorithm,paint,Javascript,Jquery,Algorithm,Paint,我正在创建一个绘图应用程序。当我选择一种非常深的颜色并在熊猫的脸上画画时,边缘是白色的。我希望我的代码为边界内的整个区域着色。这是最新的。这些是我在javascript中使用的函数。我怎样才能让他们变得更好 function matchOutlineColor(r, g, b, a) { return (r + g + b < 75 && a >= 50); }; function matchStartColor(pixelPos, startR, sta

我正在创建一个绘图应用程序。当我选择一种非常深的颜色并在熊猫的脸上画画时,边缘是白色的。我希望我的代码为边界内的整个区域着色。这是最新的。这些是我在javascript中使用的函数。我怎样才能让他们变得更好

function matchOutlineColor(r, g, b, a) {

    return (r + g + b < 75 && a >= 50);
};

function matchStartColor(pixelPos, startR, startG, startB) {
    var r = outlineLayerData.data[pixelPos],
        g = outlineLayerData.data[pixelPos + 1],
        b = outlineLayerData.data[pixelPos + 2],
        a = outlineLayerData.data[pixelPos + 3];

    // If current pixel of the outline image is black
    if (matchOutlineColor(r, g, b, a)) {
        return false;
    }

    r = colorLayerData.data[pixelPos];
    g = colorLayerData.data[pixelPos + 1];
    b = colorLayerData.data[pixelPos + 2];

    // If the current pixel matches the clicked color
    if (r === startR && g === startG && b === startB) {
        return true;
    }

    // If current pixel matches the new color
    if (r === curColor.r && g === curColor.g && b === curColor.b) {
        return false;
    }

    return true;
};

function colorPixel(pixelPos, r, g, b, a) {
    colorLayerData.data[pixelPos] = r;
    colorLayerData.data[pixelPos + 1] = g;
    colorLayerData.data[pixelPos + 2] = b;
    colorLayerData.data[pixelPos + 3] = a !== undefined ? a : 255;
};

function floodFill(startX, startY, startR, startG, startB) {

    document.getElementById('color-lib-1').style.display = "none";
    document.getElementById('color-lib-2').style.display = "none";
    document.getElementById('color-lib-3').style.display = "none";
    document.getElementById('color-lib-4').style.display = "none";
    document.getElementById('color-lib-5').style.display = "none";

    change = false;

    var newPos,
        x,
        y,
        pixelPos,
        reachLeft,
        reachRight,
        drawingBoundLeft = drawingAreaX,
        drawingBoundTop = drawingAreaY,
        drawingBoundRight = drawingAreaX + drawingAreaWidth - 1,
        drawingBoundBottom = drawingAreaY + drawingAreaHeight - 1,
        pixelStack = [
            [startX, startY]
        ];

    while (pixelStack.length) {

        newPos = pixelStack.pop();
        x = newPos[0];
        y = newPos[1];

        // Get current pixel position
        pixelPos = (y * canvasWidth + x) * 4;

        // Go up as long as the color matches and are inside the canvas
        while (y >= drawingBoundTop && matchStartColor(pixelPos, startR, startG, startB)) {
            y -= 1;
            pixelPos -= canvasWidth * 4;
        }

        pixelPos += canvasWidth * 4;
        y += 1;
        reachLeft = false;
        reachRight = false;

        // Go down as long as the color matches and in inside the canvas
        while (y <= drawingBoundBottom && matchStartColor(pixelPos, startR, startG, startB)) {
            y += 1;

            colorPixel(pixelPos, curColor.r, curColor.g, curColor.b);

            if (x > drawingBoundLeft) {
                if (matchStartColor(pixelPos - 4, startR, startG, startB)) {
                    if (!reachLeft) {
                        // Add pixel to stack
                        pixelStack.push([x - 1, y]);
                        reachLeft = true;
                    }

                } else if (reachLeft) {
                    reachLeft = false;
                }
            }

            if (x < drawingBoundRight) {
                if (matchStartColor(pixelPos + 4, startR, startG, startB)) {
                    if (!reachRight) {
                        // Add pixel to stack
                        pixelStack.push([x + 1, y]);
                        reachRight = true;
                    }
                } else if (reachRight) {
                    reachRight = false;
                }
            }

            pixelPos += canvasWidth * 4;
        }
    }
};

// Start painting with paint bucket tool starting from pixel specified by startX and startY
function paintAt(startX, startY) {

    var pixelPos = (startY * canvasWidth + startX) * 4,
        r = colorLayerData.data[pixelPos],
        g = colorLayerData.data[pixelPos + 1],
        b = colorLayerData.data[pixelPos + 2],
        a = colorLayerData.data[pixelPos + 3];

    if (r === curColor.r && g === curColor.g && b === curColor.b) {
        // Return because trying to fill with the same color
        return;
    }

    if (matchOutlineColor(r, g, b, a)) {
        // Return because clicked outline
        return;
    }

    floodFill(startX, startY, r, g, b);

    redraw();
};
函数匹配大纲颜色(r、g、b、a){
回报率(r+g+b<75&&a>=50);
};
功能匹配StartColor(像素位置、startR、startG、startB){
var r=outlineLayerData.data[pixelPos],
g=大纲视图图层数据[pixelPos+1],
b=大纲视图图层数据[pixelPos+2],
a=大纲视图图层数据[pixelPos+3];
//如果轮廓图像的当前像素为黑色
if(匹配大纲颜色(r、g、b、a)){
返回false;
}
r=colorLayerData.data[pixelPos];
g=colorLayerData.data[pixelPos+1];
b=colorLayerData.数据[pixelPos+2];
//如果当前像素与单击的颜色匹配
如果(r==startR&&g==startG&&b==startB){
返回true;
}
//如果当前像素与新颜色匹配
if(r==curColor.r&&g==curColor.g&&b==curColor.b){
返回false;
}
返回true;
};
函数colorPixel(pixelPos、r、g、b、a){
colorLayerData.data[pixelPos]=r;
colorLayerData.data[pixelPos+1]=g;
colorLayerData.data[pixelPos+2]=b;
colorLayerData.data[pixelPos+3]=a!==未定义?a:255;
};
功能注水(startX、startY、startR、startG、startB){
document.getElementById('color-lib-1').style.display=“无”;
document.getElementById('color-lib-2').style.display=“无”;
document.getElementById('color-lib-3').style.display=“无”;
document.getElementById('color-lib-4').style.display=“无”;
document.getElementById('color-lib-5').style.display=“无”;
改变=错误;
var newPos,
x,,
Y
pixelPos,
到达,
重新认识,
drawingBoundLeft=drawingAreaX,
drawingBoundTop=drawingAreaY,
drawingBoundRight=drawingAreaX+DrawingAreWidth-1,
drawingBoundBottom=drawingAreaY+DrawingAreahight-1,
像素堆栈=[
[startX,startY]
];
while(pixelStack.length){
newPos=pixelStack.pop();
x=newPos[0];
y=新位置[1];
//获取当前像素位置
pixelPos=(y*画布宽度+x)*4;
//只要颜色匹配并且在画布内,就可以向上移动
while(y>=drawingBoundTop和匹配StartColor(像素位置、startR、startG、startB)){
y-=1;
pixelPos-=画布宽度*4;
}
pixelPos+=画布宽度*4;
y+=1;
reachLeft=false;
重新考虑=错误;
//只要颜色匹配,就在画布内向下移动
while(y drawingBoundLeft){
if(匹配StartColor(像素位置-4、startR、startG、startB)){
如果(!reachLeft){
//将像素添加到堆栈
pixelStack.push([x-1,y]);
reachLeft=true;
}
}else if(reachLeft){
reachLeft=false;
}
}
如果(x
您正在对抖动/平滑/抗锯齿/有损压缩图像使用精确的颜色填充。这会使颜色稍有不同的像素(边框)不着色,从而产生瑕疵。有两种简单的补救方法:

  • 匹配相近的颜色而不是精确的颜色

    如果您现在有两种颜色(r0、g0、b0)和(r1、g1、b1)(如果我看对了),您的检查如下:

    if ((r0!=r1)&&(g0!=g1)&&(b0!=b1)) colors_does_not_match;
    
    您应该这样做:

    if (((r0-r1)*(r0-r1))+((g0-g1)*(g0-g1))+((b0-b1)*(b0-b1))>treshold) colors_does_not_match;
    
    其中
    treshold
    是填充的最大允许距离^2常量。如果使用的treshold太低,工件将保留。如果使用太高的树梢,那么填充物也会出血,使附近的颜色相似

    如果你想拥有更具视觉鲁棒性的东西,那么你可以

  • 使用封闭式边框填充

    它基本上与整体填充相同,但可以重新填充不包含边框颜色的所有像素,而不是仅包含起始颜色的像素。这将填充整个区域,但在边界中会有一些瑕疵(会有点刺耳),但不会像您当前的方法那样在视觉上令人不快

  • 我有时使用颜色均匀性填充

    这是一个很好的编辑照片的颜色是阴影由于照明或任何。因此,我填充颜色,就像泛光填充一样,但如果颜色与大树状体的起始位置不太远,与小树状体的最后填充位置不太远,则考虑颜色匹配
    //---------------------------------------------------------------------------
    // variables
    //---------------------------------------------------------------------------
    Graphics::TBitmap *bmp=NULL;            // your image
    union color { DWORD dd; BYTE db[4]; };  // direct RGB channel access
    DWORD **pixel=NULL;                     // direct 32bit pixel access
    int xs=0,ys=0;                          // image resolution
    int mx=0,my=0;                          // mouse position
    int treshold=50000;                     // color match treshold
    //---------------------------------------------------------------------------
    // Color compare with treshold
    //---------------------------------------------------------------------------
    bool color_equal(DWORD c0,DWORD c1)
        {
        static color c;
        static int r,g,b;
        c.dd=c0&0x00FFFFFF;     // mask out alpha channel just to be sure
        r=c.db[0];              // r0,g0,b0
        g=c.db[1];
        b=c.db[2];
        c.dd=c1&0x00FFFFFF;
        r-=c.db[0]; r*=r;       // (r0-r1)^2,(g0-g1)^2,(b0-b1)^2
        g-=c.db[1]; g*=g;
        b-=c.db[2]; b*=b;
        return (r+g+b<=treshold);
        }
    //---------------------------------------------------------------------------
    bool color_nonequal(DWORD c0,DWORD c1)
        {
        static color c;
        static int r,g,b;
        c.dd=c0&0x00FFFFFF;     // mask out alpha channel just to be sure
        r=c.db[0];              // r0,g0,b0
        g=c.db[1];
        b=c.db[2];
        c.dd=c1&0x00FFFFFF;
        r-=c.db[0]; r*=r;       // (r0-r1)^2,(g0-g1)^2,(b0-b1)^2
        g-=c.db[1]; g*=g;
        b-=c.db[2]; b*=b;
        return (r+g+b>treshold);
        }
    //---------------------------------------------------------------------------
    // Flood fill segmented by lines
    //---------------------------------------------------------------------------
    struct _segment
        {
        int x0,x1,y;
        int done;
        _segment(){}; _segment(_segment& a){ *this=a; }; ~_segment(){}; _segment* operator = (const _segment *a) { *this=*a; return this; }; /*_segment* operator = (const _segment &a) { ...copy... return this; };*/
        };
    void floodfill_segmented(int x,int y,DWORD col) // This is the main call for flood fill (x,y) start position and col is fill color
        {
        // init variables
        int i,j,k,e,ee,x0,x1;
        _segment s,*p;
        List<_segment> seg;     // H-line segments
        List< List<int> > idx;  // index table seg[idx[y]].y=y to speed up searches
        DWORD col0=pixel[y][x]&0x00FFFFFF;
        DWORD col1=col        &0x00FFFFFF;
        // sanity check
        if (color_equal(col0,col1)) return;
        // prepare segment table and macros
        seg.allocate(ys<<3); seg.num=0;
        idx.allocate(ys   ); idx.num=ys; for (i=0;i<ys;i++) idx.dat[i].num=0;
        // add new segment at (x,y)
        // scan the line to enlarge it as much as can
        // merge with already added segment instead of adding new one if they overlap
        // ee=1 if change has been made
        #define _seg_add                                                                 \
            {                                                                            \
            s.x0=x; s.x1=x; s.y=y; s.done=0;                                             \
            for (x=s.x0;x>=0;x--) if (color_equal(col0,pixel[y][x])) s.x0=x; else break; \
            for (x=s.x1;x<xs;x++) if (color_equal(col0,pixel[y][x])) s.x1=x; else break; \
            for (ee=0,k=0;k<idx.dat[s.y].num;k++)                                        \
                {                                                                        \
                j=idx.dat[s.y].dat[k]; p=seg.dat+j;                                      \
                if ((p->x0>=s.x0)&&(p->x0<=s.x1)) ee=1;                                  \
                if ((p->x1>=s.x0)&&(p->x1<=s.x1)) ee=1;                                  \
                if ((p->x0<=s.x0)&&(p->x1>=s.x0)) ee=1;                                  \
                if ((p->x0<=s.x1)&&(p->x1>=s.x1)) ee=1;                                  \
                if (ee)                                                                  \
                    {                                                                    \
                    if (p->x0>s.x0) { p->done=0; p->x0=s.x0; }                           \
                    if (p->x1<s.x1) { p->done=0; p->x1=s.x1; }                           \
                    s=*p;                                                                \
                    break;                                                               \
                    }                                                                    \
                }                                                                        \
            if (ee) ee=p->done; else { idx.dat[s.y].add(seg.num); seg.add(s); }          \
            }
        // first segment;
        _seg_add;
        for (e=1;e;)
            {
            // add new adjacent segments
            for (e=0,p=seg.dat,i=0;i<seg.num;i++,p++)
             if (!p->done)
                {
                p->done=1;
                y=p->y-1; if (y>=0) for (x=p->x0;x<=p->x1;x++) if (color_equal(col0,pixel[y][x])) { _seg_add; e|=!ee; x=s.x1; p=seg.dat+i; }
                y=p->y+1; if (y<ys) for (x=p->x0;x<=p->x1;x++) if (color_equal(col0,pixel[y][x])) { _seg_add; e|=!ee; x=s.x1; p=seg.dat+i; }
                }
            }
        #undef seg_add
        // recolor
        for (p=seg.dat,i=0;i<seg.num;i++,p++)
         for (x=p->x0,y=p->y;x<=p->x1;x++)
          pixel[y][x]=col;
        }
    //---------------------------------------------------------------------------