xeij/SpritePatternViewer.java- Source code ソースコード - XEiJ - STUDIO KAMADA
xeij/SpritePatternViewer.java

    //========================================================================================
    //  SpritePatternViewer.java
    //    en:Sprite pattern viewer
    //    ja:スプライトパターンビュア
    //  Copyright (C) 2003-2025 Makoto Kamada
    //
    //  This file is part of the XEiJ (X68000 Emulator in Java).
    //  You can use, modify and redistribute the XEiJ if the conditions are met.
    //  Read the XEiJ License for more details.
    //  https://stdkmd.net/xeij/
    //========================================================================================

    package xeij;

    import java.awt.*;
    import java.awt.event.*;  //MouseEvent
    import java.awt.font.*;  //TextLayout
    import java.awt.geom.*;  //Rectangle2D
    import java.awt.image.*;  //BufferedImage
    import java.util.*;  //Arrays
    import javax.swing.*;

    public class SpritePatternViewer {

      public static final boolean SPV_ON = true;

      //セル
      static final int SPV_CELL_WIDTH = 36;  //セルの幅。偶数
      static final int SPV_CELL_HEIGHT = 36;  //セルの高さ

      //グループ
      //  グループのサイズは4x4セルに固定
      static final int SPV_GROUP_GAP = 4;  //グループの間隔

      //ヘッダ
      static final String SPV_FONT_NAME = "Dialog";  //フォント名
      static final int SPV_FONT_STYLE = Font.BOLD;  //フォントスタイル
      static final int SPV_FONT_SIZE = 16;  //フォントサイズ
      static final int SPV_HEADER_WIDTH = SPV_FONT_SIZE * 3;  //左ヘッダの幅
      static final int SPV_HEADER_HEIGHT = SPV_FONT_SIZE;  //上ヘッダの高さ

      //イメージ
      static int spvImageWidth;  //イメージ幅
      static int spvImageHeight;  //イメージ高さ
      static BufferedImage spvBufferedImage;  //イメージ
      static int[] spvBitmap;  //ビットマップ
      static ScrollCanvas spvCanvas;  //キャンバス

      //色
      static final int SPV_BACKGROUND_RGB = 0xff333333;  //背景色
      static final int SPV_FOREGROUND_RGB = 0xffcccccc;  //文字色
      static final int SPV_GRAY_LINE_RGB = 0xff999999;  //灰色の線の色
      static final int SPV_BLOCK_DOT_RGB = 0xffffffff;  //パレットブロックの点の色

      //バンク
      //  0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16  17  18   19    20
      //  0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 0-3 4-7 8-11 12-15 0-15
      static final int SPV_DEF_BANK = 0;
      static final int SPV_MIN_BANK = 0;
      static final int SPV_MAX_BANK = 20;
      static int spvBankNumber;  //バンク番号
      static JComboBox<String> spvBankComboBox;  //バンクドロップダウンリスト
      static int spvCellOffset;  //開始セル番号
      static int spvColsBit;  //セル桁数のビット
      static int spvRowsBit;  //セル行数のビット
      static int spvColsMask;  //セル桁数のマスク
      static int spvRowsMask;  //セル行数のマスク
      static int spvWidth;  //幅
      static int spvHeight;  //高さ

      //サイズ
      //  -1   0   1
      //  Auto 8x8 16x16
      static final int SPV_DEF_SIZE = -1;
      static final int SPV_MIN_SIZE = -1;
      static final int SPV_MAX_SIZE = 1;
      static final int SPV_LAST_SIZE = 1;  //前回のサイズの初期値
      static int spvSizeNumber;  //サイズ番号
      static JComboBox<String> spvSizeComboBox;  //サイズドロップダウンリスト
      static final int[] spvLastSizeArray = new int[4096];  //前回のサイズ
      static final int[] spvSizeArray = new int[4096];  //サイズ

      //パレットブロック
      //  -1   0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
      //  Auto 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
      static final int SPV_DEF_BLOCK = -1;
      static final int SPV_MIN_BLOCK = -1;
      static final int SPV_MAX_BLOCK = 15;
      static final int SPV_LAST_BLOCK = 1;  //前回のパレットブロックの初期値
      static int spvBlockNumber;  //ブロック番号
      static JComboBox<String> spvBlockComboBox;  //ブロックドロップダウンリスト
      static final int[] spvLastBlockArray = new int[4 * 4096];  //前回のパレットブロック
      static final int[] spvBlockArray = new int[4 * 4096];  //パレットブロック

      //反転
      //  -1   0 1 2 3
      //  Auto - H V H&V
      static final int SPV_DEF_FLIP = -1;
      static final int SPV_MIN_FLIP = -1;
      static final int SPV_MAX_FLIP = 3;
      static final int SPV_LAST_FLIP = 0;  //前回の反転の初期値
      static int spvFlipNumber;  //反転番号
      static JComboBox<String> spvFlipComboBox;  //反転ドロップダウンリスト
      static final int[] spvLastFlipArray = new int[4 * 4096];  //前回の反転
      static final int[] spvFlipArray = new int[4 * 4096];  //反転。bit1=上下反転,bit0=左右反転

      //倍率
      //  -1 0 1 2 3  4
      //  1  2 4 8 16 32
      static final int SPV_DEF_SCALE = 0;
      static final int SPV_MIN_SCALE = -1;
      static final int SPV_MAX_SCALE = 4;
      static int spvScaleNumber;  //倍率番号
      static JComboBox<String> spvScaleComboBox;  //倍率ドロップダウンリスト

      //Hex
      //  off    on
      //  10進数 16進数
      static final boolean SPV_DEF_HEX = false;
      static boolean spvHex;

      //停止
      static boolean spvStopped;  //true=停止中
      static boolean spvStoppedRequest;
      //  コピーされた値
      static final short[] spvCopiedNum = new short[SpriteScreen.SPR_COUNT];
      static final short[] spvCopiedCol = new short[SpriteScreen.SPR_COUNT];
      static final byte[] spvCopiedPrw = new byte[SpriteScreen.SPR_COUNT];
      static final boolean[] spvCopiedH = new boolean[SpriteScreen.SPR_COUNT];
      static final boolean[] spvCopiedV = new boolean[SpriteScreen.SPR_COUNT];
      static final int[] spvCopiedPat = new int[32 * 4096];
      static final short[] spvCopiedTNum = new short[4096 * 4];
      static final short[] spvCopiedTCol = new short[4096 * 4];
      static final boolean[] spvCopiedTH = new boolean[4096 * 4];
      static final boolean[] spvCopiedTV = new boolean[4096 * 4];
      static final int[] spvCopiedPalTbl = new int[65536];
      static final int[] spvCopiedPal16TS = new int[256];
      //  参照する値。停止中はコピーされた値、さもなくば現在の値
      static short[] spvNum;
      static short[] spvCol;
      static byte[] spvPrw;
      static boolean[] spvH;
      static boolean[] spvV;
      static int[] spvPat;
      static short[] spvTNum;
      static short[] spvTCol;
      static boolean[] spvTH;
      static boolean[] spvTV;
      static int[] spvPalTbl;
      static int[] spvPal16TS;
      //
      static final int[] spvPal32TS = new int[256];

      //テキストフィールド
      static JTextField spvTextField;
      static boolean spvTextLocked;

      //ウインドウ
      static JFrame spvFrame;

      //ポップアップメニュー
      static JPopupMenu spvPopupMenu;
      static int spvOffsetToCopy;
      static int spvLengthToCopy;

      //タイマー
      public static final int SPV_INTERVAL = 10;
      public static int spvTimer;

      //spvInit ()
      //  初期化
      public static void spvInit () {
        //パラメータ
        spvBankNumber = Settings.sgsGetInt ("spvbank", SPV_DEF_BANK, SPV_MIN_BANK, SPV_MAX_BANK);
        spvBlockNumber = Settings.sgsGetInt ("spvblock", SPV_DEF_BLOCK, SPV_MIN_BLOCK, SPV_MAX_BLOCK);
        spvFlipNumber = Settings.sgsGetInt ("spvflip", SPV_DEF_FLIP, SPV_MIN_FLIP, SPV_MAX_FLIP);
        spvHex = Settings.sgsGetOnOff ("spvhex", SPV_DEF_HEX);
        spvScaleNumber = Settings.sgsGetInt ("spvscale", SPV_DEF_SCALE, SPV_MIN_SCALE, SPV_MAX_SCALE);
        spvSizeNumber = Settings.sgsGetInt ("spvsize", SPV_DEF_SIZE, SPV_MIN_SIZE, SPV_MAX_SIZE);
        //イメージ
        spvImageWidth = (SPV_HEADER_WIDTH +
                         (SPV_CELL_WIDTH << 6) +
                         (SPV_GROUP_GAP << (6 - 2)) - SPV_GROUP_GAP);
        spvImageHeight = (SPV_HEADER_HEIGHT +
                          (SPV_CELL_HEIGHT << 6) +
                          (SPV_GROUP_GAP << (6 - 2)) - SPV_GROUP_GAP);
        //バンク
        spvSetBankNumber (spvBankNumber);
        //サイズ
        Arrays.fill (spvLastSizeArray, SPV_LAST_SIZE);
        //パレットブロック
        Arrays.fill (spvLastBlockArray, SPV_LAST_BLOCK);
        //反転
        Arrays.fill (spvLastFlipArray, SPV_LAST_FLIP);
        //停止
        spvSetStoppedOff ();
        //ウインドウ
        spvFrame = null;
        //タイマー
        spvTimer = 0;
      }  //spvInit

      //spvTini ()
      //  後始末
      public static void spvTini () {
        //パラメータ
        Settings.sgsPutInt ("spvbank", spvBankNumber);
        Settings.sgsPutInt ("spvblock", spvBlockNumber);
        Settings.sgsPutInt ("spvflip", spvFlipNumber);
        Settings.sgsPutOnOff ("spvhex", spvHex);
        Settings.sgsPutInt ("spvscale", spvScaleNumber);
        Settings.sgsPutInt ("spvsize", spvSizeNumber);
      }  //spvTini

      //spvSetBankNumber (number)
      //  バンク番号を設定する
      static void spvSetBankNumber (int number) {
        spvBankNumber = number;
        spvCellOffset = (spvBankNumber < 16 ? spvBankNumber << 8 :
                         spvBankNumber < 20 ? (spvBankNumber - 16) << 10 :
                         0);  //(spvBankNumber-20)<<12
        spvColsBit =
          spvRowsBit = (spvBankNumber < 16 ? 4 :  //16x16
                        spvBankNumber < 20 ? 5 :  //32x32
                        6);  //64x64
        spvColsMask =
          spvRowsMask = (1 << spvRowsBit) - 1;
        spvWidth = (SPV_HEADER_WIDTH +
                    (SPV_CELL_WIDTH << spvColsBit) +
                    (SPV_GROUP_GAP << (spvColsBit - 2)) - SPV_GROUP_GAP);
        spvHeight = (SPV_HEADER_HEIGHT +
                     (SPV_CELL_HEIGHT << spvRowsBit) +
                     (SPV_GROUP_GAP << (spvRowsBit - 2)) - SPV_GROUP_GAP);
        if (spvCanvas != null) {
          spvDrawHeader ();
          spvCanvas.setImage (spvWidth, spvHeight);
        }
      }  //spvSetBankNumber

      //x = spvColToX (col)
      //  セル桁からX座標を求める
      static int spvColToX (int col) {
        int g = col >> 2;  //グループ
        col &= 3;  //グループ内セル桁
        return (SPV_HEADER_WIDTH +
                ((SPV_CELL_WIDTH << 2) + SPV_GROUP_GAP) * g +
                SPV_CELL_WIDTH * col);
      }  //spvColToX

      //y = spvRowToY (row)
      //  セル行からY座標を求める
      static int spvRowToY (int row) {
        int g = row >> 2;  //グループ
        row &= 3;  //グループ内セル行
        return (SPV_HEADER_HEIGHT +
                ((SPV_CELL_HEIGHT << 2) + SPV_GROUP_GAP) * g +
                SPV_CELL_HEIGHT * row);
      }  //spvRowToY

      //((x << 16) | col) = spvXToXCol (x)
      //  X座標からセル内X座標とセル桁を求める。-1=範囲外
      static int spvXToXCol (int x) {
        if (x < 0 || spvWidth <= x) {  //表示範囲の外
          return -1;
        }
        if (x < SPV_HEADER_WIDTH) {  //左ヘッダ
          return -1;
        }
        x -= SPV_HEADER_WIDTH;
        int g = x / ((SPV_CELL_WIDTH << 2) + SPV_GROUP_GAP);  //グループ
        x -= ((SPV_CELL_WIDTH << 2) + SPV_GROUP_GAP) * g;  //グループ内X座標
        if ((SPV_CELL_WIDTH << 2) <= x) {  //グループの間隔
          return -1;
        }
        int col = x / SPV_CELL_WIDTH;  //グループ内セル桁
        x -= SPV_CELL_WIDTH * col;  //セル内X座標
        return (x << 16) | ((g << 2) + col);
      }  //spvXToXCol

      //((y << 16) | row) = spvYToYRow (y)
      //  Y座標からセル内Y座標とセル行を求める。-1=範囲外
      static int spvYToYRow (int y) {
        if (y < 0 || spvHeight <= y) {  //表示範囲の外
          return -1;
        }
        if (y < SPV_HEADER_HEIGHT) {  //上ヘッダ
          return -1;
        }
        y -= SPV_HEADER_HEIGHT;
        int g = y / ((SPV_CELL_HEIGHT << 2) + SPV_GROUP_GAP);  //グループ
        y -= ((SPV_CELL_HEIGHT << 2) + SPV_GROUP_GAP) * g;  //グループ内Y座標
        if ((SPV_CELL_HEIGHT << 2) <= y) {  //グループの間隔
          return -1;
        }
        int row = y / SPV_CELL_HEIGHT;  //グループ内セル行
        y -= SPV_CELL_HEIGHT * row;  //セル内Y座標
        return (y << 16) | ((g << 2) + row);
      }  //spvYToYRow

      //spvStart ()
      //  開始
      public static void spvStart () {
        if (RestorableFrame.rfmGetOpened (Settings.SGS_SPV_FRAME_KEY)) {
          spvOpen ();
        }
      }  //spvStart

      //spvOpen ()
      //  開く
      public static void spvOpen () {
        if (spvFrame == null) {
          spvMakeFrame ();
        } else {
          spvUpdateFrame ();
        }
        XEiJ.dbgVisibleMask |= XEiJ.DBG_SPV_VISIBLE_MASK;
        XEiJ.pnlExitFullScreen (false);
        spvFrame.setVisible (true);
      }  //spvOpen

      //spvMakeFrame ()
      //  作る
      //  ここでは開かない
      static void spvMakeFrame () {
        //イメージ
        spvBufferedImage = new BufferedImage (spvImageWidth, spvImageHeight, BufferedImage.TYPE_INT_ARGB);
        spvDrawHeader ();
        //ビットマップ
        spvBitmap = ((DataBufferInt) spvBufferedImage.getRaster ().getDataBuffer ()).getData ();
        //キャンバス
        spvCanvas = new ScrollCanvas (spvBufferedImage, spvWidth, spvHeight);
        spvCanvas.setMatColor (new Color (SPV_BACKGROUND_RGB));
        spvCanvas.setMinScaleShift (SPV_MIN_SCALE);
        spvCanvas.setMaxScaleShift (SPV_MAX_SCALE);
        spvCanvas.setScaleShift (spvScaleNumber);
        //アクションリスナー
        ActionListener listener = new ActionListener () {
          @Override public void actionPerformed (ActionEvent ae) {
            Object source = ae.getSource ();
            String command = ae.getActionCommand ();
            switch (command) {
            case "Bank":
              spvSetBankNumber (spvBankComboBox.getSelectedIndex () + SPV_MIN_BANK);
              if (XEiJ.mpuTask == null) {
                spvUpdateFrame ();
              }
              break;
            case "Size":
              spvSizeNumber = spvSizeComboBox.getSelectedIndex () + SPV_MIN_SIZE;
              if (XEiJ.mpuTask == null) {
                spvUpdateFrame ();
              }
              break;
            case "Block":
              spvBlockNumber = spvBlockComboBox.getSelectedIndex () + SPV_MIN_BLOCK;
              if (XEiJ.mpuTask == null) {
                spvUpdateFrame ();
              }
              break;
            case "Flip":
              spvFlipNumber = spvFlipComboBox.getSelectedIndex () + SPV_MIN_FLIP;
              if (XEiJ.mpuTask == null) {
                spvUpdateFrame ();
              }
              break;
            case "Hex":
              spvHex = (((JCheckBox) source).isSelected ());
              spvDrawHeader ();
              spvUpdateFrame ();
              break;
            case "Stop":
              spvStoppedRequest = (((JCheckBox) source).isSelected ());
              if (XEiJ.mpuTask == null) {
                spvUpdateFrame ();
              }
              break;
            case "Scale":
              spvCanvas.setScaleShift (spvScaleComboBox.getSelectedIndex () + SPV_MIN_SCALE);
              break;
            case "Copy as hexadecimal":
              spvCopyPattern ();
              break;
            default:
              System.out.println ("unknown action command " + command);
            }
          }
        };
        //テキストフィールド
        spvTextField = new JTextField ();
        spvTextField.setEditable (false);
        spvTextField.setHorizontalAlignment (JTextField.CENTER);
        //ウインドウ
        spvFrame = Multilingual.mlnTitle (
          ComponentFactory.createRestorableSubFrame (
            Settings.SGS_SPV_FRAME_KEY,
            "Sprite Pattern Viewer",
            null,
            ComponentFactory.createBorderPanel (
              0, 0,
              //CENTER
              spvCanvas,
              //NORTH
              ComponentFactory.createVerticalBox (
                //1行目
                ComponentFactory.createHorizontalBox (
                  Box.createHorizontalGlue (),
                  //
                  Multilingual.mlnText (
                    ComponentFactory.createLabel ("Bank "),
                    "ja", "バンク "),
                  spvBankComboBox = ComponentFactory.createComboBox (
                    spvBankNumber - SPV_MIN_BANK,
                    "Bank",
                    listener,
                    "0", "1", "2", "3", "4", "5", "6", "7", "8",
                    "9", "10", "11", "12", "13", "14", "15",
                    "0-3", "4-7", "8-11", "12-15", "0-15"),
                  //
                  Multilingual.mlnText (
                    ComponentFactory.createLabel ("Size "),
                    "ja", "サイズ "),
                  spvSizeComboBox = ComponentFactory.createComboBox (
                    spvSizeNumber - SPV_MIN_SIZE,
                    "Size",
                    listener,
                    4,
                    Multilingual.mlnJapanese ? "自動" : "Auto",
                    "8x8", "16x16"),
                  //
                  Multilingual.mlnText (
                    ComponentFactory.createLabel ("Block "),
                    "ja", "ブロック "),
                  spvBlockComboBox = ComponentFactory.createComboBox (
                    spvBlockNumber - SPV_MIN_BLOCK,
                    "Block",
                    listener,
                    4,
                    Multilingual.mlnJapanese ? "自動" : "Auto",
                    "0", "1", "2", "3", "4", "5", "6", "7", "8",
                    "9", "10", "11", "12", "13", "14", "15"),
                  //
                  Multilingual.mlnText (
                    ComponentFactory.createLabel ("Flip "),
                    "ja", "反転 "),
                  spvFlipComboBox = ComponentFactory.createComboBox (
                    spvFlipNumber - SPV_MIN_FLIP,
                    "Flip",
                    listener,
                    4,
                    Multilingual.mlnJapanese ? "自動" : "Auto",
                    Multilingual.mlnJapanese ? "なし" : "-",
                    Multilingual.mlnJapanese ? "左右" : "H",
                    Multilingual.mlnJapanese ? "上下" : "V",
                    Multilingual.mlnJapanese ? "上下左右" : "H&V"),
                  //
                  Box.createHorizontalStrut (5),
                  ComponentFactory.createCheckBox (spvHex, "Hex", listener),
                  //
                  Box.createHorizontalGlue ()
                  ),  //HorizontalBox
                //2行目
                ComponentFactory.createHorizontalBox (
                  //
                  spvTextField,
                  //
                  Multilingual.mlnText (
                    ComponentFactory.createLabel ("Scale "),
                    "ja", "倍率 "),
                  spvScaleComboBox = ComponentFactory.createComboBox (
                    spvCanvas.getScaleShift () - SPV_MIN_SCALE,
                    "Scale",
                    listener,
                    "1", "2", "4", "8", "16", "32"),
                  //
                  Box.createHorizontalStrut (5),
                  Multilingual.mlnText (
                    ComponentFactory.createCheckBox (spvStoppedRequest, "Stop", listener),
                    "ja", "停止")
                  //
                  )  //HorizontalBox
                )  //VerticalBox
              )  //BorderPanel
            ),  //SubFrame
          "ja", "スプライトパターンビュア");
        //スケールシフトリスナー
        spvCanvas.addScaleShiftListener (new ScrollCanvas.ScaleShiftListener () {
          @Override public void scaleShiftChanged (int scaleShift) {
            spvScaleComboBox.setSelectedIndex (scaleShift - SPV_MIN_SCALE);
          }
        });
        //ウインドウリスナー
        ComponentFactory.addListener (
          spvFrame,
          new WindowAdapter () {
            @Override public void windowClosing (WindowEvent we) {
              XEiJ.dbgVisibleMask &= ~XEiJ.DBG_SPV_VISIBLE_MASK;
            }
          });
        //ポップアップメニュー
        spvPopupMenu = ComponentFactory.createPopupMenu (
          Multilingual.mlnText (
            ComponentFactory.createMenuItem ("Copy as hexadecimal", 'C', listener),
            "ja", "16進数でコピー")
          );
        //マウスリスナー
        MouseAdapter ma = new MouseAdapter () {
          @Override public void mouseClicked (MouseEvent me) {
            spvTextLocked = !spvTextLocked;  //テキストフィールドのロックを反転する
          }  //mouseClicked
          @Override public void mouseMoved (MouseEvent me) {
            if (!spvTextLocked) {  //テキストフィールドがロックされていないとき
              MouseEvent2D me2D = (MouseEvent2D) me;
              int x = (int) me2D.getX2D ();
              int y = (int) me2D.getY2D ();
              String s = spvGetPixel (x, y);
              spvTextField.setText (s == null ? "" : s);  //テキストフィールドに表示する
            }
          }  //mouseMoved
          @Override public void mousePressed (MouseEvent me) {
            spvShowPopup (me);
          }  //mousePressed
          @Override public void mouseReleased (MouseEvent me) {
            spvShowPopup (me);
          }  //mouseReleased
        };
        spvCanvas.addMouseListener (ma);
        spvCanvas.addMouseMotionListener (ma);
      }  //spvMakeFrame

      //spvDrawHeader ()
      //  ヘッダを描く
      static void spvDrawHeader () {
        Graphics2D g2 = spvBufferedImage.createGraphics ();
        //背景
        g2.setColor (new Color (SPV_BACKGROUND_RGB));
        g2.fillRect (0, 0, spvWidth, spvHeight);
        //ヘッダ
        g2.setColor (new Color (SPV_FOREGROUND_RGB));
        g2.setFont (new Font (SPV_FONT_NAME, SPV_FONT_STYLE, SPV_FONT_SIZE));
        //  上
        for (int col = 0; col <= spvColsMask; col++) {
          spvDrawString (g2,
                         spvColToX (col) + SPV_CELL_WIDTH / 2,  //X座標
                         spvRowToY (0) - SPV_FONT_SIZE / 12,  //Y座標
                         String.format (spvHex ? "%X" : "%d",
                                        col & spvColsMask),  //文字列
                         SwingConstants.SOUTH);  //アンカー
        }  //for col
        //  左
        for (int row = 0; row <= spvRowsMask; row++) {
          spvDrawString (g2,
                         spvColToX (0) - SPV_FONT_SIZE / 6,  //X座標
                         spvRowToY (row) + SPV_CELL_HEIGHT / 2,  //Y座標
                         String.format (spvHex ? "%X" : "%d",
                                        spvCellOffset + ((row & spvRowsMask) << spvColsBit)),  //文字列
                         SwingConstants.EAST);  //アンカー
        }  //for row
      }  //spvDrawHeader

      //spvDrawString (g, x, y, s, d)
      //  文字列を描く
      static void spvDrawString (Graphics2D g2, int x, int y, String s, int d) {
        Font f = g2.getFont ();
        FontRenderContext flc = g2.getFontRenderContext ();
        TextLayout tl = new TextLayout (s, f, flc);
        Rectangle2D r = tl.getBounds ();
        int rx = (int) Math.round (r.getX ());
        int ry = (int) Math.round (r.getY ());
        int rw = (int) Math.round (r.getWidth ());
        int rh = (int) Math.round (r.getHeight ());
        switch (d) {
        case SwingConstants.NORTH_WEST:
          g2.drawString (s, x - rx, y - ry);
          break;
        case SwingConstants.NORTH:
          g2.drawString (s, x - (rx + (rw >> 1)), y - ry);
          break;
        case SwingConstants.NORTH_EAST:
          g2.drawString (s, x - (rx + rw), y - ry);
          break;
        case SwingConstants.WEST:
          g2.drawString (s, x - rx, y - (ry + (rh >> 1)));
          break;
        case SwingConstants.CENTER:
          g2.drawString (s, x - (rx + (rw >> 1)), y - (ry + (rh >> 1)));
          break;
        case SwingConstants.EAST:
          g2.drawString (s, x - (rx + rw), y - (ry + (rh >> 1)));
          break;
        case SwingConstants.SOUTH_WEST:
          g2.drawString (s, x - rx, y - (ry + rh));
          break;
        case SwingConstants.SOUTH:
          g2.drawString (s, x - (rx + (rw >> 1)), y - (ry + rh));
          break;
        case SwingConstants.SOUTH_EAST:
          g2.drawString (s, x - (rx + rw), y - (ry + rh));
          break;
        }
      }  //spvDrawString

      //spvShowPopup (me)
      //  ポップアップメニューを表示する
      static void spvShowPopup (MouseEvent me) {
        if (me.isPopupTrigger ()) {
          MouseEvent2D me2D = (MouseEvent2D) me;
          int x = (int) me2D.getX2D ();
          int y = (int) me2D.getY2D ();
          if (spvGetPatternToCopy (x, y)) {
            Point p = spvCanvas.getPopupPoint (me2D);
            spvPopupMenu.show (spvCanvas, p.x, p.y);
          }
        }
      }  //spvShowPopup

      //f = spvGetPatternToCopy (x, y)
      //  座標からspvPatのコピー範囲を求めてspvOffsetToCopyとspvLengthToCopyを設定する
      //  1セルの長さは32、1/4セルの長さは8
      static boolean spvGetPatternToCopy (int x, int y) {
        int col = spvXToXCol (x);  //セル内X座標<<16|セル桁
        int row = spvYToYRow (y);  //セル内Y座標<<16|セル行
        if (col < 0 || row < 0) {  //セル範囲外
          spvOffsetToCopy = 0;
          spvLengthToCopy = 0;
          return false;
        }
        x = col >> 16;  //セル内X座標
        y = row >> 16;  //セル内Y座標
        col = (char) col;  //セル桁
        row = (char) row;  //セル行
        int n = (spvCellOffset + col + (row << spvColsBit)) << 2;  //8x8パターン番号
        int size = spvSizeArray[n >> 2];  //サイズ
        if (size < 0) {
          size = spvLastSizeArray[n >> 2];
        }
        if (size == 0) {  //8x8
          int qCol = x / (SPV_CELL_WIDTH / 2);  //1/4セル桁
          int qRow = y / (SPV_CELL_HEIGHT / 2);  //1/4セル行
          n += qRow + (qCol << 1);  //8x8パターン番号。進行方向に注意
          spvLengthToCopy = 1 << 3;
        } else {  //16x16
          spvLengthToCopy = 4 << 3;
        }
        spvOffsetToCopy = n << 3;
        return true;
      }  //spvGetPatternToCopy

      //spvCopyPattern ()
      //  設定された範囲のパターンを16進数でコピーする
      static void spvCopyPattern () {
        if (spvLengthToCopy != 0) {
          StringBuilder sb = new StringBuilder ();
          for (int i = 0; i < spvLengthToCopy; i++) {
            sb.append (String.format ("%08X\n", spvPat[spvOffsetToCopy + i]));
          }
          XEiJ.clpCopy (sb.toString ());
        }
      }  //spvCopyPattern

      //s = spvGetPixel (x, y)
      //  座標からピクセルの情報を求める
      static String spvGetPixel (int x, int y) {
        int col = spvXToXCol (x);  //セル内X座標<<16|セル桁
        int row = spvYToYRow (y);  //セル内Y座標<<16|セル行
        if (col < 0 || row < 0) {  //セル範囲外
          return null;
        }
        x = col >> 16;  //セル内X座標
        y = row >> 16;  //セル内Y座標
        col = (char) col;  //セル桁
        row = (char) row;  //セル行
        int n = (spvCellOffset + col + (row << spvColsBit)) << 2;  //8x8パターン番号
        int size = spvSizeArray[n >> 2];  //サイズ
        if (size < 0) {
          size = spvLastSizeArray[n >> 2];
        }
        if (size == 0) {  //8x8
          int qCol = x / (SPV_CELL_WIDTH / 2);  //1/4セル桁
          int qRow = y / (SPV_CELL_HEIGHT / 2);  //1/4セル行
          n += qRow + (qCol << 1);  //8x8パターン番号。進行方向に注意
          x -= (SPV_CELL_WIDTH / 2) * qCol;  //1/4セル内X座標
          y -= (SPV_CELL_HEIGHT / 2) * qRow;  //1/4セル内Y座標
          x -= 1;
          y -= 1;
          if (x < 0 || 2 * 8 <= x ||
              y < 0 || 2 * 8 <= y) {  //パータン範囲外
            return null;
          }
          x >>= 1;  //8x8パターン内X座標
          y >>= 1;  //8x8パターン内Y座標
          int pb = spvBlockArray[n];  //パレットブロック
          if (pb < 0) {
            pb = spvLastBlockArray[n];
          }
          int vh = spvFlipArray[n];  //反転
          if (vh < 0) {
            vh = spvLastFlipArray[n];
          }
          int v = vh >> 1;
          int h = vh & 1;
          if (h != 0) {  //水平反転
            x ^= 7;
          }
          if (v != 0) {  //垂直反転
            y ^= 7;
          }
          int pc = (spvPat[(n << 3) + y] >> ((7 - x) << 2)) & 15;  //パレットコード
          int p = pb << 4 | pc;
          int c = spvPal16TS[p];
          return String.format ("8x8 n=%d x=%d y=%d p=0x%02X c=0x%04X(%d,%d,%d,%d)",
                                n, x, y, p, c,
                                c >> 11, (c >> 6) & 31, (c >> 1) & 31, c & 1);
        } else {  //16x16
          x -= 2;
          y -= 2;
          if (x < 0 || 2 * 16 <= x ||
              y < 0 || 2 * 16 <= y) {  //パターン範囲外
            return null;
          }
          x >>= 1;  //16x16パターン内X座標
          y >>= 1;  //16x16パターン内Y座標
          int pb = spvBlockArray[n];  //パレットブロック
          if (pb < 0) {
            pb = spvLastBlockArray[n];
          }
          int vh = spvFlipArray[n];  //反転
          if (vh < 0) {
            vh = spvLastFlipArray[n];
          }
          int v = vh >> 1;
          int h = vh & 1;
          if ((vh & 1) != 0) {  //水平反転
            x ^= 15;
          }
          if ((vh & 2) != 0) {  //垂直反転
            y ^= 15;
          }
          int pc = (spvPat[(n << 3) + y + ((x & 8) << 1)] >> ((7 - (x & 7)) << 2)) & 15;  //パレットコード
          int p = pb << 4 | pc;
          int c = spvPal16TS[p];
          return String.format ("16x16 n=%d x=%d y=%d p=0x%02X c=0x%04X(%d,%d,%d,%d)",
                                n >> 2, x, y, p, c,
                                c >> 11, (c >> 6) & 31, (c >> 1) & 31, c & 1);
        }  //if 8x8 else 16x16
      }  //spvGetPixel

      //spvUpdateFrame ()
      //  更新する
      static void spvUpdateFrame () {
        if (spvFrame == null) {  //未初期化
          return;
        }
        //停止/動作
        if (spvStopped != spvStoppedRequest) {
          if (spvStoppedRequest) {  //動作→停止
            spvSetStoppedOn ();
          } else {  //停止→動作
            spvSetStoppedOff ();
          }
        }
        //パレットテーブルを作る
        for (int i = 0; i < 256; i++) {
          spvPal32TS[i] = spvPalTbl[spvPal16TS[i]];
        }
        //情報を集める
        Arrays.fill (spvSizeArray, spvSizeNumber);
        Arrays.fill (spvBlockArray, spvBlockNumber);
        Arrays.fill (spvFlipArray, spvFlipNumber);
        //自動の要素があるときだけ集める
        if (spvSizeNumber < 0 ||
            spvBlockNumber < 0 ||
            spvFlipNumber < 0) {  //自動の要素がある
          //スプライトから集める
          for (int sn = 0; sn < SpriteScreen.sprNumberOfSprites; sn++) {  //スプライト番号
            if ((256 <= sn && sn < 256 + 8) ||  //欠番
                spvPrw[sn] == 0) {  //表示されていない
              continue;
            }
            int n = spvNum[sn] << 2;  //8x8パターン番号
            if (spvSizeArray[n >> 2] < 0) {
              spvSizeArray[n >> 2] =
                spvLastSizeArray[n >> 2] = 1;  //16x16
            }
            int pb = spvCol[sn] >> 4;  //パレットブロック
            int vh = ((spvV[sn] ? 2 : 0) |
                      (spvH[sn] ? 1 : 0));  //反転
            for (int k = 0; k < 4; k++) {
              if (spvBlockArray[n + k] < 0) {
                spvBlockArray[n + k] =
                  spvLastBlockArray[n + k] = pb;
              }
              if (spvFlipArray[n + k] < 0) {
                spvFlipArray[n + k] =
                  spvLastFlipArray[n + k] = vh;
              }
            }
          }  //for sn
          //バックグラウンドから集める
          int tm = 0;  //表示されているテキストページのマスク
          if ((SpriteScreen.sprReg4BgCtrlPort & 1) != 0) {  //BG0が表示されている
            tm |= 1 << ((SpriteScreen.sprReg4BgCtrlPort >> 1) & 3);  //BG0に割り当てられているテキストページ
          }
          if (((SpriteScreen.sprReg8ResoPort & 3) == 0 ||  //256x256または
               SpriteScreen.spr512bg1) &&  //512x512でBG1を表示かつ
              (SpriteScreen.sprReg4BgCtrlPort & 8) != 0) {  //BG1が表示されている
            tm |= 1 << ((SpriteScreen.sprReg4BgCtrlPort >> 4) & 3);  //BG1に割り当てられているテキストページ
          }
          if ((SpriteScreen.sprReg8ResoPort & 3) == 0) {  //256x256
            for (int tp = 0; tp < 4; tp++) {  //テキストページ
              if ((tm & (1 << tp)) == 0) {  //表示されていない
                continue;
              }
              int o = 4096 * tp;  //テキスト配列インデックス開始位置
              for (int i = 0; i < 4096; i++) {  //テキスト配列インデックス
                int n = spvTNum[o + i] >> 3;  //8x8パターン番号
                if (spvSizeArray[n >> 2] < 0) {
                  spvSizeArray[n >> 2] =
                    spvLastSizeArray[n >> 2] = 0;  //8x8
                }
                int pb = spvTCol[o + i] >> 4;  //パレットブロック
                int vh = ((spvTV[o + i] ? 2 : 0) |
                          (spvTH[o + i] ? 1 : 0));  //反転
                if (spvBlockArray[n] < 0) {
                  spvBlockArray[n] =
                    spvLastBlockArray[n] = pb;
                }
                if (spvFlipArray[n] < 0) {
                  spvFlipArray[n] =
                    spvLastFlipArray[n] = vh;
                }
              }  //for i
            }  //for tp
          } else {  //512x512
            for (int tp = 0; tp < 4; tp++) {  //テキストページ
              if ((tm & (1 << tp)) == 0) {  //表示されていない
                continue;
              }
              int o = 4096 * tp;  //テキスト配列インデックス開始位置
              for (int i = 0; i < 4096; i++) {  //テキスト配列インデックス
                int n = spvTNum[o + i] >> 1;  //8x8パターン番号
                if (spvSizeArray[n >> 2] < 0) {
                  spvSizeArray[n >> 2] =
                    spvLastSizeArray[n >> 2] = 1;  //16x16
                }
                int pb = spvTCol[o + i] >> 4;  //パレットブロック
                int vh = ((spvTV[o + i] ? 2 : 0) |
                          (spvTH[o + i] ? 1 : 0));  //反転
                for (int k = 0; k < 4; k++) {
                  if (spvBlockArray[n + k] < 0) {
                    spvBlockArray[n + k] =
                      spvLastBlockArray[n + k] = pb;
                  }
                  if (spvFlipArray[n + k] < 0) {
                    spvFlipArray[n + k] =
                      spvLastFlipArray[n + k] = vh;
                  }
                }
              }  //for i
            }  //for tp
          }  //if 256x256 else 512x512
          //集められなかったものは前回と同じにする
          for (int n = 0; n < 4096; n++) {  //16x16パターン番号
            if (spvSizeArray[n] < 0) {
              spvSizeArray[n] = spvLastSizeArray[n];
            }
          }
          for (int n = 0; n < 4 * 4096; n++) {  //8x8パターン番号
            if (spvBlockArray[n] < 0) {
              spvBlockArray[n] = spvLastBlockArray[n];
            }
            if (spvFlipArray[n] < 0) {
              spvFlipArray[n] = spvLastFlipArray[n];
            }
          }
        }  //if 自動の要素がある
        //パターンを表示する
        int a = 32 * spvCellOffset;  //パターン配列インデックス
        for (int cRow = 0; cRow <= spvRowsMask; cRow++) {  //セル行
          int cY = spvRowToY (cRow);  //セル左上(0,0)Y座標
          for (int cCol = 0; cCol <= spvColsMask; cCol++) {  //セル桁
            int cX = spvColToX (cCol);  //セル左上(0,0)X座標
            int cIndex = cX + spvImageWidth * cY;  //セル左上ビットマップインデックス
            //背景色で塗り潰す
            //  サイズが変わるときゴミが残らないようにする
            int i = cIndex;
            for (int v = 0; v < SPV_CELL_HEIGHT; v++) {
              for (int h = 0; h < SPV_CELL_WIDTH; h++) {
                spvBitmap[i + h] = SPV_BACKGROUND_RGB;
              }
              i += spvImageWidth;  //(+0,+1)
            }
            int n = (spvCellOffset + cCol + (cRow << spvColsBit)) << 2;  //8x8パターン番号
            if (spvSizeArray[n >> 2] == 0) {  //8x8
              for (int qCol = 0; qCol < 2; qCol++) {  //1/4セル桁
                int qX = cX + SPV_CELL_WIDTH / 2 * qCol;  //1/4セル左上X座標
                for (int qRow = 0; qRow < 2; qRow++) {  //1/4セル行
                  int qY = cY + SPV_CELL_HEIGHT / 2 * qRow;  //1/4セル左上Y座標
                  int qIndex = qX + spvImageWidth * qY;  //1/4セル左上ビットマップインデックス
                  int pb = spvBlockArray[n];  //パレットブロック
                  int vh = spvFlipArray[n];  //反転
                  int hMask = (vh & 1) != 0 ? 7 : 0;  //左右反転マスク
                  int vMask = (vh & 2) != 0 ? 15 : 0;  //上下反転マスク
                  //灰色の線とパレットブロックを表す点
                  i = qIndex + (hMask == 0 ? 0 + spvImageWidth * 1 :
                                1 + 2 * 8 + spvImageWidth * 1);  //左上ビットマップインデックス
                  for (int v = 0; v <= 15; v++) {  //描画方向。上0→下15
                    int vm = v ^ vMask;  //参照方向。上0→下15または下15→上0
                    if (vm == pb) {  //パレットブロックの点
                      spvBitmap[i] = SPV_BLOCK_DOT_RGB;
                    } else if (vm < 8) {  //灰色の線
                      spvBitmap[i] = SPV_GRAY_LINE_RGB;
                    }
                    i += spvImageWidth * 1;  //(+0,+1)
                  }
                  //パターン
                  pb <<= 4;  //パレットブロック<<4
                  vMask >>= 1;
                  i = qIndex + 1 + spvImageWidth * 1;  //左上(1,1)ビットマップインデックス
                  for (int v = 0; v <= 7; v++) {  //描画方向。上0→下7
                    int vm = v ^ vMask;  //参照方向。上0→下7または下7→上0
                    int d = spvPat[a + vm];
                    for (int h = 7; 0 <= h; h--) {  //描画方向。左7→右0
                      int hm = h ^ hMask;  //参照方向。左7→右0または右0→左7
                      spvBitmap[i] =
                        spvBitmap[i + 1] =
                          spvBitmap[i + spvImageWidth] =
                            spvBitmap[i + spvImageWidth + 1] = spvPal32TS[pb + ((d >> (hm << 2)) & 15)];
                      i += 2;  //(+2,+0)
                    }  //for h
                    i += spvImageWidth * 2 - 2 * 8;  //(-2*8,+2)
                  }  //for v
                  a += 8;
                  n++;
                }  //for qRow
              }  //for qCol
            } else {  //16x16
              int pb = spvBlockArray[n];  //パレットブロック
              int vh = spvFlipArray[n];  //反転
              int hMask = (vh & 1) != 0 ? 15 : 0;  //左右反転マスク
              int vMask = (vh & 2) != 0 ? 15 : 0;  //上下反転マスク
              //灰色の線とパレットブロックの点
              i = cIndex + (hMask == 0 ? 0 + spvImageWidth * 2 :
                            2 + 2 * 16 + spvImageWidth * 2);  //左上ビットマップインデックス
              for (int v = 0; v <= 15; v++) {  //描画方向。上0→下15
                int vm = v ^ vMask;  //参照方向。上0→下15または下15→上0
                if (vm == pb) {  //パレットブロックの点
                  spvBitmap[i] =
                    spvBitmap[i + 1] =
                      spvBitmap[i + spvImageWidth] =
                        spvBitmap[i + spvImageWidth + 1] = SPV_BLOCK_DOT_RGB;
                } else if (vm < 8) {  //灰色の線
                  spvBitmap[i] =
                    spvBitmap[i + 1] =
                      spvBitmap[i + spvImageWidth] =
                        spvBitmap[i + spvImageWidth + 1] = SPV_GRAY_LINE_RGB;
                }
                i += spvImageWidth * 2;  //(+0,+2)
              }
              //パターン
              pb <<= 4;  //パレットブロック<<4
              i = cIndex + 2 + spvImageWidth * 2;  //左上(2,2)ビットマップインデックス
              for (int v = 0; v <= 15; v++) {  //描画方向。上0→下15
                int vm = v ^ vMask;  //参照方向。上0→下15または下15→上0
                long d = ((long) spvPat[a + vm] << 32 |
                          (long) spvPat[a + vm + 16] & 0xffffffffL);
                for (int h = 15; 0 <= h; h--) {  //描画方向。左15→右0
                  int hm = h ^ hMask;  //参照方向。左15→右0または右0→左15
                  spvBitmap[i] =
                    spvBitmap[i + 1] =
                      spvBitmap[i + spvImageWidth] =
                        spvBitmap[i + spvImageWidth + 1] = spvPal32TS[pb + ((int) (d >> (hm << 2)) & 15)];
                  i += 2;  //(+2,+0)
                }  //for h
                i += spvImageWidth * 2 - 2 * 16;  //(-2*16,+2)
              }  //for v
              a += 32;
            }  //if 8x8 else 16x16
          }  //for cCol
        }  //for cRow
        spvCanvas.repaint ();
      }  //spvUpdateFrame

      //spvSetStoppedOn ()
      //  停止する
      static void spvSetStoppedOn () {
        spvStopped = true;
        spvStoppedRequest = true;
        //現在の値をコピーする
        System.arraycopy (SpriteScreen.sprNumPort, 0, spvCopiedNum, 0, SpriteScreen.SPR_COUNT);
        System.arraycopy (SpriteScreen.sprColPort, 0, spvCopiedCol, 0, SpriteScreen.SPR_COUNT);
        System.arraycopy (SpriteScreen.sprPrw, 0, spvCopiedPrw, 0, SpriteScreen.SPR_COUNT);
        System.arraycopy (SpriteScreen.sprH, 0, spvCopiedH, 0, SpriteScreen.SPR_COUNT);
        System.arraycopy (SpriteScreen.sprV, 0, spvCopiedV, 0, SpriteScreen.SPR_COUNT);
        System.arraycopy (SpriteScreen.sprPatPort, 0, spvCopiedPat, 0, 32 * 4096);
        System.arraycopy (SpriteScreen.sprTNum, 0, spvCopiedTNum, 0, 4096 * 4);
        System.arraycopy (SpriteScreen.sprTColPort, 0, spvCopiedTCol, 0, 4096 * 4);
        System.arraycopy (SpriteScreen.sprTH, 0, spvCopiedTH, 0, 4096 * 4);
        System.arraycopy (SpriteScreen.sprTV, 0, spvCopiedTV, 0, 4096 * 4);
        System.arraycopy (VideoController.vcnPalBase[VideoController.VCN_CONTRAST_SCALE * 15], 0, spvCopiedPalTbl, 0, 65536);
        System.arraycopy (VideoController.vcnPal16TSPort, 0, spvCopiedPal16TS, 0, 256);
        //コピーされた値を参照する
        spvNum = spvCopiedNum;
        spvCol = spvCopiedCol;
        spvPrw = spvCopiedPrw;
        spvH = spvCopiedH;
        spvV = spvCopiedV;
        spvPat = spvCopiedPat;
        spvTNum = spvCopiedTNum;
        spvTCol = spvCopiedTCol;
        spvTH = spvCopiedTH;
        spvTV = spvCopiedTV;
        spvPalTbl = spvCopiedPalTbl;
        spvPal16TS = spvCopiedPal16TS;
      }  //spvSetStoppedOn

      //spvSetStoppedOff ()
      //  停止を解除する
      static void spvSetStoppedOff () {
        spvStopped = false;
        spvStoppedRequest = false;
        //現在の値を参照する
        spvNum = SpriteScreen.sprNumPort;
        spvCol = SpriteScreen.sprColPort;
        spvPrw = SpriteScreen.sprPrw;
        spvH = SpriteScreen.sprH;
        spvV = SpriteScreen.sprV;
        spvPat = SpriteScreen.sprPatPort;
        spvTNum = SpriteScreen.sprTNum;
        spvTCol = SpriteScreen.sprTColPort;
        spvTH = SpriteScreen.sprTH;
        spvTV = SpriteScreen.sprTV;
        spvPalTbl = VideoController.vcnPalBase[VideoController.VCN_CONTRAST_SCALE * 15];
        spvPal16TS = VideoController.vcnPal16TSPort;
      }  //spvSetStoppedOff

    }  //class SpritePatternViewer

STUDIO KAMADA
Copyright © 1999-2026 Makoto Kamada