Java-апплет: есть ли простой способ рисовать и стирать в двухслойной 2D-сцене? - PullRequest
3 голосов
/ 12 сентября 2011

У меня есть апплет, который отображает некоторые данные, используя круги и линии.Поскольку данные постоянно меняются, отображение обновляется, что означает, что иногда круги и линии необходимо стирать, поэтому я просто рисую их белым (мой фоновый цвет), чтобы стереть их.(Их много, поэтому стирание всего, а затем перерасчет и перерисовка всего, кроме стертого элемента, будет ужасно медленным способом удаления одного элемента.)

Логика ситуации заключается в том, что существует дваслои, которые должны быть отображены, и мне нужно иметь возможность стереть объект в одном слое, не затрагивая другой слой.Я полагаю, что верхний слой должен иметь цвет фона «прозрачный», но как тогда стереть объект, поскольку рисование прозрачным цветом не имеет никакого эффекта.

Что отличает эту ситуацию от всей прозрачностив Интернете мне нужна помощь, чтобы иметь возможность стирать линии и круги по очереди с прозрачного слоя, перезаписывая их пиксели «полностью прозрачным» цветом.

В настоящее время мой апплет рисует (используя только один слой), выполнив это в start ():

    screenBuffer = createImage(640, 480);
    screenBufferGraphics = screenBuffer.getGraphics();

и это в paint ():

    g.drawImage(screenBuffer, 0, 0, this);

и объекты отображаются (или «стираются» при рисованиибелым цветом) такими командами, как:

    screenBufferGraphics.drawLine(x1,y1,x2,y2);

Легко ли как-нибудь создать второй экранный буфер с прозрачным фоном, а затем иметь возможность рисовать и стирать объекты в этом буфере и отображать его поверх первого буфера?

Ответы [ 2 ]

2 голосов
/ 13 сентября 2011

Это кажется довольно быстрым, пока область изображения отрисовывается остается около 640x480, код может достигать 125-165 FPS.Код отслеживает 2000 полупрозрачных линий шириной 4 пикселя и перемещает их в области, в 8 раз превышающей размер визуализированного изображения.

Bouncing Lines

import java.awt.image.BufferedImage;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.*;
import javax.swing.*;
import java.util.Random;

class LineAnimator {

    public static void main(String[] args) {
        final int w = 640;
        final int h = 480;
        final RenderingHints hints = new RenderingHints(
            RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON
            );
        hints.put(
            RenderingHints.KEY_ALPHA_INTERPOLATION,
            RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY
            );
        final BufferedImage bi = new BufferedImage(w,h, BufferedImage.TYPE_INT_RGB);
        final JLabel l = new JLabel(new ImageIcon(bi));
        final BouncingLine[] lines = new BouncingLine[20000];
        for (int ii=0; ii<lines.length; ii++) {
            lines[ii] = new BouncingLine(w*8,h*8);
        }
        final Font font = new Font("Arial", Font.BOLD, 30);
        ActionListener al = new ActionListener() {

            int count = 0;
            long lastTime;
            String fps = "";

            public void actionPerformed(ActionEvent ae) {
                count++;
                Graphics2D g = bi.createGraphics();
                g.setRenderingHints(hints);

                g.clearRect(0,0,w,h);
                for (int ii=0; ii<lines.length; ii++) {
                    lines[ii].move();
                    lines[ii].paint(g);
                }

                if ( System.currentTimeMillis()-lastTime>1000 ) {
                    lastTime = System.currentTimeMillis();
                    fps = count + " FPS";
                    count = 0;
                }
                g.setColor(Color.YELLOW);
                g.setFont(font);
                g.drawString(fps,10,30);

                l.repaint();
                g.dispose();
            }
        };
        Timer timer = new Timer(1,al);
        timer.start();

        JOptionPane.showMessageDialog(null, l);
        System.exit(0);
    }
}

class BouncingLine {
    private final Color color;
    private static final BasicStroke stroke = new BasicStroke(4);
    private static final Random random = new Random();
    Line2D line;
    int w;
    int h;
    int x1;
    int y1;
    int x2;
    int y2;

    BouncingLine(int w, int h) {
        line = new Line2D.Double(random.nextInt(w),random.nextInt(h),random.nextInt(w),random.nextInt(h));
        this.w = w;
        this.h = h;
        this.color = new Color(
            128+random.nextInt(127),
            128+random.nextInt(127),
            128+random.nextInt(127),
            85
            );
        x1 = (random.nextBoolean() ? 1 : -1);
        y1 = (random.nextBoolean() ? 1 : -1);
        x2 = -x1;
        y2 = -y1;
    }

    public void move() {
        int tx1 = 0;
        if (line.getX1()+x1>0 && line.getX1()+x1<w) {
            tx1 = (int)line.getX1()+x1;
        } else {
            x1 = -x1;
            tx1 = (int)line.getX1()+x1;
        }
        int ty1 = 0;
        if (line.getY1()+y1>0 && line.getY1()+y1<h) {
            ty1 = (int)line.getY1()+y1;
        } else {
            y1 = -y1;
            ty1 = (int)line.getY1()+y1;
        }
        int tx2 = 0;
        if (line.getX2()+x2>0 && line.getX2()+x2<w) {
            tx2 = (int)line.getX2()+x2;
        } else {
            x2 = -x2;
            tx2 = (int)line.getX2()+x2;
        }
        int ty2 = 0;
        if (line.getY2()+y2>0 && line.getY2()+y2<h) {
            ty2 = (int)line.getY2()+y2;
        } else {
            y2 = -y2;
            ty2 = (int)line.getY2()+y2;
        }
        line.setLine(tx1,ty1,tx2,ty2);
    }

    public void paint(Graphics g) {
        Graphics2D g2 = (Graphics2D)g;
        g2.setColor(color);
        g2.setStroke(stroke);
        //line.set
        g2.draw(line);
    }
}

Обновление 1

Когда я опубликовал этот код, я думал, что вы сказали 100-1000, а не 1000-100000!При 20000 строк скорость падает примерно до 16-18 FPS.

Обновление 2

.. Возможен ли этот оптимизированный подход с использованием слоев в Java?

Конечно.Я использую эту технику в DukeBox - которая показывает причудливый сюжет звука, который он играет.Он хранит несколько буферизованных изображений.

  1. Фон.Сплошной цвет на непрозрачном изображении.
  2. Старые следы.Более старые звуки прослеживаются как растянутые или исчезнувшие с исходных позиций.Имеет прозрачность, чтобы показать BG.
  3. Последняя трассировка.Нарисовано поверх двух других.Имеет прозрачность.
0 голосов
/ 14 сентября 2011

После дня отсутствия предлагаемых решений я начал думать, что Java Graphics не может стереть отдельные элементы обратно в прозрачный цвет.Но оказывается, что улучшенная Graphics2D, вместе с BufferedImage и AlphaComposite, обеспечивают в значительной степени именно ту функциональность, которую я искал, позволяя мне как рисовать фигуры, так и стирать фигуры (обратно до полной прозрачности) в различных слоях.

Теперь я делаю в start () следующее:

    screenBuffer = new BufferedImage(640, 480, BufferedImage.TYPE_INT_ARGB);
    screenBufferGraphics = screenBuffer.createGraphics();

    overlayBuffer = new BufferedImage(640, 480, BufferedImage.TYPE_INT_ARGB);
    overlayBufferGraphics = overlayBuffer.createGraphics();

Я должен использовать new BufferedImage() вместо createImage(), потому что мне нужно запросить альфа.(Даже для screenBuffer, хотя это фон - иди на фигуру!) Я использую createGraphics() вместо getGraphics() только потому, что моя переменная screenBufferGraphics теперь является объектом Graphics2D, а не просто объектом Graphics.(Хотя приведение вперед и назад тоже работает нормально.)

Код в paint () почти не отличается:

        g.drawImage(screenBuffer, 0, 0, null);
        g.drawImage(overlayBuffer, 0, 0, null);

И объекты отображаются (или стираются) следующим образом:

// render to background
    screenBufferGraphics.setColor(Color.red);
    screenBufferGraphics.fillOval(80,80, 40,40);
// render to overlay
    overlayBufferGraphics.setComposite(AlphaComposite.SrcOver);
    overlayBufferGraphics.setColor(Color.green);
    overlayBufferGraphics.fillOval(90,70, 20,60);
// render invisibility onto overlay
    overlayBufferGraphics.setComposite(AlphaComposite.DstOut);
    overlayBufferGraphics.setColor(Color.blue);
    overlayBufferGraphics.fillOval(70,90, 30,20);
// and flush just this locally changed region
    repaint(60,60, 80,80);

Конечный Color.blue дает прозрачность, а не синеву - это может быть любой цвет, который не имеет прозрачности.

В качестве заключительного замечания, если вы выполняете рендеринг в потоке, отличном от AWT- Поток EventQueue (которым вы, вероятно, являетесь, если вы тратите много времени на рендеринг, но также должны иметь адаптивный интерфейс), тогда вы захотите синхронизировать приведенный выше код в paint () с вашей процедурой рендеринга;в противном случае дисплей может оказаться в полусрисованном состоянии.

Если вы выполняете рендеринг в более чем одном потоке, вам все равно нужно будет синхронизировать процедуру рендеринга, чтобы изменения состояния Graphics2D не мешали друг другу.,(Или, может быть, каждый поток может иметь свой собственный объект Graphics2D, рисующий на одном и том же BufferedImage - я этого не пробовал.)

Это выглядит так просто, что трудно поверить, сколько времени понадобилось, чтобы понять, каксделай это!

...