Where is this border coming from?

edited September 2015 in Questions about Code

After looking at the type of paint stroke that is made with simply using line ( pmouseX, pmouseY, mouseX,mouseY) when the mouse is dragged, the problem seems to be the spacing. Its erratic and ugly. I found this code on how to blit a brush tip with even spacing : http://losingfight.com/blog/2007/08/18/how-to-implement-a-basic-bitmap-brush/

It makes a much nicer paint stroke. But I've run into a problem. I'm getting a border on the ellipse even though I've set nostroke() while creating the brush tip. It looks fine at low trans, but at high trans you can see a black border around the ellipse. Where is that coming from? The brush spacing and transparency displays in the title bar. Use the key '1' to decrease the spacing and use key '2' to increase the spacing. Use '+' to increase the transparency and use '-' to decrease transparency. Space clears the screen:

PGraphics test;
PGraphics Brush;
PGraphics BrushMask;
color paintColor = color(255,0,0);
int oldx, oldy;
float Brushspacing = 0.10;
int Brushtrans = 05;
float leftoverD = 0.0;
String Info;

void setup(){
     size(600,600);

     test = createGraphics(600,600);
     test.beginDraw(); 
     test.background(255);
     test.endDraw();
     BrushMask = createGraphics(27,27);
     BrushMask.beginDraw();
     BrushMask.noStroke();
     BrushMask.fill(Brushtrans);
     BrushMask.ellipse(13,13,25,25);
     BrushMask.endDraw();
     Brush =createGraphics(27,27);
     Brush.beginDraw();
     Brush.noSmooth();
     Brush.noStroke();
     Brush.fill(paintColor);
     Brush.ellipse(13,13,25,25);     
     Brush.mask(BrushMask);
     Brush.endDraw();
     Info = "Brush Spacing:" + Brushspacing + " BrushTraans:" +Brushtrans;
     frame.setTitle(Info);  

}
void draw(){
     image(test,0,0); 

}
void mousePressed(){
     oldx = mouseX - 13;
     oldy = mouseY - 13;
     test.beginDraw();
     test.image(Brush,oldx,oldy);
     test.endDraw();
}
void mouseDragged(){
     float spacing = 25 * Brushspacing;
     if ( spacing < 0.5 )
          spacing = 0.5;
     float deltaX = mouseX - oldx;
     float deltaY = mouseY - oldy;
     float distance = sqrt(deltaX * deltaX + deltaY * deltaY);
     float stepX = 0.0;
     float stepY = 0.0;
     if(distance > 0.0){
          float invertDistance = 1.0/distance;
          stepX = deltaX * invertDistance;
          stepY = deltaY * invertDistance;
     }
     float offsetX = 0.0;
     float offsetY = 0.0;
     float totalDistance = leftoverD + distance;
     test.beginDraw();
     while ( totalDistance >= spacing ) {
          // Increment where we put the stamp
          if ( leftoverD > 0 ) {
               // If we're making up distance we didn't cover the last
               //     time we drew a line, take that into account when calculating
               //     the offset. leftOverDistance is always < spacing.
               offsetX += stepX * (spacing - leftoverD);
               offsetY += stepY * (spacing - leftoverD);

               leftoverD -= spacing;
          } else {
               // The normal case. The offset increment is the normalized vector
               //     times the spacing
               offsetX += stepX * spacing;
               offsetY += stepY * spacing;
          }

          // Calculate where to put the current stamp at.
          test.image(Brush, oldx + offsetX, oldy + offsetY);

          // Ka-chunk! Draw the image at the current location
    //

          // Remove the distance we just covered
          totalDistance -= spacing;
     }  
     test.endDraw();

     leftoverD = totalDistance;
     oldx= mouseX;
     oldy = mouseY;
}void mouseReleased(){
     leftoverD = 0;
}
void keyPressed(){
     if (key == '1'){
          Brushspacing -= 0.05;
          if(Brushspacing < 0.05)
               Brushspacing = 0.05;
     }
     else if (key == '2')
          Brushspacing += 0.05;
     else if (key == '+'){
          Brushtrans += 5;
          if( Brushtrans > 255) Brushtrans = 255;
          BrushMask.beginDraw();
          BrushMask.clear();
          BrushMask.noStroke();
          BrushMask.fill(Brushtrans);
          BrushMask.ellipse(13,13,25,25);
          BrushMask.endDraw();
          Brush.beginDraw();
          Brush.mask(BrushMask);
          Brush.endDraw();
     }
     else if ( key == '-'){
          Brushtrans  -= 5;
          if(Brushtrans < 5) Brushtrans = 5;
          BrushMask.beginDraw();
          BrushMask.clear();
          BrushMask.noStroke();
          BrushMask.fill(Brushtrans);
          BrushMask.ellipse(13,13,25,25);
          BrushMask.endDraw();
          Brush.beginDraw();
          Brush.mask(BrushMask);
          Brush.endDraw();
     }
     else if ( key == ' '){
          test.beginDraw();
          test.background(255);
          test.endDraw();
     }
     Info = "Brush Spacing:" + Brushspacing + " BrushTraans:" +Brushtrans;
     frame.setTitle(Info);     

}     
Tagged:

Answers

  • Answer ✓

    Without running the code I'd say anti-aliasing

  • Wouldn't noSmooth() stop it?

  • That worked. In 3.05 you call noSmooth() right after size(). That fixed the problem.

  • edited September 2015 Answer ✓

    Just in case anyone experiences the same problem. You don't have to disable anti-aliasing to fix this.

    The black border is appearing because your brush-PGraphics object is initially black. Although these black pixels are invisible (fully transparent) Processing's blending calculations do take them into account. So in order to fix this blending issue you'd just have to fill your PGraphics object with fully transparent pixels in your brush color. Just add the following code before rendering the ellipse onto your brush canvas.

    brush.blendMode(REPLACE);
    brush.background(brushColor & 0xffffff); // 0xffffff is just bitmask to remove any alpha information (make the color invisible)
    brush.blendMode(BLEND);
    

    Here is your sketch with fixed blending (and a few other optimizations):

    PGraphics canvas;
    PGraphics brush;
    int brushColor = color(255, 0, 0);
    int oldX, oldXY;
    float brushSpacing = 0.10;
    int brushOpacity = 40;
    float leftoverD = 0.0;
    
    void setup() {
        size(600, 600);
        canvas = createGraphics(600, 600);
        canvas.beginDraw(); 
        canvas.background(255);
        canvas.endDraw();
        brush = createGraphics(27, 27);
        updateBrush();
        updateInfo();
    }
    
    void draw() {
        image(canvas, 0, 0);
    }
    
    void mousePressed() {
        oldX = mouseX - 13;
        oldXY = mouseY - 13;
        canvas.beginDraw();
        canvas.image(brush, oldX, oldXY);
        canvas.endDraw();
    }
    
    void mouseDragged() {
        float spacing = 25 * brushSpacing;
        if(spacing < 0.5)
            spacing = 0.5;
        float deltaX = mouseX - oldX, deltaY = mouseY - oldXY;
        float distance = sqrt(deltaX * deltaX + deltaY * deltaY);
        float stepX = 0.0, stepY = 0.0;
        if(distance > 0.0) {
            float invertDistance = 1.0 / distance;
            stepX = deltaX * invertDistance;
            stepY = deltaY * invertDistance;
        }
        float offsetX = 0.0, offsetY = 0.0, totalDistance = leftoverD + distance;
        canvas.beginDraw();
        while(totalDistance >= spacing) {
            if(leftoverD > 0) {
                offsetX += stepX * (spacing - leftoverD);
                offsetY += stepY * (spacing - leftoverD);
    
                leftoverD -= spacing;
            } else {
                offsetX += stepX * spacing;
                offsetY += stepY * spacing;
            }
            canvas.image(brush, oldX + offsetX, oldXY + offsetY);
            totalDistance -= spacing;
        }  
        canvas.endDraw();
        leftoverD = totalDistance;
        oldX= mouseX;
        oldXY = mouseY;
    }
    
    void mouseReleased() {
        leftoverD = 0;
    }
    
    void updateInfo() {
        surface.setTitle("Spacing: " + brushSpacing + " Opacity: " + brushOpacity);
    }
    
    void updateBrush() {
        brush = createGraphics(27, 27);
        brush.beginDraw();
    
        // Fill the background in the same color as the brush but with 0 alpha (completely transparent)
        brush.blendMode(REPLACE);
        brush.background(brushColor & 0xffffff); // 0xffffff is just bitmask to remove any alpha information (make the color invisible)
        brush.blendMode(BLEND);
    
        brush.noStroke();
        brush.fill(brushColor, brushOpacity);
        brush.blendMode(REPLACE);
        brush.ellipse(13, 13, 25, 25);     
        brush.endDraw();
    }
    
    void keyPressed() {
        switch(key) {
        case '1':
            brushSpacing = max(0.05, brushSpacing - 0.05);
            break;
        case '2':
            brushSpacing += 0.05;
            break;
        case '-':
            brushOpacity = max(5, brushOpacity - 5);
            updateBrush();
            break;
        case '+':
            brushOpacity = min(255, brushOpacity + 5);
            updateBrush();
            break;
        case ' ':
            canvas.beginDraw();
            canvas.background(255);
            canvas.endDraw();
        }
        updateInfo();
    } 
    
  • Thanks for that. Thats something to keep in mind as I can see that popping up in other areas.

Sign In or Register to comment.