Paintbrush stamping algorithm / technique
I am toying around with a small paint application. I want to create different Brush tips (not only simple lines). The basic idea is to repeat (stamping) the brush tip along the mouse movements. Because the mouse movements don't dispatch the required event for every pixel the mouse moves. My current approach is I use the Bresenham algorithm to geht the pixels were I want to paint and then stamp the brush tip over every pixel. This is however not very efficient, because the brush tip is for example 30x30 px. I want to stamp at 25% of the brush width but I don't know how this is done in a good way. I could detect the distance and only stamp until a distance of 25% brush tip is reaced.
Any other idea how to implement a stamping brush algorithm which considers irregular mouse events and allows the spac开发者_Python百科ing to be defined?
Thanks for reading!
A bit late in answering but if someone searches for answers here is how I implement it in my code for a project in java.
The step is the percent of the brush so if it's 20x20 brush then 25 step is 5 pixels which is the space.
I then create a normalized vector from the the last and current position of the mouse.
After the first
which is the first click.
When the dist is more then the space an iterator is made to process all the distance because sometimes the mouse can move fast so there is multiple dibs(or "stamps", dibs is the term in this domain)
iter=space-remn
is for aligning it with the previous dib.
The previous position added with the vector*iter
gets us the position for the dib.
After we draw them all comes the important bits.
remn = dist-iter+space-remn;
Remainder(remn) is collected from a previous process added to the dist(distance) from the initial stage.
To understand the math lets show an example.
brush = 30x30, step = 25%, remn=2.5, dist = 28.5(including remn), space = 7.5(30*25/100)
The next remn = 28.5(dist)-27.5(5 initially+7.5*3 times since the check(<28.5-2.5) is done after iter is updated) +7.5(space)-2.5(previous remn) = 6 pixels
So the mouse has to travel 1.5pixels for the next dib since 6 pixles are already traveled.
In the else case its even more straightforward.
dist(which already has remn added) fails the remn=dist.
For example if we have 2 pixels from last time we add the distance traveled by the mouse say 3 pixels so we need to travel an additional 2.5 pixels for the next dib.
int size =(Integer) tool.getAttribute("size");
int step = (Integer) tool.getAttribute("step");
double space = size*step/100.0f; //what is actualy need for the check algorithm for the step rate to work
double dist = Point.distance(pZero.getX(),pZero.getY(),last.getX(),last.getY());
int bleed = (int) (size/tilemap[0].getWidth()+size/tilemap[0].getHeight());
Point2D.Double vec = new Point2D.Double(pZero.getX()-last.getX(),pZero.getY()-last.getY());
vec.x /= dist;
vec.y /= dist;
dist+=remn;
if(first){
//System.out.println("First ");
for(int y=0; y < tilesHigh; ++y) {
for(int x=0; x < tilesWide; ++x) {
int pos = x+y*tilesWide;
// This should never exceed tilemap.length.
BufferedImage tile = tilemap[pos];
//tool.operate(tile.getGraphics(), new Point(pZero.x-x*tile.getWidth(), pZero.y-y*tile.getHeight()));
tool.operate(tile.getGraphics(), tilemapPointToTilePoint(pZero, pos));
}
}
first = false;
}else {
if(dist>=space){//check to see if the mouse distance is enoght for a step(space)
iter=space-remn;
//test=0;
//System.out.println("pZero="+pZero);
while(iter<dist-remn){//fills the gap between with at the rate of step(space),if you move the mouse fast you use to get those
//do stuff
pZero.x =(int) Math.round(last.x + (vec.x*iter));
pZero.y =(int) Math.round(last.y + (vec.y*iter));
//int pos = xyToIndex(pZero.x, pZero.y);
//test++;
//System.out.println("iter = "+iter+" remn="+remn+" space="+space);
//System.out.println("pIter="+pZero);
//System.out.println("Second ");
for(int y=0; y < tilesHigh; ++y) {//bleed
for(int x=0; x < tilesWide; ++x) {
int pos = x+y*tilesWide;
// This should never exceed tilemap.length.
BufferedImage tile = tilemap[pos];
//tool.operate(tile.getGraphics(), new Point(pZero.x-x*tile.getWidth(), pZero.y-y*tile.getHeight()));
tool.operate(tile.getGraphics(), tilemapPointToTilePoint(pZero, pos));
}
}
iter += space;
}
//System.out.println("last = "+last);
//System.out.println("test="+test);
remn = dist-iter+space-remn;
}else remn = dist;
}
The Brasenham is an approximation algorithm, since you are already approximating instead of aliasing, you could go that way further...
With a slight modification of the algorithm where you get the brush width as a parameter (or better yet, the print distance):
- threshold is the print distance * vector orientation multiplier (45,135,225,315 degrees = sqrt(2))
- instead of drawing Pixels at X,Y -> count pixels
- when pixelCount >= threshold print brush at location X,Y and reset pixelCount
Use a float for the counter, and if pixels are not square then add a different amount for each deltaY or deltaX you step through, and take into account the vector orientation from x1,y1 to x2,y2 to get the real distance to add for each pixel.
Pretty simple modification...
精彩评论