最初は線を引いたり消しゴムで消したりする機能を実装します。(MSX版のDRAWコマンドにあったFROM-P、FROM-N/R機能はアニメのフレームコントロールができてから実装することにし、今回はパスします)
線の引き方として「自由曲線モード」「単直線モード」「ポリ直線モード」の3種類の描画モードがあります。モード切り替えは画面下部に並んでいる3つのボタンで行います。
MSX版にはなかった単直線モードが追加されました。また、単直線及びポリ直線モードで仮描画線が出るようになったので線の位置の目安が付けやすくなりました。
「自由曲線モード」は、マウスの左ボタンを押しながらマウスを動かすとカーソルの軌跡が黒いドットで残ります。「単直線モード」は線の引き始めでマウスの左ボタンを押し、そのままボタンを離さずに引き終わりの点まで移動してボタンを離すと2点間を結ぶ直線を描きます。「ポリ直線モード」は、画面上で左ボタンをクリックするとクリックされた点を次々直線で結んでいきます。
右ボタンは「消しゴム」で、カーソル付近の小さい領域を白いドットで消します。右ボタンを押すとカーソルの形が変わるようにしたかったのですが、今回そこまで手が回りませんでした。最終バージョンまでにはなんとか実現したいと思います。
さらに、MSX版ではDRAWモードにはなかった(COPY&PASTEのみにあった)undo機能と、カーソル座標の表示が追加されました。
以下がプログラムリストです。コメントを見れば大体何をやっているのかわかると思います。
// Java版AceDrawのためのステップ // 「モノクロ2値お絵かき その1」McOekaki1.java // Copyright(c)2006 A.Hiramatsu import java.awt.*; import java.awt.image.*; import java.awt.event.*; import javax.swing.*; import javax.swing.border.*; public class McOekaki1 { private final int imgWidth = 320; private final int imgHeight = 240; private byte[] mono = {0,(byte)255}; private IndexColorModel cmMono = new IndexColorModel(1,2,mono,mono,mono); private BufferedImage img = new BufferedImage(imgWidth,imgHeight, BufferedImage.TYPE_BYTE_INDEXED,cmMono); private Graphics2D imgGfx; private JPanel grpPnl; private JLabel infoLbl; private JToggleButton freeBtn; private JToggleButton snglBtn; private JToggleButton polyBtn; private JButton undoBtn; private boolean drawing = false; private boolean dragging = false; private boolean erasing = false; private boolean tempdraw = false; private boolean inXORmode = false; private int drawLatX,drawLatY; private int drawCurX,drawCurY; private enum DrawMode {none,free,sngl,poly}; private DrawMode drawmode; private byte[] buffundo = new byte[imgWidth*imgHeight]; private byte[] buffredo = new byte[imgWidth*imgHeight]; private boolean canundo,canredo; private void setUndo() { img.getRaster().getDataElements(0,0, // 画面イメージを imgWidth,imgHeight,buffundo); // undoバッファに保存 canundo=true; // 「undo可能だがまだ一度もundoしてない」 canredo=false; // 状態にする } private void drawTempLine(int x,int y) { if((drawLatX != x) || (drawLatY != y)) { // 長さが0でなければ imgGfx.drawLine(drawLatX,drawLatY,x,y); // 仮描画する drawCurX=x; // 仮描画位置更新 drawCurY=y; tempdraw=true; // 仮描画済み } } private void cancelLine() { if(drawmode.equals(DrawMode.sngl) || // 仮描画するのは単直線かポリ直線のみ drawmode.equals(DrawMode.poly)) { if(tempdraw) { // 仮描画線を引いていたら if((drawLatX != drawCurX) || (drawLatY != drawCurY)) { imgGfx.drawLine(drawLatX,drawLatY, // 仮描画線を消す drawCurX,drawCurY); } } } tempdraw=false; } private void toXORmode() { imgGfx.setColor(Color.WHITE); imgGfx.setXORMode(Color.BLACK); inXORmode = true; } private void exitXORmode() { if(inXORmode) { imgGfx.setPaintMode(); } inXORmode = false; } MouseListener grpMLsn = new MouseAdapter() { // マウスボタンイベント public void mousePressed(MouseEvent e) { int x = e.getX(); int y = e.getY(); if(e.getButton()==MouseEvent.BUTTON1) { // 左ボタン押された if(!erasing) { // 消しゴム使用中でなければ setUndo(); if(drawmode.equals(DrawMode.free)) { // 自由曲線モード drawing=true; dragging=false; drawLatX=x; // 現在位置を最後の位置とする drawLatY=y; } else if(drawmode.equals(DrawMode.sngl)) { // 単直線モード drawing=true; drawLatX=x; // 現在位置を最後の位置とする drawLatY=y; drawCurX=x; // 仮描画用位置 drawCurY=y; toXORmode(); // 仮描画のためにXORモードにする } else if(drawmode.equals(DrawMode.poly)) { // ポリ直線モード if(drawing) { // すでに描画中なら if((drawLatX != x) || (drawLatY != y)) { cancelLine(); // 仮描画した線を消す exitXORmode(); // XORモードを抜ける imgGfx.setColor(Color.BLACK); imgGfx.drawLine(drawLatX,drawLatY,x,y); // 最後に打った点から線を引く grpPnl.repaint(); } } toXORmode(); // 仮描画のためXORモードにする drawing=true; // 描画中にする drawLatX=x; // 最後の位置を更新 drawLatY=y; drawCurX=x; // 仮描画用位置 drawCurY=y; } else { } } } else if(e.getButton()==MouseEvent.BUTTON3) { // 右ボタン押された if(drawing) { // 描画中で if(drawmode.equals(DrawMode.poly)) { // なおかつポリ直線モードなら cancelLine(); // 仮描画した線を消す exitXORmode(); // XORモードを抜ける drawing = false; // 描画中を解除 grpPnl.repaint(); } } else { // 描画中でなければ setUndo(); imgGfx.setColor(Color.WHITE); // 消しゴム imgGfx.drawLine(x-1,y-2,x+1,y-2); imgGfx.fillRect(x-2,y-1,5,3); imgGfx.drawLine(x-1,y+2,x+1,y+2); grpPnl.repaint(); erasing = true; // 消しゴム使用中にする } } } public void mouseReleased(MouseEvent e) { int x = e.getX(); int y = e.getY(); if(e.getButton()==MouseEvent.BUTTON1) { // 左ボタン離された if(drawmode.equals(DrawMode.free)) { // 自由曲線モード if(drawing && !dragging) { img.setRGB(drawLatX,drawLatY,0); // ドラッグしてなければ1点だけ打つ grpPnl.repaint(); } drawing=false; dragging=false; } else if(drawmode.equals(DrawMode.sngl)) { // 単直線モード if(drawing) { cancelLine(); // 仮描画した線を消す exitXORmode(); // XORモードを抜ける if((drawLatX != x) || (drawLatY != y)) { // 線の長さが0でなければ imgGfx.setColor(Color.BLACK); imgGfx.drawLine(drawLatX,drawLatY, drawCurX,drawCurY); // 正式に線を引く } grpPnl.repaint(); } drawing=false; } else { } } else if(e.getButton()==MouseEvent.BUTTON3) { // 右ボタン離された erasing=false; // 消しゴム使用中を解除 } else { } } public void mouseExited(MouseEvent e) { // マウスが描画領域を出た infoLbl.setText(""); } }; MouseMotionListener grpMmLsn = new MouseMotionAdapter() { // マウス移動イベント public void mouseDragged(MouseEvent e) { int x = e.getX(); int y = e.getY(); infoLbl.setText( "(" + Integer.toString(x) + "," + Integer.toString(y) + ")" ); // 座標表示 if(drawing) { if(drawmode.equals(DrawMode.free)) { // 自由曲線モード if((drawLatX != x) || (drawLatY != y)) { // カーソルが動いていたら imgGfx.setColor(Color.BLACK); imgGfx.drawLine(drawLatX,drawLatY,x,y); // 線を引く drawLatX=x; // 最後の位置を更新 drawLatY=y; dragging=true; grpPnl.repaint(); } } else if(drawmode.equals(DrawMode.sngl) || // 単直線モード または (drawmode.equals(DrawMode.poly) && // ポリ直線モードで描画中なら drawing)) { cancelLine(); // 仮描画線を消す drawTempLine(x,y); // 新しい仮描画線を書く grpPnl.repaint(); } else { } } else if(erasing) { // 消しゴム使用中 imgGfx.setColor(Color.WHITE); // 消しゴム imgGfx.drawLine(x-1,y-2,x+1,y-2); imgGfx.fillRect(x-2,y-1,5,3); imgGfx.drawLine(x-1,y+2,x+1,y+2); grpPnl.repaint(); } } public void mouseMoved(MouseEvent e) { int x = e.getX(); int y = e.getY(); infoLbl.setText( "(" + Integer.toString(x) + "," + Integer.toString(y) + ")" ); // 座標表示 if(drawmode.equals(DrawMode.poly)) { // ポリ直線モードで if(drawing) { // 描画中なら cancelLine(); // 仮描画線を消す drawTempLine(x,y); // 新しい仮描画線を描く grpPnl.repaint(); } } } }; MouseListener undoBtnLsn = new MouseAdapter() { // undoボタンイベント public void mousePressed(MouseEvent e) { cancelLine(); exitXORmode(); if(canredo) { // redo可(一度undoしている) if(canundo) { // undo可なら img.getRaster().setDataElements(0,0, // undoバッファの内容を imgWidth,imgHeight,buffundo); // 画面に復帰 } else { img.getRaster().setDataElements(0,0, // undoバッファの内容を imgWidth,imgHeight,buffredo); // 画面に復帰 } canundo = !canundo; // undo可をトグル } else { if(canundo) { // 描画後最初のundo img.getRaster().getDataElements(0,0, // 現在の画面イメージを imgWidth,imgHeight,buffredo); // redoバッファに保存 img.getRaster().setDataElements(0,0, // undoバッファの内容を imgWidth,imgHeight,buffundo); // 画面に復帰 canundo=false; canredo=true; // redo可にする } } grpPnl.repaint(); drawing=false; dragging=false; erasing=false; } }; MouseListener freeBtnLsn = new MouseAdapter() { // freeボタン public void mousePressed(MouseEvent e) { cancelLine(); exitXORmode(); grpPnl.repaint(); drawmode=DrawMode.free; drawing=false; dragging=false; erasing=false; } }; MouseListener snglBtnLsn = new MouseAdapter() { // 単直線ボタン public void mousePressed(MouseEvent e) { cancelLine(); exitXORmode(); grpPnl.repaint(); drawmode=DrawMode.sngl; drawing=false; dragging=false; erasing=false; } }; MouseListener polyBtnLsn = new MouseAdapter() { // ポリ直線ボタン public void mousePressed(MouseEvent e) { cancelLine(); exitXORmode(); drawmode=DrawMode.poly; drawing=false; dragging=false; erasing=false; } }; private void doProg() { drawmode = DrawMode.none; imgGfx=img.createGraphics(); imgGfx.setColor(Color.WHITE); imgGfx.fillRect(0,0,imgWidth,imgHeight); grpPnl.repaint(); grpPnl.addMouseListener(grpMLsn); grpPnl.addMouseMotionListener(grpMmLsn); undoBtn.addMouseListener(undoBtnLsn); freeBtn.addMouseListener(freeBtnLsn); snglBtn.addMouseListener(snglBtnLsn); polyBtn.addMouseListener(polyBtnLsn); } private void makeFrame() { JFrame.setDefaultLookAndFeelDecorated(true); JFrame frm = new JFrame("モノクロ2値お絵かき その1"); frm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JPanel toolPnl = new JPanel(new GridLayout(1,3,2,0)); freeBtn =new JToggleButton("free"); snglBtn =new JToggleButton("single"); polyBtn =new JToggleButton("poly"); toolPnl.add(freeBtn); toolPnl.add(snglBtn); toolPnl.add(polyBtn); ButtonGroup toolBtns = new ButtonGroup(); toolBtns.add(freeBtn); toolBtns.add(snglBtn); toolBtns.add(polyBtn); JPanel btnPnl = new JPanel(new BorderLayout(8,0)); undoBtn = new JButton("undo"); btnPnl.add(undoBtn,BorderLayout.WEST); btnPnl.add(toolPnl,BorderLayout.CENTER); grpPnl = new JPanel() { public void paintComponent(Graphics g) { super.paintComponent(g); g.drawImage(img,0,0,this); } }; grpPnl.setPreferredSize(new Dimension(imgWidth,imgHeight)); JPanel infoPnl = new JPanel(new BorderLayout()); infoPnl.setPreferredSize(new Dimension(imgWidth,14)); infoLbl=new JLabel(); infoLbl.setFont(new Font("Monospaced",Font.PLAIN,10)); infoPnl.add(infoLbl,BorderLayout.WEST); JPanel grpBasePnl = new JPanel(new BorderLayout()); grpBasePnl.add(grpPnl,BorderLayout.CENTER); grpBasePnl.add(infoPnl,BorderLayout.SOUTH); JPanel mainPnl = new JPanel(new BorderLayout(0,4)); mainPnl.add(grpBasePnl,BorderLayout.CENTER); mainPnl.add(btnPnl,BorderLayout.SOUTH); mainPnl.setBorder(new EmptyBorder(4,4,4,4)); frm.getContentPane().add(mainPnl); frm.pack(); frm.setVisible(true); } public static void main(final String[] args) { McOekaki1 myProg = new McOekaki1(); myProg.makeFrame(); myProg.doProg(); } } |
愚痴コーナー
|