英文:
Efficient way to draw paint using mouse on canvas
问题
我有一个Brush
类
final class Brush {
private final int size;
private final Color color;
private final Ellipse2D.Double blob = new Ellipse2D.Double();
private Brush(int size, Color color) {
this.size = size;
this.color = color;
}
void paint(Graphics2D g2d, Point location) {
g2d.setColor(color);
blob.setFrame(location.x - (size / 2.0), location.y - (size / 2.0), size, size);
g2d.fill(blob);
}
}
我有一个Blob
类,用于跟踪用户当前的笔刷设置以及用户之前拖动鼠标的位置,以便在那里绘制一个斑点。
final class Blob {
final Brush brush;
final Point location;
private Blob(Brush brush, Point location) {
this.brush = brush;
this.location = location;
}
private void paint(Graphics2D g) {
brush.paint(g, location);
}
}
最后是我的绘制逻辑,非常简单。
每当用户拖动鼠标时,在当前位置使用当前笔刷设置添加一个斑点,并在paint()
中循环遍历所有斑点并重新绘制它们。
final class Painter extends Canvas {
private Brush brush = new Brush(5, Color.red);
private final ArrayList<Blob> blobs = new ArrayList();
private Painter() {
addMouseMotionListener(new Dragger());
}
@Override
public void paint(Graphics g) {
super.paint(g);
blobs.forEach(blob -> blob.paint(g));
}
private final class Dragger extends MouseAdapter {
@Override
public void mouseDragged(MouseEvent m) {
blobs.add(new Blob(brush, m.getPoint()));
repaint();
}
}
}
你已经可以看到问题了。列表的大小呈指数增长,我的应用程序很快变慢。有没有更高效的方法来做这个?
英文:
I have a Brush
class
final class Brush
{
private final int size;
private final Color color;
private final Ellipse2D.Double blob=new Ellipse2D.Double();
private Brush(int size,Color color)
{
this.size=size;
this.color=color;
}
void paint(Graphics2D g2d,Point location)
{
g2d.setColor(color);
blob.setFrame(location.x-(size/2.0),location.y-(size/2.0),size,size);//Translate ecllipse so that the centre of it's bounding box is exactly at the cursor location for more accurate blobs
g2d.fill(blob);
}
}
I have a Blob
class which keeps track of the user's current brush settings and where the user previously dragged his mouse so as to remember to draw a blob there.
final class Blob
{
final Brush brush;
final Point location;
private Blob(Brush brush,Point location)
{
this.brush=brush;
this.location=location;
}
private void paint(Graphics2D g){brush.paint(g,location);}
}
Finally my paint logic which is very simple.
Whenever the user drags his mouse add a blob at that current location with the current brush settings and inside paint()
loop through all blobs and redraw them.
final class Painter extends Canvas
{
private Brush brush=new Brush(5,Color.red);//Can Change
private final ArrayList<Blob> blobs=new ArrayList();
private Painter(){addMouseMotionListener(new Dragger());}
@Override
public void paint(Graphics g)
{
super.paint(g);
blobs.forEach(blob->blob.paint(g));
}
private final class Dragger extends MouseAdapter
{
@Override
public void mouseDragged(MouseEvent m)
{
blobs.add(brush,m.getPoint());
repaint();
}
}
}
You can already see the problem here. The size of the list grows exponentially and my app quickly slows down. Is there an more efficient way to do this?
答案1
得分: 2
使用BufferedImage来进行绘制,并在paintComponent中绘制BufferedImage是更高效的方式。
从PaintArea中提取的代码如下:
public void paintComponent(Graphics g) {
if (mSizeChanged) {
handleResize();
}
g.drawImage(mImg, 0, 0, null);
}
protected class MListener extends MouseAdapter implements MouseMotionListener {
Point mLastPoint;
public void mouseDragged(MouseEvent me) {
Graphics g = mImg.getGraphics();
if ((me.getModifiers() & InputEvent.BUTTON1_MASK) != 0) {
g.setColor(mColor1);
} else {
g.setColor(mColor2);
}
Point p = me.getPoint();
if (mLastPoint == null) {
g.fillOval(p.x - mBrushSize / 2, p.y - mBrushSize / 2, mBrushSize, mBrushSize);
} else {
g.drawLine(mLastPoint.x, mLastPoint.y, p.x, p.y);
double angle = MathUtils.angle(mLastPoint, p);
if (angle < 0) {
angle += 2 * Math.PI;
}
@SuppressWarnings("unused")
double distance = MathUtils.distance(mLastPoint, p) * 1.5;
if (angle < Math.PI / 4 || angle > 7 * Math.PI / 4 || Math.abs(Math.PI - angle) < Math.PI / 4) {
for (int i = 0; i < mBrushSize / 2; i ++) {
g.drawLine(mLastPoint.x, mLastPoint.y + i, p.x, p.y + i);
g.drawLine(mLastPoint.x, mLastPoint.y - i, p.x, p.y - i);
}
} else {
for (int i = 0; i < mBrushSize / 2; i ++) {
g.drawLine(mLastPoint.x + i, mLastPoint.y, p.x + i, p.y);
g.drawLine(mLastPoint.x - i, mLastPoint.y, p.x - i, p.y);
}
}
}
mLastPoint = p;
g.dispose();
repaint();
}
public void mouseMoved(MouseEvent me) {}
public void mouseReleased(MouseEvent me) {
mLastPoint = null;
}
}
英文:
The much more efficient way to do this is to use a BufferedImage for your drawing, and than painting the BufferedImage in paintComponent
Code taken from PaintArea:
public void paintComponent(Graphics g) {
if (mSizeChanged) {
handleResize();
}
g.drawImage(mImg, 0, 0, null);
}
protected class MListener extends MouseAdapter implements MouseMotionListener {
Point mLastPoint;
public void mouseDragged(MouseEvent me) {
Graphics g = mImg.getGraphics();
if ((me.getModifiers() & InputEvent.BUTTON1_MASK) != 0) {
g.setColor(mColor1);
} else {
g.setColor(mColor2);
}
Point p = me.getPoint();
if (mLastPoint == null) {
g.fillOval(p.x - mBrushSize / 2, p.y - mBrushSize / 2, mBrushSize, mBrushSize);
//g.drawLine(p.x, p.y, p.x, p.y);
}
else {
g.drawLine(mLastPoint.x, mLastPoint.y, p.x, p.y);
//g.fillOval(p.x - mBrushSize / 2, p.y - mBrushSize / 2, mBrushSize, mBrushSize);
double angle = MathUtils.angle(mLastPoint, p);
if (angle < 0) {
angle += 2 * Math.PI;
}
@SuppressWarnings("unused")
double distance = MathUtils.distance(mLastPoint, p) * 1.5;
if (angle < Math.PI / 4 || angle > 7 * Math.PI / 4 || Math.abs(Math.PI - angle) < Math.PI / 4) {
for (int i = 0; i < mBrushSize / 2; i ++) {
g.drawLine(mLastPoint.x, mLastPoint.y + i, p.x, p.y + i);
g.drawLine(mLastPoint.x, mLastPoint.y - i, p.x, p.y - i);
}
}
else {
for (int i = 0; i < mBrushSize / 2; i ++) {
g.drawLine(mLastPoint.x + i, mLastPoint.y, p.x + i, p.y);
g.drawLine(mLastPoint.x - i, mLastPoint.y, p.x - i, p.y);
}
}
}
mLastPoint = p;
g.dispose();
repaint();
}
public void mouseMoved(MouseEvent me) {}
public void mouseReleased(MouseEvent me) {
mLastPoint = null;
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论