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