使用Java有效地对图像进行颜色循环 [英] Efficiently color cycling an image in Java

查看:119
本文介绍了使用Java有效地对图像进行颜色循环的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在写一个Mandelbrot分形观察器,我想以聪明的方式实现色彩循环。给定一个图像,我想修改它的IndexColorModel。



据我所知,没有办法修改IndexColorModel,也没办法给出图像一个新的IndexColorModel。事实上,我认为无法提取其颜色模型或图像数据。



似乎唯一的解决方案是保持原始图像数据和调色板用于创建图像,手动创建具有旋转颜色的新调色板,创建新的IndexColorModel,然后从数据和新颜色模型创建一个全新的图像。



<这看起来好像太多了。是否有更简单快捷的方式?



这是我能想到的最佳解决方案。此代码创建一个1000x1000像素的图像,并显示以每秒约30帧的速度循环的颜色动画。



(旧)

  import java.awt。*; 
import java.awt.event。*;
import java.awt.image。*;
import javax.swing。*;

公共类ColorCycler {

public static void main(String [] args){
SwingUtilities.invokeLater(new Runnable(){
public void run(){
createAndShowGUI();
}
});
}

private static void createAndShowGUI(){
JFrame jFrame = new JFrame(Color Cycler);
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.add(new MyPanel());
jFrame.pack();
jFrame.setVisible(true);
}

}

类MyPanel扩展JPanel实现ActionListener {

private byte [] reds = new byte [216];
private byte [] greens = new byte [216];
private byte [] blues = new byte [216];
private final byte [] imageData = new byte [1000 * 1000];
私人图片图片;

public MyPanel(){
generateColors();
generateImageData();
(new Timer(35,this))。start();
}

//窗口大小为1000x1000像素。
public Dimension getPreferredSize(){
return new Dimension(1000,1000);
}

//为颜色模型生成216种独特颜色。
private void generateColors(){
int index = 0;
for(int i = 0; i< 6; i ++){
for(int j = 0; j< 6; j ++){
for(int k = 0; k < 6; k ++,index ++){
reds [index] =(byte)(i * 51);
greens [index] =(byte)(j * 51);
布鲁斯[指数] =(字节)(k * 51);
}
}
}
}

//为MemoryImageSource创建图像数据。
//这个数据创建一次,永远不会改变。
private void generateImageData(){
for(int i = 0; i< 1000 * 1000; i ++){
imageData [i] =(byte)(i%216);
}
}

//绘制图像。
protected void paintComponent(Graphics g){
super.paintComponent(g);
g.drawImage(image,0,0,1000,1000,null);
}

//定时器每隔35 ms调用一次此方法。
//它创建要绘制的修改后的图像。
@Override
public void actionPerformed(ActionEvent e){//由Timer调用。
reds = cycleColors(reds);
greens = cycleColors(greens);
blues = cycleColors(蓝调);
IndexColorModel colorModel = new IndexColorModel(8,216,红色,绿色,蓝色);
image = createImage(new MemoryImageSource(1000,1000,colorModel,imageData,0,1000));
repaint();
}

//将颜色循环到右边1.
private byte [] cycleColors(byte [] colors){
byte [] newColors = new字节[216];
newColors [0] = colors [215];
System.arraycopy(colors,0,newColors,1,215);
返回newColors;
}
}






编辑2:



现在我预先计算IndexColorModels。这意味着在每个帧上我只需要使用新的IndexColorModel更新MemoryImageSource。这似乎是最好的解决方案。



(我也注意到在我的分形探测器中,我可以在我生成的每个图像上重复使用单组预先计算的IndexColorModel。这意味着140K的一次性成本让我可以实时地对所有内容进行颜色循环。这很棒。)



以下是代码:

  import java.awt。*; 
import java.awt.event。*;
import java.awt.image。*;
import javax.swing。*;

公共类ColorCycler {

public static void main(String [] args){
SwingUtilities.invokeLater(new Runnable(){
public void run(){
createAndShowGUI();
}
});
}

private static void createAndShowGUI(){
JFrame jFrame = new JFrame(Color Cycler);
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.add(new MyPanel());
jFrame.pack();
jFrame.setVisible(true);
}

}

类MyPanel扩展JPanel实现ActionListener {

private final IndexColorModel [] colorModels = new IndexColorModel [216];
private final byte [] imageData = new byte [1000 * 1000];
private final MemoryImageSource imageSource;
私人最终图片图片;
private int currentFrame = 0;

public MyPanel(){
generateColorModels();
generateImageData();
imageSource = new MemoryImageSource(1000,1000,colorModels [0],imageData,0,1000);
imageSource.setAnimated(true);
image = createImage(imageSource);
(new Timer(35,this))。start();
}

//窗口大小为1000x1000像素。
public Dimension getPreferredSize(){
return new Dimension(1000,1000);
}

//生成216种独特的颜色模型,每帧一个。
private void generateColorModels(){
byte [] reds = new byte [216];
byte [] greens = new byte [216];
byte [] blues = new byte [216];
int index = 0;
for(int i = 0; i< 6; i ++){
for(int j = 0; j< 6; j ++){
for(int k = 0; k < 6; k ++,index ++){
reds [index] =(byte)(i * 51);
greens [index] =(byte)(j * 51);
布鲁斯[指数] =(字节)(k * 51);
}
}
}
for(int i = 0; i< 216; i ++){
colorModels [i] = new IndexColorModel(8,216,红色,绿色,蓝色);
reds = cycleColors(reds);
greens = cycleColors(greens);
blues = cycleColors(蓝调);
}
}

//为MemoryImageSource创建图像数据。
//这个数据创建一次,永远不会改变。
private void generateImageData(){
for(int i = 0; i< 1000 * 1000; i ++){
imageData [i] =(byte)(i%216);
}
}

//绘制图像。
protected void paintComponent(Graphics g){
super.paintComponent(g);
g.drawImage(image,0,0,1000,1000,null);
}

//定时器每隔35 ms调用一次此方法。
//它更新要绘制的图像的ImageSource。
@Override
public void actionPerformed(ActionEvent e){//由Timer调用。
currentFrame ++;
if(currentFrame == 216){
currentFrame = 0;
}
imageSource.newPixels(imageData,colorModels [currentFrame],0,1000);
repaint();
}

//将颜色循环到右边1.
private byte [] cycleColors(byte [] colors){
byte [] newColors = new字节[216];
newColors [0] = colors [215];
System.arraycopy(colors,0,newColors,1,215);
返回newColors;
}
}






编辑:(旧)



Heisenbug建议我使用MemoryImageSource的newPixels()方法。答案已被删除,但事实证明这是一个好主意。现在我只创建一个MemoryImageSource和一个Image。在每个帧上,我创建一个新的IndexColorModel并更新MemoryImageSource。



这是更新的代码: (旧)

  import java.awt。*; 
import java.awt.event。*;
import java.awt.image。*;
import javax.swing。*;

公共类ColorCycler {

public static void main(String [] args){
SwingUtilities.invokeLater(new Runnable(){
public void run(){
createAndShowGUI();
}
});
}

private static void createAndShowGUI(){
JFrame jFrame = new JFrame(Color Cycler);
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.add(new MyPanel());
jFrame.pack();
jFrame.setVisible(true);
}

}

类MyPanel扩展JPanel实现ActionListener {

private byte [] reds = new byte [216];
private byte [] greens = new byte [216];
private byte [] blues = new byte [216];
private final byte [] imageData = new byte [1000 * 1000];
private final MemoryImageSource imageSource;
私人最终图片图片;

public MyPanel(){
generateColors();
generateImageData();
IndexColorModel colorModel = new IndexColorModel(8,216,红色,绿色,蓝色);
imageSource = new MemoryImageSource(1000,1000,colorModel,imageData,0,1000);
imageSource.setAnimated(true);
image = createImage(imageSource);
(new Timer(35,this))。start();
}

//窗口大小为1000x1000像素。
public Dimension getPreferredSize(){
return new Dimension(1000,1000);
}

//为颜色模型生成216种独特颜色。
private void generateColors(){
int index = 0;
for(int i = 0; i< 6; i ++){
for(int j = 0; j< 6; j ++){
for(int k = 0; k < 6; k ++,index ++){
reds [index] =(byte)(i * 51);
greens [index] =(byte)(j * 51);
布鲁斯[指数] =(字节)(k * 51);
}
}
}
}

//为MemoryImageSource创建图像数据。
//这个数据创建一次,永远不会改变。
private void generateImageData(){
for(int i = 0; i< 1000 * 1000; i ++){
imageData [i] =(byte)(i%216);
}
}

//绘制图像。
protected void paintComponent(Graphics g){
super.paintComponent(g);
g.drawImage(image,0,0,1000,1000,null);
}

//定时器每隔35 ms调用一次此方法。
//它更新要绘制的图像的ImageSource。
@Override
public void actionPerformed(ActionEvent e){//由Timer调用。
reds = cycleColors(reds);
greens = cycleColors(greens);
blues = cycleColors(蓝调);
IndexColorModel colorModel = new IndexColorModel(8,216,红色,绿色,蓝色);
imageSource.newPixels(imageData,colorModel,0,1000);
repaint();
}

//将颜色循环到右边1.
private byte [] cycleColors(byte [] colors){
byte [] newColors = new字节[216];
newColors [0] = colors [215];
System.arraycopy(colors,0,newColors,1,215);
返回newColors;
}
}


解决方案

In除了预先计算周期之外,正如@Thomas评论一样,将神奇数字1000分解出来。以下是 更改BufferedImage的ColorModel



附录:保理幻数将允许您在分析时可靠地更改它们,这是查看您是否正在取得进展所必需的。



附录:虽然我每帧建议三个颜色查找表,但你想要预先计算 IndexColorModel 实例的想法更好。作为数组的替代方案,请考虑 Queue< IndexColorModel> LinkedList< IndexColorModel> 作为具体实现。这简化了模型旋转,如下所示。

  @Override 
public void actionPerformed(ActionEvent e){//由Timer调用。
imageSource.newPixels(imageData,models.peek(),0,N);
models.add(models.remove());
repaint();
}

附录:动态更改颜色模型和显示时间的另一种变体。



  import java.awt。*; 
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.IndexColorModel;
import java.awt.image.MemoryImageSource;
import java.util.LinkedList;
import java.util.Queue;
import javax.swing。*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

/ ** @see http://stackoverflow.com/questions/7546025 * /
公共类ColorCycler {

public static void main(String [] args){
SwingUtilities.invokeLater(new Runnable(){

@Override
public void run(){
new ColorCycler()。create();
}
});
}

private void create(){
JFrame jFrame = new JFrame(Color Cycler);
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final ColorPanel cp = new ColorPanel();
JPanel control = new JPanel();
final JSpinner s = new JSpinner(
new SpinnerNumberModel(cp.colorCount,2,256,1));
s.addChangeListener(new ChangeListener(){

@Override
public void stateChanged(ChangeEvent e){
cp.setColorCount(((Integer)s.getValue ())。intValue());
}
});
control.add(new JLabel(Shades:));
control.add(s);
jFrame.add(cp,BorderLayout.CENTER);
jFrame.add(control,BorderLayout.SOUTH);
jFrame.pack();
jFrame.setLocationRelativeTo(null);
jFrame.setVisible(true);
}

私有静态类ColorPanel扩展JPanel实现ActionListener {

private static final int WIDE = 256;
private static final int PERIOD = 40; // ~25 Hz
私人最终队列< IndexColorModel> models =
new LinkedList< IndexColorModel>();
private final MemoryImageSource imageSource;
private final byte [] imageData = new byte [WIDE * WIDE];
私人最终图片图片;
private int colorCount = 128;

public ColorPanel(){
generateColorModels();
generateImageData();
imageSource = new MemoryImageSource(
WIDE,WIDE,models.peek(),imageData,0,WIDE);
imageSource.setAnimated(true);
image = createImage(imageSource);
(新的计时器(PERIOD,this))。start();
}

//首选大小为NxN像素。
@Override
public Dimension getPreferredSize(){
return new Dimension(WIDE,WIDE);
}

public void setColorCount(int colorCount){
this.colorCount = colorCount;
generateColorModels();
generateImageData();
repaint();
}

//生成MODEL_SIZE独特的颜色模型。
private void generateColorModels(){
byte [] reds = new byte [colorCount];
byte [] greens = new byte [colorCount];
byte [] blues = new byte [colorCount];
for(int i = 0; i< colorCount; i ++){
reds [i] =(byte)(i * 256 / colorCount);
greens [i] =(byte)(i * 256 / colorCount);
blues [i] =(byte)(i * 256 / colorCount);
}
models.clear();
for(int i = 0; i< colorCount; i ++){
reds = rotateColors(reds);
greens = rotateColors(greens);
blues = rotateColors(blues);
models.add(新的IndexColorModel(
8,colorCount,reds,greens,blues));
}
}

//向右旋转颜色一个。
private byte [] rotateColors(byte [] colors){
byte [] newColors = new byte [colors.length];
newColors [0] = colors [colors.length - 1];
System.arraycopy(colors,0,newColors,1,colors.length - 1);
返回newColors;
}

//为MemoryImageSource创建一些数据。
private void generateImageData(){
for(int i = 0; i< imageData.length; i ++){
imageData [i] =(byte)(i%colorCount);
}
}

//绘制图像。
@Override
protected void paintComponent(Graphics g){
super.paintComponent(g);
long start = System.nanoTime();
imageSource.newPixels(imageData,models.peek(),0,WIDE);
models.add(models.remove());
double delta =(System.nanoTime() - start)/ 1000000d;
g.drawImage(image,0,0,getWidth(),getHeight(),null);
g.drawString(String.format(%1 $ 5.3f,delta),5,15);
}

//每个PERIOD ms由定时器调用。
@Override
public void actionPerformed(ActionEvent e){//由Timer调用。
repaint();
}
}
}


I'm writing a Mandelbrot fractal viewer, and I would like to implement color cycling in a smart way. Given an image, I would like to modify its IndexColorModel.

As far as I can tell, there's no way to modify an IndexColorModel, and there's no way to give an image a new IndexColorModel. In fact, I think there's no way to extract its color model or image data.

It seems that the only solution is to hold on to the raw image data and color palette that were used to create the image, manually create a new palette with the rotated colors, create a new IndexColorModel, then create a whole new image from the data and new color model.

This all seems like too much work. Is there an easier and faster way?

Here's the best solution I can come up with. This code creates a 1000x1000 pixel image and shows an animation of the colors cycling at about 30 frames per second.

(old)

import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import javax.swing.*;

public class ColorCycler {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createAndShowGUI();
            }
        });
    }

    private static void createAndShowGUI() {
        JFrame jFrame = new JFrame("Color Cycler");
        jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        jFrame.add(new MyPanel());
        jFrame.pack();
        jFrame.setVisible(true);
    }

}

class MyPanel extends JPanel implements ActionListener {

    private byte[] reds = new byte[216];
    private byte[] greens = new byte[216];
    private byte[] blues = new byte[216];
    private final byte[] imageData = new byte[1000 * 1000];
    private Image image;

    public MyPanel() {
        generateColors();
        generateImageData();
        (new Timer(35, this)).start();
    }

    // The window size is 1000x1000 pixels.
    public Dimension getPreferredSize() {
        return new Dimension(1000, 1000);
    }

    // Generate 216 unique colors for the color model.
    private void generateColors() {
        int index = 0;
        for (int i = 0; i < 6; i++) {
            for (int j = 0; j < 6; j++) {
                for (int k = 0; k < 6; k++, index++) {
                    reds[index] = (byte) (i * 51);
                    greens[index] = (byte) (j * 51);
                    blues[index] = (byte) (k * 51);
                }
            }
        }
    }

    // Create the image data for the MemoryImageSource.
    // This data is created once and never changed.
    private void generateImageData() {
        for (int i = 0; i < 1000 * 1000; i++) {
            imageData[i] = (byte) (i % 216);
        }
    }

    // Draw the image.
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.drawImage(image, 0, 0, 1000, 1000, null);
    }

    // This method is called by the timer every 35 ms.
    // It creates the modified image to be drawn.
    @Override
    public void actionPerformed(ActionEvent e) { // Called by Timer.
        reds = cycleColors(reds);
        greens = cycleColors(greens);
        blues = cycleColors(blues);
        IndexColorModel colorModel = new IndexColorModel(8, 216, reds, greens, blues);
        image = createImage(new MemoryImageSource(1000, 1000, colorModel, imageData, 0, 1000));
        repaint();
    }

    // Cycle the colors to the right by 1.
    private byte[] cycleColors(byte[] colors) {
        byte[] newColors = new byte[216];
        newColors[0] = colors[215];
        System.arraycopy(colors, 0, newColors, 1, 215);
        return newColors;
    }
}


Edit 2:

Now I precompute the IndexColorModels. This means that on each frame I only need to update the MemoryImageSource with a new IndexColorModel. This seems like the best solution.

(I also just noticed that in my fractal explorer, I can reuse the single set of precomputed IndexColorModels on every image I generate. That means the one-time cost of 140K lets me color cycle everything in real-time. This is great.)

Here's the code:

import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import javax.swing.*;

public class ColorCycler {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createAndShowGUI();
            }
        });
    }

    private static void createAndShowGUI() {
        JFrame jFrame = new JFrame("Color Cycler");
        jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        jFrame.add(new MyPanel());
        jFrame.pack();
        jFrame.setVisible(true);
    }

}

class MyPanel extends JPanel implements ActionListener {

    private final IndexColorModel[] colorModels = new IndexColorModel[216];
    private final byte[] imageData = new byte[1000 * 1000];
    private final MemoryImageSource imageSource;
    private final Image image;
    private int currentFrame = 0;

    public MyPanel() {
        generateColorModels();
        generateImageData();
        imageSource = new MemoryImageSource(1000, 1000, colorModels[0], imageData, 0, 1000);
        imageSource.setAnimated(true);
        image = createImage(imageSource);
        (new Timer(35, this)).start();
    }

    // The window size is 1000x1000 pixels.
    public Dimension getPreferredSize() {
        return new Dimension(1000, 1000);
    }

    // Generate 216 unique colors models, one for each frame.
    private void generateColorModels() {
        byte[] reds = new byte[216];
        byte[] greens = new byte[216];
        byte[] blues = new byte[216];
        int index = 0;
        for (int i = 0; i < 6; i++) {
            for (int j = 0; j < 6; j++) {
                for (int k = 0; k < 6; k++, index++) {
                    reds[index] = (byte) (i * 51);
                    greens[index] = (byte) (j * 51);
                    blues[index] = (byte) (k * 51);
                }
            }
        }
        for (int i = 0; i < 216; i++) {
            colorModels[i] = new IndexColorModel(8, 216, reds, greens, blues);
            reds = cycleColors(reds);
            greens = cycleColors(greens);
            blues = cycleColors(blues);
        }
    }

    // Create the image data for the MemoryImageSource.
    // This data is created once and never changed.
    private void generateImageData() {
        for (int i = 0; i < 1000 * 1000; i++) {
            imageData[i] = (byte) (i % 216);
        }
    }

    // Draw the image.
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.drawImage(image, 0, 0, 1000, 1000, null);
    }

    // This method is called by the timer every 35 ms.
    // It updates the ImageSource of the image to be drawn.
    @Override
    public void actionPerformed(ActionEvent e) { // Called by Timer.
        currentFrame++;
        if (currentFrame == 216) {
            currentFrame = 0;
        }
        imageSource.newPixels(imageData, colorModels[currentFrame], 0, 1000);
        repaint();
    }

    // Cycle the colors to the right by 1.
    private byte[] cycleColors(byte[] colors) {
        byte[] newColors = new byte[216];
        newColors[0] = colors[215];
        System.arraycopy(colors, 0, newColors, 1, 215);
        return newColors;
    }
}


Edit: (old)

Heisenbug suggested that I use the newPixels() method of MemoryImageSource. The answer has since been deleted, but it turned out to be a good idea. Now I only create one MemoryImageSource and one Image. On each frame I create a new IndexColorModel and update the MemoryImageSource.

Here's the updated code: (old)

import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import javax.swing.*;

public class ColorCycler {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createAndShowGUI();
            }
        });
    }

    private static void createAndShowGUI() {
        JFrame jFrame = new JFrame("Color Cycler");
        jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        jFrame.add(new MyPanel());
        jFrame.pack();
        jFrame.setVisible(true);
    }

}

class MyPanel extends JPanel implements ActionListener {

    private byte[] reds = new byte[216];
    private byte[] greens = new byte[216];
    private byte[] blues = new byte[216];
    private final byte[] imageData = new byte[1000 * 1000];
    private final MemoryImageSource imageSource;
    private final Image image;

    public MyPanel() {
        generateColors();
        generateImageData();
        IndexColorModel colorModel = new IndexColorModel(8, 216, reds, greens, blues);
        imageSource = new MemoryImageSource(1000, 1000, colorModel, imageData, 0, 1000);
        imageSource.setAnimated(true);
        image = createImage(imageSource);
        (new Timer(35, this)).start();
    }

    // The window size is 1000x1000 pixels.
    public Dimension getPreferredSize() {
        return new Dimension(1000, 1000);
    }

    // Generate 216 unique colors for the color model.
    private void generateColors() {
        int index = 0;
        for (int i = 0; i < 6; i++) {
            for (int j = 0; j < 6; j++) {
                for (int k = 0; k < 6; k++, index++) {
                    reds[index] = (byte) (i * 51);
                    greens[index] = (byte) (j * 51);
                    blues[index] = (byte) (k * 51);
                }
            }
        }
    }

    // Create the image data for the MemoryImageSource.
    // This data is created once and never changed.
    private void generateImageData() {
        for (int i = 0; i < 1000 * 1000; i++) {
            imageData[i] = (byte) (i % 216);
        }
    }

    // Draw the image.
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.drawImage(image, 0, 0, 1000, 1000, null);
    }

    // This method is called by the timer every 35 ms.
    // It updates the ImageSource of the image to be drawn.
    @Override
    public void actionPerformed(ActionEvent e) { // Called by Timer.
        reds = cycleColors(reds);
        greens = cycleColors(greens);
        blues = cycleColors(blues);
        IndexColorModel colorModel = new IndexColorModel(8, 216, reds, greens, blues);
        imageSource.newPixels(imageData, colorModel, 0, 1000);
        repaint();
    }

    // Cycle the colors to the right by 1.
    private byte[] cycleColors(byte[] colors) {
        byte[] newColors = new byte[216];
        newColors[0] = colors[215];
        System.arraycopy(colors, 0, newColors, 1, 215);
        return newColors;
    }
}

解决方案

In addition to pre-computing the cycles, as @Thomas comments, factor out the magic number 1000. Here's a related example of Changing the ColorModel of a BufferedImage and a project you may like.

Addendum: Factoring out magic numbers will allow you to change them reliably while profiling, which is required to see if you're making progress.

Addendum: While I suggested three color lookup tables per frame, your idea to pre-compute IndexColorModel instances is even better. As an alternative to an array, consider a Queue<IndexColorModel>, with LinkedList<IndexColorModel> as a concrete implementation. This simplifies your model rotation as shown below.

@Override
public void actionPerformed(ActionEvent e) { // Called by Timer.
    imageSource.newPixels(imageData, models.peek(), 0, N);
    models.add(models.remove());
    repaint();
}

Addendum: One more variation to dynamically change the color models and display timing.

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.IndexColorModel;
import java.awt.image.MemoryImageSource;
import java.util.LinkedList;
import java.util.Queue;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

/** @see http://stackoverflow.com/questions/7546025 */
public class ColorCycler {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                new ColorCycler().create();
            }
        });
    }

    private void create() {
        JFrame jFrame = new JFrame("Color Cycler");
        jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        final ColorPanel cp = new ColorPanel();
        JPanel control = new JPanel();
        final JSpinner s = new JSpinner(
            new SpinnerNumberModel(cp.colorCount, 2, 256, 1));
        s.addChangeListener(new ChangeListener() {

            @Override
            public void stateChanged(ChangeEvent e) {
                cp.setColorCount(((Integer) s.getValue()).intValue());
            }
        });
        control.add(new JLabel("Shades:"));
        control.add(s);
        jFrame.add(cp, BorderLayout.CENTER);
        jFrame.add(control, BorderLayout.SOUTH);
        jFrame.pack();
        jFrame.setLocationRelativeTo(null);
        jFrame.setVisible(true);
    }

    private static class ColorPanel extends JPanel implements ActionListener {

        private static final int WIDE = 256;
        private static final int PERIOD = 40; // ~25 Hz
        private final Queue<IndexColorModel> models =
            new LinkedList<IndexColorModel>();
        private final MemoryImageSource imageSource;
        private final byte[] imageData = new byte[WIDE * WIDE];
        private final Image image;
        private int colorCount = 128;

        public ColorPanel() {
            generateColorModels();
            generateImageData();
            imageSource = new MemoryImageSource(
                WIDE, WIDE, models.peek(), imageData, 0, WIDE);
            imageSource.setAnimated(true);
            image = createImage(imageSource);
            (new Timer(PERIOD, this)).start();
        }

        // The preferred size is NxN pixels.
        @Override
        public Dimension getPreferredSize() {
            return new Dimension(WIDE, WIDE);
        }

        public void setColorCount(int colorCount) {
            this.colorCount = colorCount;
            generateColorModels();
            generateImageData();
            repaint();
        }

        // Generate MODEL_SIZE unique color models.
        private void generateColorModels() {
            byte[] reds = new byte[colorCount];
            byte[] greens = new byte[colorCount];
            byte[] blues = new byte[colorCount];
            for (int i = 0; i < colorCount; i++) {
                reds[i] = (byte) (i * 256 / colorCount);
                greens[i] = (byte) (i * 256 / colorCount);
                blues[i] = (byte) (i * 256 / colorCount);
            }
            models.clear();
            for (int i = 0; i < colorCount; i++) {
                reds = rotateColors(reds);
                greens = rotateColors(greens);
                blues = rotateColors(blues);
                models.add(new IndexColorModel(
                    8, colorCount, reds, greens, blues));
            }
        }

        // Rotate colors to the right by one.
        private byte[] rotateColors(byte[] colors) {
            byte[] newColors = new byte[colors.length];
            newColors[0] = colors[colors.length - 1];
            System.arraycopy(colors, 0, newColors, 1, colors.length - 1);
            return newColors;
        }

        // Create some data for the MemoryImageSource.
        private void generateImageData() {
            for (int i = 0; i < imageData.length; i++) {
                imageData[i] = (byte) (i % colorCount);
            }
        }

        // Draw the image.
        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            long start = System.nanoTime();
            imageSource.newPixels(imageData, models.peek(), 0, WIDE);
            models.add(models.remove());
            double delta = (System.nanoTime() - start) / 1000000d;
            g.drawImage(image, 0, 0, getWidth(), getHeight(), null);
            g.drawString(String.format("%1$5.3f", delta), 5, 15);
        }

        // Called by the Timer every PERIOD ms.
        @Override
        public void actionPerformed(ActionEvent e) { // Called by Timer.
            repaint();
        }
    }
}

这篇关于使用Java有效地对图像进行颜色循环的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆