Java:基本绘图,绘制点/圆点/像素

6

我需要一个面板,在其中可以进行绘制。我希望能够逐像素绘制

注:我不需要线条/圆等其他图形。 提示:图形库并不重要,可以是awt、swing、qt等任何一个库。我只想拥有通常由Bufferedimage或类似的东西表示的东西,在其中设置单个像素的颜色,然后将其渲染到屏幕上。


不幸的是,Java 没有绘制单个点的方法,您必须使用 drawLine 并使用相同的起始点和结束点。 - Extreme Coders
1
@ExtremeCoders:这真是太遗憾了,这难道不会增加可怕的开销吗?我只想能够显示大型数据集... - Dmitry Avtonomov
6个回答

5
一种实现方式的示例:

一种实现方式的示例:

// Create the new image needed
img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB );

for ( int rc = 0; rc < height; rc++ ) {
  for ( int cc = 0; cc < width; cc++ ) {
    // Set the pixel colour of the image n.b. x = cc, y = rc
    img.setRGB(cc, rc, Color.BLACK.getRGB() );
  }//for cols
}//for rows


然后从重写的paintComponent(Graphics g)方法内部进行操作

((Graphics2D)g).drawImage(img, <args>)

谢谢,我会尝试类似的方法。你有什么建议我进一步阅读关于Java中这种类型图形的内容吗? - Dmitry Avtonomov
你可能已经知道了,但以防万一:http://docs.oracle.com/javase/tutorial/2d/index.html - Jool

2
如果您想快速完成某项任务,可以使用Graphics方法setColor和drawLine。例如:
public void paintComponent(Graphics g) {
    super.paintComponent(g);       

    // Set the colour of pixel (x=1, y=2) to black
    g.setColor(Color.BLACK);
    g.drawLine(1, 2, 1, 2);
}

我曾使用过这种技术,速度并不是很慢。我没有与使用BufferedImage对象进行比较。


2

对于此问题,我建议使用BufferedImage,它可以显示......或者你可以设置单个像素的颜色,然后将其渲染到屏幕上。

JLabel中显示-如此答案所示。

当然,一旦我们有了BufferedImage的实例,我们就可以使用setRGB(..)方法。


2
如果您需要逐像素渲染,我曾为一个研究实验室编写的热点可视化软件进行了详细的处理。
您需要使用BufferedImage.setRGB(..) -- 如果您正在逐像素绘制,则我假设您已经实现了一种算法来呈现每个像素的RGB值(就像我们在热图中所做的那样)。这是我们在旧版IE兼容的Applet中使用的。它工作得很好,并且相对快速。
不幸的是,任何时候直接操作BufferedImage中的RGB值都会使其由支持视频内存的缓存变为未缓存状态。
但是自Java 7以来,我听说底层的J2D实现将尝试重新将图像缓存到视频内存中,一旦操作停止并重复渲染--例如,在渲染热图时它不会加速,但一旦渲染完成,当您拖动窗口并使用应用程序时,后备图像数据可以重新加速。

谢谢,我正在尝试做类似的事情 - 我有一系列光谱(连续测量时间),我想将它们绘制成2D地图,根据强度着色。因此,它看起来就像热力图。但是有很多数据要绘制,我希望实现一些智能缓冲和线程加载数据的功能,以便我们可以以合理的速度进行缩放。 - Dmitry Avtonomov
@chhh - 非常欢迎!当我们实现缩放时,我们保留了每个缩放级别的最终BufferedImage(因此它们不必重新渲染),但如果分辨率太高,则会使您的HEAP崩溃,因此您可以将它们写入磁盘作为PNG并在需要时加载/重新渲染它们或者采用一些等效的技巧。对于我们来说,仍然比重新计算和重新渲染帧要快。 - Riyad Kalla

0
我提供了一种快速且与Graphics2D兼容的解决方案,它不会从分离的像素数组中绘制。
fun drawLand(area: Rectangle): BufferedImage {
    val height = area.height

    val image = BufferedImage(area.width, height, BufferedImage.TYPE_INT_ARGB)
    val g2 = image.createGraphics()
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)
    g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY)
    g2.background = Color.PINK
    g2.clearRect(area.x, area.y, area.width, area.height)

    val squares: Sequence<Square> = repo.getSquares(area)

    val pixels: IntArray = (image.raster.dataBuffer as DataBufferInt).data

    for (square in squares) {
        val color = square.color or OPAQUE // 0xFF000000.toInt()
        val base = square.location.y * height + square.location.x
        pixels[base] = color
    }

    g2.dispose()

    return image
}

说明:

在背景中,图像由一些Java原始类型的数组支持,具体取决于BufferedImage.TYPE_

这里我选择了TYPE_INT_ARGB,因此每个像素都方便地是一个Int。然后你可以逐行处理底层像素。


1
你是否想获得死灵法师徽章?在问题得到接受的答案9年后,你重新发布了相同的答案,但翻译成了Kotlin。 - Dmitry Avtonomov
我在 Kotlin 市场部工作 :) 嗯,我认为 Jimmy 的答案有所不同。但是由于屏幕类的分离和行宽过大,它有点难以理解。 - Ondra Žižka
这不是他的答案,请查看Andrew Thompson的被接受的答案(使用BufferImage和链接到另一个包含完整代码示例的答案)。 - Dmitry Avtonomov

0
有点晚了,但你可以像Java游戏程序员一样使用Screen类来实现:
public class Screen {

    private int width, height;  

    public int[] pixels;

    public Screen(int width, int height) {
        this.width = width;
        this.height = height;

        pixels = new int[width * height];
    }

    public void render() {
        for(int y = 0; y < height; y++) {
            for(int x = 0; x < width; x++) {
                pixels[x + y * width] = 0xFFFFFF; //make every pixel white
            }
        }
    }

public void clear() {
            for(int i = 0; i < pixels.length; i++) {
                    pixels[i] = 0; //make every pixel black
            }
    }

}

然后在你的主类中:

    private Screen screen;

    private BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
    private int[] pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();

    public void render() {
         BufferStrategy bs = getBufferStrategy();
        if (bs == null) {
            createBufferStrategy(3);
            return;
        }

        screen.clear();
        screen.render();

         for(int i = 0; i < pixels.length; i++) {
            pixels[i] = screen.pixels[i];
         }

        Graphics g = bs.getDrawGraphics();
        g.drawImage(image, 0, 0, getWidth(), getHeight(), null);
        g.dispose();
        bs.show();
}

应该可以,我想。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接