自定义JPanel组件与消失显示部分

时间:2017-03-08 13:35:58

标签: java swing jpanel

我正在为制作游戏或应用程序构建自定义ASCII终端java显示。

我目前在Swing重绘处理方面遇到问题。视图的某些部分是绘画,但经过一段时间后,它们会从屏幕上消失。当您点击屏幕上的任何位置时,会很快发生这种情况。

错误的一个例子: Error Screen Capture

我读了Painting in AWT and Swing,但我找不到解决方案。

这是AsciiPanel州的当前状态。

package ui;

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.awt.image.FilteredImageSource;
import java.awt.image.LookupOp;
import java.awt.image.LookupTable;
import java.awt.image.ShortLookupTable;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.imageio.ImageIO;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.RepaintManager;

/**
 * JPanel with a ASCII render system
 * 
 * @author julien MAITRE
 *
 */
public class AsciiPanel extends JPanel {
    private Dimension size;
    private BufferedImage[] character;
    private Color defaultCharacterColor;
    private Color defaultCharacterBackgroundColor;
    private Dimension characterSize;
    private AsciiTerminalDataCell[][] terminal;
    private AsciiTerminalDataCell[][] oldTerminal;
    private Image image;
    private Graphics2D graphics;
    private int scale;

    public AsciiPanel(Dimension dimension, String tilesetFile, int characterWidth, int characterHeight) {
        this(dimension, tilesetFile, characterWidth, characterHeight, 1);
    }

    public AsciiPanel(Dimension dimension, String tilesetFile, int characterWidth, int characterHeight, int scale) {
        this.size = dimension;
        this.characterSize = new Dimension(characterWidth, characterHeight);
        this.scale = scale;
        this.defaultCharacterColor = Color.WHITE;
        this.defaultCharacterBackgroundColor = Color.BLACK;

        terminal = new AsciiTerminalDataCell[size.height][size.width];
        oldTerminal = new AsciiTerminalDataCell[size.height][size.width];
        for(int i = 0; i < size.height; i++){
            for(int j = 0; j < size.width; j++){
                terminal[i][j] = new AsciiTerminalDataCell();
                oldTerminal[i][j] = new AsciiTerminalDataCell();
            }
        }

        this.setPreferredSize(new Dimension(size.width*characterSize.width*scale, size.height*characterSize.height*scale));

        try {
            character = new  BufferedImage[256];
            BufferedImage tilesets = ImageIO.read(getClass().getResource(tilesetFile));

            // Recuperation of the background color
            BufferedImage imageBackgroundColor = tilesets.getSubimage(0, 0, 1, 1);
            int color = imageBackgroundColor.getRGB(0, 0);
            Color m_characterBackgroundColor = Color.getColor(null, color);

            // Modification of characters background
            Image characterBackgroundColorModified = createImage(new FilteredImageSource(tilesets.getSource(), new AsciiBackgroundFilter(m_characterBackgroundColor)));

            // Creation of tileset with a modification of the background color
            BufferedImage tilesetsModified = new BufferedImage(tilesets.getWidth(), tilesets.getHeight(), BufferedImage.TYPE_INT_ARGB);
            Graphics graphicsTilesetsModified = tilesetsModified.getGraphics();
            graphicsTilesetsModified.setColor(Color.BLACK);
            graphicsTilesetsModified.fillRect(0, 0, tilesetsModified.getWidth(), tilesetsModified.getHeight());
            // Draw in a BufferedImage for characters recuperation
            graphicsTilesetsModified.drawImage(characterBackgroundColorModified, 0, 0, this);

            for(int i = 0; i < 256; i++){
                int x = (i%16)*characterSize.width;
                int y = (i/16)*characterSize.height;
                character[i] = new BufferedImage(characterSize.width, characterSize.height, BufferedImage.TYPE_INT_ARGB);
                character[i].getGraphics().drawImage(tilesetsModified, 0, 0, characterSize.width, characterSize.height, x, y, x+characterSize.width, y+characterSize.height, this);
            }
        }
        catch (IOException ex) {
            Logger.getLogger(AsciiTerminal.class.getName()).log(Level.SEVERE, null, ex);
        }

        this.setLayout(null);
    }

    public void write(int positionX, int positionY, char character, Color characterColor){
        this.write(positionX, positionY, character, characterColor, defaultCharacterBackgroundColor);
    }

    public void write(int positionX, int positionY, AsciiTerminalDataCell character){
        this.write(positionX, positionY, character.data, character.dataColor, character.backgroundColor);
    }

    public void write(int positionX, int positionY, char character, Color characterColor, Color characterBackgroundColor){
        if(positionX < 0 || positionX > size.width - 1){
            throw new IllegalArgumentException("X position between [0 and "+size.width+"]");
        }
        if(positionY < 0 || positionY > size.height - 1){
            throw new IllegalArgumentException("Y position between [0 and "+size.height+"]");
        }

        terminal[positionY][positionX].data = character;
        terminal[positionY][positionX].dataColor = characterColor;
        terminal[positionY][positionX].backgroundColor = characterBackgroundColor;
    }

    public void writeString(int positionX, int positionY, String string, Color characterColor){
        writeString(positionX, positionY, string, characterColor, defaultCharacterBackgroundColor);
    }

    public void writeString(int positionX, int positionY, String string, Color characterColor, Color characterBackgroundColor){
        for(char c : string.toCharArray()){
            this.write(positionX, positionY, c, characterColor, characterBackgroundColor);
            positionX++;
        }
    }

    public AsciiTerminalDataCell readCurrent(int x, int y){
        return this.oldTerminal[y][x];
    }

    public AsciiTerminalDataCell readNext(int x, int y){
        return this.terminal[y][x];
    }

    public void clear(){
        clear(0, 0, size.width, size.height);
    }

    public void clear(int x, int y, int width, int height){
        if(x < 0 || x > size.width - 1){
            throw new IllegalArgumentException("X position between [0 and "+(size.width-1)+"]");
        }
        if(y < 0 || y > size.height - 1){
            throw new IllegalArgumentException("Y position between [0 and "+(size.height-1)+"]");
        }
        if(width < 1){
            throw new IllegalArgumentException("Width under 1");
        }
        if(height < 1){
            throw new IllegalArgumentException("Height under 1");
        }
        if(width+x > size.width || height+y > size.height){
            throw new IllegalArgumentException("Clear over the terminal");
        }
        for(int i = y; i < y + height; i++){
            for(int j = x; j < x + width; j++) {
                write(j, i, (char)0, defaultCharacterColor, defaultCharacterBackgroundColor);
            }
        }
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        if(image == null) {
            image = this.createImage(this.getPreferredSize().width, this.getPreferredSize().height);
            graphics = (Graphics2D)image.getGraphics();
            graphics.setColor(defaultCharacterBackgroundColor);
            graphics.fillRect(0, 0, this.getWidth(), this.getHeight());
        }

        for(Component component : getComponents()) {
            component.paint(graphics);
        }

        for(int i = 0; i < size.height; i++){
            for(int j = 0; j < size.width; j++){
                if(     terminal[i][j].data == oldTerminal[i][j].data &&
                        terminal[i][j].dataColor.equals(oldTerminal[i][j].dataColor) &&
                        terminal[i][j].backgroundColor.equals(oldTerminal[i][j].backgroundColor)) {
                    continue;
                }

                LookupOp lookupOp = setColorCharacter(terminal[i][j].backgroundColor, terminal[i][j].dataColor);
                graphics.drawImage(lookupOp.filter(character[terminal[i][j].data], null), j*characterSize.width*scale, i*characterSize.height*scale, characterSize.width*scale, characterSize.height*scale, this);

                oldTerminal[i][j].data = terminal[i][j].data;
                oldTerminal[i][j].dataColor = terminal[i][j].dataColor;
                oldTerminal[i][j].backgroundColor = terminal[i][j].backgroundColor;
            }
        }
        g.drawImage(image, 0, 0, this);
    }

    private LookupOp setColorCharacter(Color bgColor, Color fgColor){
        short[] red = new short[256];
        short[] green = new short[256];
        short[] blue = new short[256];
        short[] alpha = new short[256];

        // Recuperation of compound colors of foreground character color
        short dcr = (short) fgColor.getRed();
        short dcg = (short) fgColor.getGreen();
        short dcb = (short) fgColor.getBlue();

        // Recuperation of compound colors of background character color
        short bgr = (short) bgColor.getRed();
        short bgg = (short) bgColor.getGreen();
        short bgb = (short) bgColor.getBlue();

        for(short j = 0; j < 256; j++){
            // if is foreground color
            if(j != 0){

                /**
                 * Calculation of j*255/dcr .
                 * Cross product
                 * dcr = 180     255
                 *   j =  ?       X
                 * Distribute the requested color [0 to 255] on the character color [0 to X]
                 */
                // Red
                if(dcr != 0){
                    red[j] = (short)(j*dcr/255);
                }
                else{
                    red[j] = 0;
                }

                // green
                if(dcg != 0){
                    green[j] = (short)(j*dcg/255);
                }
                else{
                    green[j] = 0;
                }

                // Blue
                if( dcb != 0){
                    blue[j] = (short)(j*dcb/255);
                }
                else{
                    blue[j] = 0;
                }

                // Alpha
                alpha[j] = 255;
            }
            // else is background color
            else {
                red[j] = bgr;
                green[j] = bgg;
                blue[j] = bgb;
                alpha[j] = 255;
            }
        }

        short[][] data = new short[][]{red, green, blue, alpha};
        LookupTable lookupTable = new ShortLookupTable(0, data);
        LookupOp lookupOp = new LookupOp(lookupTable, null);
        return lookupOp;
    }

    public Color getDefaultCharacterColor(){
        return this.defaultCharacterColor;
    }

    public void setDefaultCharacterColor(Color color){
        this.defaultCharacterColor = color;
    }

    public Color getDefaultCharacterBackgroundColor() {
        return defaultCharacterBackgroundColor;
    }

    public void setDefaultCharacterBackgroundColor(Color defaultCharacterBackgroundColor) {
        this.defaultCharacterBackgroundColor = defaultCharacterBackgroundColor;
    }

    public Dimension getCharacterSize() {
        return characterSize;
    }

    public int getScale() {
        return scale;
    }

    public AsciiTerminalDataCell getCell(int x, int y) {
        return terminal[y][x];
    }
}

ChiptuneTracker主要课程:

package main;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.net.URISyntaxException;

import javax.imageio.ImageIO;
import javax.swing.JOptionPane;

import ui.AsciiPanel;
import ui.AsciiTerminalDataCell;
import ui.CustomAsciiTerminal;

public class ChiptuneTracker {
    public static final String TITLE = "ChiptuneTracker";
    public static final int WINDOW_WIDTH = 29;
    public static final int WINDOW_HEIGHT = 18;
    public static final String TILESET_FILE = "/assets/wanderlust.png";
    public static final String ICON_FILE = "/assets/icon.png";
    public static final int CHARACTER_WIDTH = 12;
    public static final int CHARACTER_HEIGHT = 12;
    public static final int TARGET_FPS = 60;
    public static final long OPTIMAL_TIME = 1000000000 / TARGET_FPS;
    public static final int SCALE = 3;
    public static final boolean CUSTOM_WINDOW = true;

    private static ChiptuneTracker instance = new ChiptuneTracker();

    private CustomAsciiTerminal asciiTerminal;
    private AsciiPanel asciiPanel;

    private boolean initSampleView = true;
    private boolean initPatternView = true;

    private Data data = new Data();
    private DataManager dataManager;
    private boolean changeData = false;
    private Chanels chanels = new Chanels();

    private View currentView;
    private MenuView menuView;
    private SampleView sampleView;
    private PatternView patternView;

    private ChiptuneTracker() {
        asciiTerminal = new CustomAsciiTerminal(TITLE, new Dimension(WINDOW_WIDTH, WINDOW_HEIGHT), TILESET_FILE, CHARACTER_WIDTH, CHARACTER_HEIGHT, SCALE, ICON_FILE, CUSTOM_WINDOW);
        asciiPanel = asciiTerminal.getAsciiPanel();
        asciiPanel.setDefaultCharacterBackgroundColor(Color.DARK_GRAY);
        asciiPanel.setDefaultCharacterColor(Color.WHITE);
        dataManager = new DataManager();
    }

    public void init() {
        menuView = new MenuView(this);
        sampleView = new SampleView(this);
        patternView = new PatternView(this);
        changeView(sampleView);
    }

    public void run() {
        long lastLoopTime = System.nanoTime();
        boolean stopRender = true;

        while(true) {
            long now = System.nanoTime();
            double updateLength = now - lastLoopTime;
            lastLoopTime = now;
            double delta = updateLength / ChiptuneTracker.OPTIMAL_TIME;

            // Screenshot
            KeyEvent event = asciiTerminal.getEvent();
            if(event != null) {
                if(event.getKeyCode() == KeyEvent.VK_F12) {
                    try {
                        BufferedImage image = new BufferedImage(WINDOW_WIDTH * CHARACTER_WIDTH * SCALE, WINDOW_HEIGHT * CHARACTER_HEIGHT * SCALE, BufferedImage.TYPE_INT_RGB);
                        Graphics2D graphics = image.createGraphics();
                        asciiPanel.paint(graphics);

                        boolean save = false;
                        int i = 0;
                        do {
                            File file = new File("screenshot-" + i + ".png");
                            if(!file.exists()) {
                                ImageIO.write(image, "PNG", file);
                                save = true;
                            }
                            i++;
                        } while(!save);
                    } catch (Exception e) {
                        JOptionPane.showMessageDialog(ChiptuneTracker.getInstance().getAsciiTerminal(), e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
                    }
                    finally {
                        asciiTerminal.setEvent(null);
                    }
                }
            }

            // Update
            currentView.update(delta);
            chanels.update();

            // Paint
            asciiPanel.clear();
            currentView.paint();
            asciiTerminal.revalidate();
            asciiTerminal.repaint();

            try {
                long value = (lastLoopTime - System.nanoTime() + ChiptuneTracker.OPTIMAL_TIME) / 1000000;
                if(value > 0) {
                    Thread.sleep(value);                    
                }
                else {
                    Thread.sleep(5);
                }
            } catch (InterruptedException e) {
            }
        }
    }

    public void changeView(View nextView) {
        if(currentView != null) {
            currentView.quit();
        }
        currentView = nextView;
        asciiPanel.clear();
        currentView.init();
    }

    public static ChiptuneTracker getInstance() {
        return instance;
    }

    public CustomAsciiTerminal getAsciiTerminal() {
        return asciiTerminal;
    }

    public AsciiPanel getAsciiPanel() {
        return asciiPanel;
    }

    public boolean isInitPatternView() {
        return initPatternView;
    }

    public boolean isInitSampleView() {
        return initSampleView;
    }

    public void setInitPatternView(boolean initPatternView) {
        this.initPatternView = initPatternView;
    }

    public void setInitSampleView(boolean initSampleView) {
        this.initSampleView = initSampleView;
    }

    public Data getData() {
        return data;
    }

    public void setData(Data data) {
        this.data = data;
    }

    public DataManager getDataManager() {
        return dataManager;
    }

    public boolean isChangeData() {
        return changeData;
    }

    public void setChangeData(boolean changeData) {
        this.changeData = changeData;
    }

    public Chanels getChanels() {
        return chanels;
    }

    public MenuView getMenuView() {
        return menuView;
    }

    public SampleView getSampleView() {
        return sampleView;
    }

    public PatternView getPatternView() {
        return patternView;
    }

    public static void main(String[] args) {
        ChiptuneTracker chiptuneTracker = ChiptuneTracker.getInstance();
        chiptuneTracker.init();
        chiptuneTracker.run();
    }
}

有解决办法吗?

谢谢!

0 个答案:

没有答案