SpritePatternViewer.java
     1: //========================================================================================
     2: //  SpritePatternViewer.java
     3: //    en:Sprite pattern viewer
     4: //    ja:スプライトパターンビュア
     5: //  Copyright (C) 2003-2025 Makoto Kamada
     6: //
     7: //  This file is part of the XEiJ (X68000 Emulator in Java).
     8: //  You can use, modify and redistribute the XEiJ if the conditions are met.
     9: //  Read the XEiJ License for more details.
    10: //  https://stdkmd.net/xeij/
    11: //========================================================================================
    12: 
    13: package xeij;
    14: 
    15: import java.awt.*;
    16: import java.awt.event.*;  //MouseEvent
    17: import java.awt.font.*;  //TextLayout
    18: import java.awt.geom.*;  //Rectangle2D
    19: import java.awt.image.*;  //BufferedImage
    20: import java.util.*;  //Arrays
    21: import javax.swing.*;
    22: 
    23: public class SpritePatternViewer {
    24: 
    25:   public static final boolean SPV_ON = true;
    26: 
    27:   //セル
    28:   static final int SPV_CELL_WIDTH = 36;  //セルの幅。偶数
    29:   static final int SPV_CELL_HEIGHT = 36;  //セルの高さ
    30: 
    31:   //グループ
    32:   //  グループのサイズは4x4セルに固定
    33:   static final int SPV_GROUP_GAP = 4;  //グループの間隔
    34: 
    35:   //ヘッダ
    36:   static final String SPV_FONT_NAME = "Dialog";  //フォント名
    37:   static final int SPV_FONT_STYLE = Font.BOLD;  //フォントスタイル
    38:   static final int SPV_FONT_SIZE = 16;  //フォントサイズ
    39:   static final int SPV_HEADER_WIDTH = SPV_FONT_SIZE * 3;  //左ヘッダの幅
    40:   static final int SPV_HEADER_HEIGHT = SPV_FONT_SIZE;  //上ヘッダの高さ
    41: 
    42:   //イメージ
    43:   static int spvImageWidth;  //イメージ幅
    44:   static int spvImageHeight;  //イメージ高さ
    45:   static BufferedImage spvBufferedImage;  //イメージ
    46:   static int[] spvBitmap;  //ビットマップ
    47:   static ScrollCanvas spvCanvas;  //キャンバス
    48: 
    49:   //色
    50:   static final int SPV_BACKGROUND_RGB = 0xff333333;  //背景色
    51:   static final int SPV_FOREGROUND_RGB = 0xffcccccc;  //文字色
    52:   static final int SPV_GRAY_LINE_RGB = 0xff999999;  //灰色の線の色
    53:   static final int SPV_BLOCK_DOT_RGB = 0xffffffff;  //パレットブロックの点の色
    54: 
    55:   //バンク
    56:   //  0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16  17  18   19    20
    57:   //  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
    58:   static final int SPV_DEF_BANK = 0;
    59:   static final int SPV_MIN_BANK = 0;
    60:   static final int SPV_MAX_BANK = 20;
    61:   static int spvBankNumber;  //バンク番号
    62:   static JComboBox<String> spvBankComboBox;  //バンクドロップダウンリスト
    63:   static int spvCellOffset;  //開始セル番号
    64:   static int spvColsBit;  //セル桁数のビット
    65:   static int spvRowsBit;  //セル行数のビット
    66:   static int spvColsMask;  //セル桁数のマスク
    67:   static int spvRowsMask;  //セル行数のマスク
    68:   static int spvWidth;  //幅
    69:   static int spvHeight;  //高さ
    70: 
    71:   //サイズ
    72:   //  -1   0   1
    73:   //  Auto 8x8 16x16
    74:   static final int SPV_DEF_SIZE = -1;
    75:   static final int SPV_MIN_SIZE = -1;
    76:   static final int SPV_MAX_SIZE = 1;
    77:   static final int SPV_LAST_SIZE = 1;  //前回のサイズの初期値
    78:   static int spvSizeNumber;  //サイズ番号
    79:   static JComboBox<String> spvSizeComboBox;  //サイズドロップダウンリスト
    80:   static final int[] spvLastSizeArray = new int[4096];  //前回のサイズ
    81:   static final int[] spvSizeArray = new int[4096];  //サイズ
    82: 
    83:   //パレットブロック
    84:   //  -1   0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
    85:   //  Auto 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
    86:   static final int SPV_DEF_BLOCK = -1;
    87:   static final int SPV_MIN_BLOCK = -1;
    88:   static final int SPV_MAX_BLOCK = 15;
    89:   static final int SPV_LAST_BLOCK = 1;  //前回のパレットブロックの初期値
    90:   static int spvBlockNumber;  //ブロック番号
    91:   static JComboBox<String> spvBlockComboBox;  //ブロックドロップダウンリスト
    92:   static final int[] spvLastBlockArray = new int[4 * 4096];  //前回のパレットブロック
    93:   static final int[] spvBlockArray = new int[4 * 4096];  //パレットブロック
    94: 
    95:   //反転
    96:   //  -1   0 1 2 3
    97:   //  Auto - H V H&V
    98:   static final int SPV_DEF_FLIP = -1;
    99:   static final int SPV_MIN_FLIP = -1;
   100:   static final int SPV_MAX_FLIP = 3;
   101:   static final int SPV_LAST_FLIP = 0;  //前回の反転の初期値
   102:   static int spvFlipNumber;  //反転番号
   103:   static JComboBox<String> spvFlipComboBox;  //反転ドロップダウンリスト
   104:   static final int[] spvLastFlipArray = new int[4 * 4096];  //前回の反転
   105:   static final int[] spvFlipArray = new int[4 * 4096];  //反転。bit1=上下反転,bit0=左右反転
   106: 
   107:   //倍率
   108:   //  -1 0 1 2 3  4
   109:   //  1  2 4 8 16 32
   110:   static final int SPV_DEF_SCALE = 0;
   111:   static final int SPV_MIN_SCALE = -1;
   112:   static final int SPV_MAX_SCALE = 4;
   113:   static int spvScaleNumber;  //倍率番号
   114:   static JComboBox<String> spvScaleComboBox;  //倍率ドロップダウンリスト
   115: 
   116:   //Hex
   117:   //  off    on
   118:   //  10進数 16進数
   119:   static final boolean SPV_DEF_HEX = false;
   120:   static boolean spvHex;
   121: 
   122:   //停止
   123:   static boolean spvStopped;  //true=停止中
   124:   static boolean spvStoppedRequest;
   125:   //  コピーされた値
   126:   static final short[] spvCopiedNum = new short[SpriteScreen.SPR_COUNT];
   127:   static final short[] spvCopiedCol = new short[SpriteScreen.SPR_COUNT];
   128:   static final byte[] spvCopiedPrw = new byte[SpriteScreen.SPR_COUNT];
   129:   static final boolean[] spvCopiedH = new boolean[SpriteScreen.SPR_COUNT];
   130:   static final boolean[] spvCopiedV = new boolean[SpriteScreen.SPR_COUNT];
   131:   static final int[] spvCopiedPat = new int[32 * 4096];
   132:   static final short[] spvCopiedTNum = new short[4096 * 4];
   133:   static final short[] spvCopiedTCol = new short[4096 * 4];
   134:   static final boolean[] spvCopiedTH = new boolean[4096 * 4];
   135:   static final boolean[] spvCopiedTV = new boolean[4096 * 4];
   136:   static final int[] spvCopiedPalTbl = new int[65536];
   137:   static final int[] spvCopiedPal16TS = new int[256];
   138:   //  参照する値。停止中はコピーされた値、さもなくば現在の値
   139:   static short[] spvNum;
   140:   static short[] spvCol;
   141:   static byte[] spvPrw;
   142:   static boolean[] spvH;
   143:   static boolean[] spvV;
   144:   static int[] spvPat;
   145:   static short[] spvTNum;
   146:   static short[] spvTCol;
   147:   static boolean[] spvTH;
   148:   static boolean[] spvTV;
   149:   static int[] spvPalTbl;
   150:   static int[] spvPal16TS;
   151:   //
   152:   static final int[] spvPal32TS = new int[256];
   153: 
   154:   //テキストフィールド
   155:   static JTextField spvTextField;
   156:   static boolean spvTextLocked;
   157: 
   158:   //ウインドウ
   159:   static JFrame spvFrame;
   160: 
   161:   //ポップアップメニュー
   162:   static JPopupMenu spvPopupMenu;
   163:   static int spvOffsetToCopy;
   164:   static int spvLengthToCopy;
   165: 
   166:   //タイマー
   167:   public static final int SPV_INTERVAL = 10;
   168:   public static int spvTimer;
   169: 
   170:   //spvInit ()
   171:   //  初期化
   172:   public static void spvInit () {
   173:     //パラメータ
   174:     spvBankNumber = Settings.sgsGetInt ("spvbank", SPV_DEF_BANK, SPV_MIN_BANK, SPV_MAX_BANK);
   175:     spvBlockNumber = Settings.sgsGetInt ("spvblock", SPV_DEF_BLOCK, SPV_MIN_BLOCK, SPV_MAX_BLOCK);
   176:     spvFlipNumber = Settings.sgsGetInt ("spvflip", SPV_DEF_FLIP, SPV_MIN_FLIP, SPV_MAX_FLIP);
   177:     spvHex = Settings.sgsGetOnOff ("spvhex", SPV_DEF_HEX);
   178:     spvScaleNumber = Settings.sgsGetInt ("spvscale", SPV_DEF_SCALE, SPV_MIN_SCALE, SPV_MAX_SCALE);
   179:     spvSizeNumber = Settings.sgsGetInt ("spvsize", SPV_DEF_SIZE, SPV_MIN_SIZE, SPV_MAX_SIZE);
   180:     //イメージ
   181:     spvImageWidth = (SPV_HEADER_WIDTH +
   182:                      (SPV_CELL_WIDTH << 6) +
   183:                      (SPV_GROUP_GAP << (6 - 2)) - SPV_GROUP_GAP);
   184:     spvImageHeight = (SPV_HEADER_HEIGHT +
   185:                       (SPV_CELL_HEIGHT << 6) +
   186:                       (SPV_GROUP_GAP << (6 - 2)) - SPV_GROUP_GAP);
   187:     //バンク
   188:     spvSetBankNumber (spvBankNumber);
   189:     //サイズ
   190:     Arrays.fill (spvLastSizeArray, SPV_LAST_SIZE);
   191:     //パレットブロック
   192:     Arrays.fill (spvLastBlockArray, SPV_LAST_BLOCK);
   193:     //反転
   194:     Arrays.fill (spvLastFlipArray, SPV_LAST_FLIP);
   195:     //停止
   196:     spvSetStoppedOff ();
   197:     //ウインドウ
   198:     spvFrame = null;
   199:     //タイマー
   200:     spvTimer = 0;
   201:   }  //spvInit
   202: 
   203:   //spvTini ()
   204:   //  後始末
   205:   public static void spvTini () {
   206:     //パラメータ
   207:     Settings.sgsPutInt ("spvbank", spvBankNumber);
   208:     Settings.sgsPutInt ("spvblock", spvBlockNumber);
   209:     Settings.sgsPutInt ("spvflip", spvFlipNumber);
   210:     Settings.sgsPutOnOff ("spvhex", spvHex);
   211:     Settings.sgsPutInt ("spvscale", spvScaleNumber);
   212:     Settings.sgsPutInt ("spvsize", spvSizeNumber);
   213:   }  //spvTini
   214: 
   215:   //spvSetBankNumber (number)
   216:   //  バンク番号を設定する
   217:   static void spvSetBankNumber (int number) {
   218:     spvBankNumber = number;
   219:     spvCellOffset = (spvBankNumber < 16 ? spvBankNumber << 8 :
   220:                      spvBankNumber < 20 ? (spvBankNumber - 16) << 10 :
   221:                      0);  //(spvBankNumber-20)<<12
   222:     spvColsBit =
   223:       spvRowsBit = (spvBankNumber < 16 ? 4 :  //16x16
   224:                     spvBankNumber < 20 ? 5 :  //32x32
   225:                     6);  //64x64
   226:     spvColsMask =
   227:       spvRowsMask = (1 << spvRowsBit) - 1;
   228:     spvWidth = (SPV_HEADER_WIDTH +
   229:                 (SPV_CELL_WIDTH << spvColsBit) +
   230:                 (SPV_GROUP_GAP << (spvColsBit - 2)) - SPV_GROUP_GAP);
   231:     spvHeight = (SPV_HEADER_HEIGHT +
   232:                  (SPV_CELL_HEIGHT << spvRowsBit) +
   233:                  (SPV_GROUP_GAP << (spvRowsBit - 2)) - SPV_GROUP_GAP);
   234:     if (spvCanvas != null) {
   235:       spvDrawHeader ();
   236:       spvCanvas.setImage (spvWidth, spvHeight);
   237:     }
   238:   }  //spvSetBankNumber
   239: 
   240:   //x = spvColToX (col)
   241:   //  セル桁からX座標を求める
   242:   static int spvColToX (int col) {
   243:     int g = col >> 2;  //グループ
   244:     col &= 3;  //グループ内セル桁
   245:     return (SPV_HEADER_WIDTH +
   246:             ((SPV_CELL_WIDTH << 2) + SPV_GROUP_GAP) * g +
   247:             SPV_CELL_WIDTH * col);
   248:   }  //spvColToX
   249: 
   250:   //y = spvRowToY (row)
   251:   //  セル行からY座標を求める
   252:   static int spvRowToY (int row) {
   253:     int g = row >> 2;  //グループ
   254:     row &= 3;  //グループ内セル行
   255:     return (SPV_HEADER_HEIGHT +
   256:             ((SPV_CELL_HEIGHT << 2) + SPV_GROUP_GAP) * g +
   257:             SPV_CELL_HEIGHT * row);
   258:   }  //spvRowToY
   259: 
   260:   //((x << 16) | col) = spvXToXCol (x)
   261:   //  X座標からセル内X座標とセル桁を求める。-1=範囲外
   262:   static int spvXToXCol (int x) {
   263:     if (x < 0 || spvWidth <= x) {  //表示範囲の外
   264:       return -1;
   265:     }
   266:     if (x < SPV_HEADER_WIDTH) {  //左ヘッダ
   267:       return -1;
   268:     }
   269:     x -= SPV_HEADER_WIDTH;
   270:     int g = x / ((SPV_CELL_WIDTH << 2) + SPV_GROUP_GAP);  //グループ
   271:     x -= ((SPV_CELL_WIDTH << 2) + SPV_GROUP_GAP) * g;  //グループ内X座標
   272:     if ((SPV_CELL_WIDTH << 2) <= x) {  //グループの間隔
   273:       return -1;
   274:     }
   275:     int col = x / SPV_CELL_WIDTH;  //グループ内セル桁
   276:     x -= SPV_CELL_WIDTH * col;  //セル内X座標
   277:     return (x << 16) | ((g << 2) + col);
   278:   }  //spvXToXCol
   279: 
   280:   //((y << 16) | row) = spvYToYRow (y)
   281:   //  Y座標からセル内Y座標とセル行を求める。-1=範囲外
   282:   static int spvYToYRow (int y) {
   283:     if (y < 0 || spvHeight <= y) {  //表示範囲の外
   284:       return -1;
   285:     }
   286:     if (y < SPV_HEADER_HEIGHT) {  //上ヘッダ
   287:       return -1;
   288:     }
   289:     y -= SPV_HEADER_HEIGHT;
   290:     int g = y / ((SPV_CELL_HEIGHT << 2) + SPV_GROUP_GAP);  //グループ
   291:     y -= ((SPV_CELL_HEIGHT << 2) + SPV_GROUP_GAP) * g;  //グループ内Y座標
   292:     if ((SPV_CELL_HEIGHT << 2) <= y) {  //グループの間隔
   293:       return -1;
   294:     }
   295:     int row = y / SPV_CELL_HEIGHT;  //グループ内セル行
   296:     y -= SPV_CELL_HEIGHT * row;  //セル内Y座標
   297:     return (y << 16) | ((g << 2) + row);
   298:   }  //spvYToYRow
   299: 
   300:   //spvStart ()
   301:   //  開始
   302:   public static void spvStart () {
   303:     if (RestorableFrame.rfmGetOpened (Settings.SGS_SPV_FRAME_KEY)) {
   304:       spvOpen ();
   305:     }
   306:   }  //spvStart
   307: 
   308:   //spvOpen ()
   309:   //  開く
   310:   public static void spvOpen () {
   311:     if (spvFrame == null) {
   312:       spvMakeFrame ();
   313:     } else {
   314:       spvUpdateFrame ();
   315:     }
   316:     XEiJ.dbgVisibleMask |= XEiJ.DBG_SPV_VISIBLE_MASK;
   317:     XEiJ.pnlExitFullScreen (false);
   318:     spvFrame.setVisible (true);
   319:   }  //spvOpen
   320: 
   321:   //spvMakeFrame ()
   322:   //  作る
   323:   //  ここでは開かない
   324:   static void spvMakeFrame () {
   325:     //イメージ
   326:     spvBufferedImage = new BufferedImage (spvImageWidth, spvImageHeight, BufferedImage.TYPE_INT_ARGB);
   327:     spvDrawHeader ();
   328:     //ビットマップ
   329:     spvBitmap = ((DataBufferInt) spvBufferedImage.getRaster ().getDataBuffer ()).getData ();
   330:     //キャンバス
   331:     spvCanvas = new ScrollCanvas (spvBufferedImage, spvWidth, spvHeight);
   332:     spvCanvas.setMatColor (new Color (SPV_BACKGROUND_RGB));
   333:     spvCanvas.setMinScaleShift (SPV_MIN_SCALE);
   334:     spvCanvas.setMaxScaleShift (SPV_MAX_SCALE);
   335:     spvCanvas.setScaleShift (spvScaleNumber);
   336:     //アクションリスナー
   337:     ActionListener listener = new ActionListener () {
   338:       @Override public void actionPerformed (ActionEvent ae) {
   339:         Object source = ae.getSource ();
   340:         String command = ae.getActionCommand ();
   341:         switch (command) {
   342:         case "Bank":
   343:           spvSetBankNumber (spvBankComboBox.getSelectedIndex () + SPV_MIN_BANK);
   344:           if (XEiJ.mpuTask == null) {
   345:             spvUpdateFrame ();
   346:           }
   347:           break;
   348:         case "Size":
   349:           spvSizeNumber = spvSizeComboBox.getSelectedIndex () + SPV_MIN_SIZE;
   350:           if (XEiJ.mpuTask == null) {
   351:             spvUpdateFrame ();
   352:           }
   353:           break;
   354:         case "Block":
   355:           spvBlockNumber = spvBlockComboBox.getSelectedIndex () + SPV_MIN_BLOCK;
   356:           if (XEiJ.mpuTask == null) {
   357:             spvUpdateFrame ();
   358:           }
   359:           break;
   360:         case "Flip":
   361:           spvFlipNumber = spvFlipComboBox.getSelectedIndex () + SPV_MIN_FLIP;
   362:           if (XEiJ.mpuTask == null) {
   363:             spvUpdateFrame ();
   364:           }
   365:           break;
   366:         case "Hex":
   367:           spvHex = (((JCheckBox) source).isSelected ());
   368:           spvDrawHeader ();
   369:           spvUpdateFrame ();
   370:           break;
   371:         case "Stop":
   372:           spvStoppedRequest = (((JCheckBox) source).isSelected ());
   373:           if (XEiJ.mpuTask == null) {
   374:             spvUpdateFrame ();
   375:           }
   376:           break;
   377:         case "Scale":
   378:           spvCanvas.setScaleShift (spvScaleComboBox.getSelectedIndex () + SPV_MIN_SCALE);
   379:           break;
   380:         case "Copy as hexadecimal":
   381:           spvCopyPattern ();
   382:           break;
   383:         default:
   384:           System.out.println ("unknown action command " + command);
   385:         }
   386:       }
   387:     };
   388:     //テキストフィールド
   389:     spvTextField = new JTextField ();
   390:     spvTextField.setEditable (false);
   391:     spvTextField.setHorizontalAlignment (JTextField.CENTER);
   392:     //ウインドウ
   393:     spvFrame = Multilingual.mlnTitle (
   394:       ComponentFactory.createRestorableSubFrame (
   395:         Settings.SGS_SPV_FRAME_KEY,
   396:         "Sprite Pattern Viewer",
   397:         null,
   398:         ComponentFactory.createBorderPanel (
   399:           0, 0,
   400:           //CENTER
   401:           spvCanvas,
   402:           //NORTH
   403:           ComponentFactory.createVerticalBox (
   404:             //1行目
   405:             ComponentFactory.createHorizontalBox (
   406:               Box.createHorizontalGlue (),
   407:               //
   408:               Multilingual.mlnText (
   409:                 ComponentFactory.createLabel ("Bank "),
   410:                 "ja", "バンク "),
   411:               spvBankComboBox = ComponentFactory.createComboBox (
   412:                 spvBankNumber - SPV_MIN_BANK,
   413:                 "Bank",
   414:                 listener,
   415:                 "0", "1", "2", "3", "4", "5", "6", "7", "8",
   416:                 "9", "10", "11", "12", "13", "14", "15",
   417:                 "0-3", "4-7", "8-11", "12-15", "0-15"),
   418:               //
   419:               Multilingual.mlnText (
   420:                 ComponentFactory.createLabel ("Size "),
   421:                 "ja", "サイズ "),
   422:               spvSizeComboBox = ComponentFactory.createComboBox (
   423:                 spvSizeNumber - SPV_MIN_SIZE,
   424:                 "Size",
   425:                 listener,
   426:                 4,
   427:                 Multilingual.mlnJapanese ? "自動" : "Auto",
   428:                 "8x8", "16x16"),
   429:               //
   430:               Multilingual.mlnText (
   431:                 ComponentFactory.createLabel ("Block "),
   432:                 "ja", "ブロック "),
   433:               spvBlockComboBox = ComponentFactory.createComboBox (
   434:                 spvBlockNumber - SPV_MIN_BLOCK,
   435:                 "Block",
   436:                 listener,
   437:                 4,
   438:                 Multilingual.mlnJapanese ? "自動" : "Auto",
   439:                 "0", "1", "2", "3", "4", "5", "6", "7", "8",
   440:                 "9", "10", "11", "12", "13", "14", "15"),
   441:               //
   442:               Multilingual.mlnText (
   443:                 ComponentFactory.createLabel ("Flip "),
   444:                 "ja", "反転 "),
   445:               spvFlipComboBox = ComponentFactory.createComboBox (
   446:                 spvFlipNumber - SPV_MIN_FLIP,
   447:                 "Flip",
   448:                 listener,
   449:                 4,
   450:                 Multilingual.mlnJapanese ? "自動" : "Auto",
   451:                 Multilingual.mlnJapanese ? "なし" : "-",
   452:                 Multilingual.mlnJapanese ? "左右" : "H",
   453:                 Multilingual.mlnJapanese ? "上下" : "V",
   454:                 Multilingual.mlnJapanese ? "上下左右" : "H&V"),
   455:               //
   456:               Box.createHorizontalStrut (5),
   457:               ComponentFactory.createCheckBox (spvHex, "Hex", listener),
   458:               //
   459:               Box.createHorizontalGlue ()
   460:               ),  //HorizontalBox
   461:             //2行目
   462:             ComponentFactory.createHorizontalBox (
   463:               //
   464:               spvTextField,
   465:               //
   466:               Multilingual.mlnText (
   467:                 ComponentFactory.createLabel ("Scale "),
   468:                 "ja", "倍率 "),
   469:               spvScaleComboBox = ComponentFactory.createComboBox (
   470:                 spvCanvas.getScaleShift () - SPV_MIN_SCALE,
   471:                 "Scale",
   472:                 listener,
   473:                 "1", "2", "4", "8", "16", "32"),
   474:               //
   475:               Box.createHorizontalStrut (5),
   476:               Multilingual.mlnText (
   477:                 ComponentFactory.createCheckBox (spvStoppedRequest, "Stop", listener),
   478:                 "ja", "停止")
   479:               //
   480:               )  //HorizontalBox
   481:             )  //VerticalBox
   482:           )  //BorderPanel
   483:         ),  //SubFrame
   484:       "ja", "スプライトパターンビュア");
   485:     //スケールシフトリスナー
   486:     spvCanvas.addScaleShiftListener (new ScrollCanvas.ScaleShiftListener () {
   487:       @Override public void scaleShiftChanged (int scaleShift) {
   488:         spvScaleComboBox.setSelectedIndex (scaleShift - SPV_MIN_SCALE);
   489:       }
   490:     });
   491:     //ウインドウリスナー
   492:     ComponentFactory.addListener (
   493:       spvFrame,
   494:       new WindowAdapter () {
   495:         @Override public void windowClosing (WindowEvent we) {
   496:           XEiJ.dbgVisibleMask &= ~XEiJ.DBG_SPV_VISIBLE_MASK;
   497:         }
   498:       });
   499:     //ポップアップメニュー
   500:     spvPopupMenu = ComponentFactory.createPopupMenu (
   501:       Multilingual.mlnText (
   502:         ComponentFactory.createMenuItem ("Copy as hexadecimal", 'C', listener),
   503:         "ja", "16進数でコピー")
   504:       );
   505:     //マウスリスナー
   506:     MouseAdapter ma = new MouseAdapter () {
   507:       @Override public void mouseClicked (MouseEvent me) {
   508:         spvTextLocked = !spvTextLocked;  //テキストフィールドのロックを反転する
   509:       }  //mouseClicked
   510:       @Override public void mouseMoved (MouseEvent me) {
   511:         if (!spvTextLocked) {  //テキストフィールドがロックされていないとき
   512:           MouseEvent2D me2D = (MouseEvent2D) me;
   513:           int x = (int) me2D.getX2D ();
   514:           int y = (int) me2D.getY2D ();
   515:           String s = spvGetPixel (x, y);
   516:           spvTextField.setText (s == null ? "" : s);  //テキストフィールドに表示する
   517:         }
   518:       }  //mouseMoved
   519:       @Override public void mousePressed (MouseEvent me) {
   520:         spvShowPopup (me);
   521:       }  //mousePressed
   522:       @Override public void mouseReleased (MouseEvent me) {
   523:         spvShowPopup (me);
   524:       }  //mouseReleased
   525:     };
   526:     spvCanvas.addMouseListener (ma);
   527:     spvCanvas.addMouseMotionListener (ma);
   528:   }  //spvMakeFrame
   529: 
   530:   //spvDrawHeader ()
   531:   //  ヘッダを描く
   532:   static void spvDrawHeader () {
   533:     Graphics2D g2 = spvBufferedImage.createGraphics ();
   534:     //背景
   535:     g2.setColor (new Color (SPV_BACKGROUND_RGB));
   536:     g2.fillRect (0, 0, spvWidth, spvHeight);
   537:     //ヘッダ
   538:     g2.setColor (new Color (SPV_FOREGROUND_RGB));
   539:     g2.setFont (new Font (SPV_FONT_NAME, SPV_FONT_STYLE, SPV_FONT_SIZE));
   540:     //  上
   541:     for (int col = 0; col <= spvColsMask; col++) {
   542:       spvDrawString (g2,
   543:                      spvColToX (col) + SPV_CELL_WIDTH / 2,  //X座標
   544:                      spvRowToY (0) - SPV_FONT_SIZE / 12,  //Y座標
   545:                      String.format (spvHex ? "%X" : "%d",
   546:                                     col & spvColsMask),  //文字列
   547:                      SwingConstants.SOUTH);  //アンカー
   548:     }  //for col
   549:     //  左
   550:     for (int row = 0; row <= spvRowsMask; row++) {
   551:       spvDrawString (g2,
   552:                      spvColToX (0) - SPV_FONT_SIZE / 6,  //X座標
   553:                      spvRowToY (row) + SPV_CELL_HEIGHT / 2,  //Y座標
   554:                      String.format (spvHex ? "%X" : "%d",
   555:                                     spvCellOffset + ((row & spvRowsMask) << spvColsBit)),  //文字列
   556:                      SwingConstants.EAST);  //アンカー
   557:     }  //for row
   558:   }  //spvDrawHeader
   559: 
   560:   //spvDrawString (g, x, y, s, d)
   561:   //  文字列を描く
   562:   static void spvDrawString (Graphics2D g2, int x, int y, String s, int d) {
   563:     Font f = g2.getFont ();
   564:     FontRenderContext flc = g2.getFontRenderContext ();
   565:     TextLayout tl = new TextLayout (s, f, flc);
   566:     Rectangle2D r = tl.getBounds ();
   567:     int rx = (int) Math.round (r.getX ());
   568:     int ry = (int) Math.round (r.getY ());
   569:     int rw = (int) Math.round (r.getWidth ());
   570:     int rh = (int) Math.round (r.getHeight ());
   571:     switch (d) {
   572:     case SwingConstants.NORTH_WEST:
   573:       g2.drawString (s, x - rx, y - ry);
   574:       break;
   575:     case SwingConstants.NORTH:
   576:       g2.drawString (s, x - (rx + (rw >> 1)), y - ry);
   577:       break;
   578:     case SwingConstants.NORTH_EAST:
   579:       g2.drawString (s, x - (rx + rw), y - ry);
   580:       break;
   581:     case SwingConstants.WEST:
   582:       g2.drawString (s, x - rx, y - (ry + (rh >> 1)));
   583:       break;
   584:     case SwingConstants.CENTER:
   585:       g2.drawString (s, x - (rx + (rw >> 1)), y - (ry + (rh >> 1)));
   586:       break;
   587:     case SwingConstants.EAST:
   588:       g2.drawString (s, x - (rx + rw), y - (ry + (rh >> 1)));
   589:       break;
   590:     case SwingConstants.SOUTH_WEST:
   591:       g2.drawString (s, x - rx, y - (ry + rh));
   592:       break;
   593:     case SwingConstants.SOUTH:
   594:       g2.drawString (s, x - (rx + (rw >> 1)), y - (ry + rh));
   595:       break;
   596:     case SwingConstants.SOUTH_EAST:
   597:       g2.drawString (s, x - (rx + rw), y - (ry + rh));
   598:       break;
   599:     }
   600:   }  //spvDrawString
   601: 
   602:   //spvShowPopup (me)
   603:   //  ポップアップメニューを表示する
   604:   static void spvShowPopup (MouseEvent me) {
   605:     if (me.isPopupTrigger ()) {
   606:       MouseEvent2D me2D = (MouseEvent2D) me;
   607:       int x = (int) me2D.getX2D ();
   608:       int y = (int) me2D.getY2D ();
   609:       if (spvGetPatternToCopy (x, y)) {
   610:         Point p = spvCanvas.getPopupPoint (me2D);
   611:         spvPopupMenu.show (spvCanvas, p.x, p.y);
   612:       }
   613:     }
   614:   }  //spvShowPopup
   615: 
   616:   //f = spvGetPatternToCopy (x, y)
   617:   //  座標からspvPatのコピー範囲を求めてspvOffsetToCopyとspvLengthToCopyを設定する
   618:   //  1セルの長さは32、1/4セルの長さは8
   619:   static boolean spvGetPatternToCopy (int x, int y) {
   620:     int col = spvXToXCol (x);  //セル内X座標<<16|セル桁
   621:     int row = spvYToYRow (y);  //セル内Y座標<<16|セル行
   622:     if (col < 0 || row < 0) {  //セル範囲外
   623:       spvOffsetToCopy = 0;
   624:       spvLengthToCopy = 0;
   625:       return false;
   626:     }
   627:     x = col >> 16;  //セル内X座標
   628:     y = row >> 16;  //セル内Y座標
   629:     col = (char) col;  //セル桁
   630:     row = (char) row;  //セル行
   631:     int n = (spvCellOffset + col + (row << spvColsBit)) << 2;  //8x8パターン番号
   632:     int size = spvSizeArray[n >> 2];  //サイズ
   633:     if (size < 0) {
   634:       size = spvLastSizeArray[n >> 2];
   635:     }
   636:     if (size == 0) {  //8x8
   637:       int qCol = x / (SPV_CELL_WIDTH / 2);  //1/4セル桁
   638:       int qRow = y / (SPV_CELL_HEIGHT / 2);  //1/4セル行
   639:       n += qRow + (qCol << 1);  //8x8パターン番号。進行方向に注意
   640:       spvLengthToCopy = 1 << 3;
   641:     } else {  //16x16
   642:       spvLengthToCopy = 4 << 3;
   643:     }
   644:     spvOffsetToCopy = n << 3;
   645:     return true;
   646:   }  //spvGetPatternToCopy
   647: 
   648:   //spvCopyPattern ()
   649:   //  設定された範囲のパターンを16進数でコピーする
   650:   static void spvCopyPattern () {
   651:     if (spvLengthToCopy != 0) {
   652:       StringBuilder sb = new StringBuilder ();
   653:       for (int i = 0; i < spvLengthToCopy; i++) {
   654:         sb.append (String.format ("%08X\n", spvPat[spvOffsetToCopy + i]));
   655:       }
   656:       XEiJ.clpCopy (sb.toString ());
   657:     }
   658:   }  //spvCopyPattern
   659: 
   660:   //s = spvGetPixel (x, y)
   661:   //  座標からピクセルの情報を求める
   662:   static String spvGetPixel (int x, int y) {
   663:     int col = spvXToXCol (x);  //セル内X座標<<16|セル桁
   664:     int row = spvYToYRow (y);  //セル内Y座標<<16|セル行
   665:     if (col < 0 || row < 0) {  //セル範囲外
   666:       return null;
   667:     }
   668:     x = col >> 16;  //セル内X座標
   669:     y = row >> 16;  //セル内Y座標
   670:     col = (char) col;  //セル桁
   671:     row = (char) row;  //セル行
   672:     int n = (spvCellOffset + col + (row << spvColsBit)) << 2;  //8x8パターン番号
   673:     int size = spvSizeArray[n >> 2];  //サイズ
   674:     if (size < 0) {
   675:       size = spvLastSizeArray[n >> 2];
   676:     }
   677:     if (size == 0) {  //8x8
   678:       int qCol = x / (SPV_CELL_WIDTH / 2);  //1/4セル桁
   679:       int qRow = y / (SPV_CELL_HEIGHT / 2);  //1/4セル行
   680:       n += qRow + (qCol << 1);  //8x8パターン番号。進行方向に注意
   681:       x -= (SPV_CELL_WIDTH / 2) * qCol;  //1/4セル内X座標
   682:       y -= (SPV_CELL_HEIGHT / 2) * qRow;  //1/4セル内Y座標
   683:       x -= 1;
   684:       y -= 1;
   685:       if (x < 0 || 2 * 8 <= x ||
   686:           y < 0 || 2 * 8 <= y) {  //パータン範囲外
   687:         return null;
   688:       }
   689:       x >>= 1;  //8x8パターン内X座標
   690:       y >>= 1;  //8x8パターン内Y座標
   691:       int pb = spvBlockArray[n];  //パレットブロック
   692:       if (pb < 0) {
   693:         pb = spvLastBlockArray[n];
   694:       }
   695:       int vh = spvFlipArray[n];  //反転
   696:       if (vh < 0) {
   697:         vh = spvLastFlipArray[n];
   698:       }
   699:       int v = vh >> 1;
   700:       int h = vh & 1;
   701:       if (h != 0) {  //水平反転
   702:         x ^= 7;
   703:       }
   704:       if (v != 0) {  //垂直反転
   705:         y ^= 7;
   706:       }
   707:       int pc = (spvPat[(n << 3) + y] >> ((7 - x) << 2)) & 15;  //パレットコード
   708:       int p = pb << 4 | pc;
   709:       int c = spvPal16TS[p];
   710:       return String.format ("8x8 n=%d x=%d y=%d p=0x%02X c=0x%04X(%d,%d,%d,%d)",
   711:                             n, x, y, p, c,
   712:                             c >> 11, (c >> 6) & 31, (c >> 1) & 31, c & 1);
   713:     } else {  //16x16
   714:       x -= 2;
   715:       y -= 2;
   716:       if (x < 0 || 2 * 16 <= x ||
   717:           y < 0 || 2 * 16 <= y) {  //パターン範囲外
   718:         return null;
   719:       }
   720:       x >>= 1;  //16x16パターン内X座標
   721:       y >>= 1;  //16x16パターン内Y座標
   722:       int pb = spvBlockArray[n];  //パレットブロック
   723:       if (pb < 0) {
   724:         pb = spvLastBlockArray[n];
   725:       }
   726:       int vh = spvFlipArray[n];  //反転
   727:       if (vh < 0) {
   728:         vh = spvLastFlipArray[n];
   729:       }
   730:       int v = vh >> 1;
   731:       int h = vh & 1;
   732:       if ((vh & 1) != 0) {  //水平反転
   733:         x ^= 15;
   734:       }
   735:       if ((vh & 2) != 0) {  //垂直反転
   736:         y ^= 15;
   737:       }
   738:       int pc = (spvPat[(n << 3) + y + ((x & 8) << 1)] >> ((7 - (x & 7)) << 2)) & 15;  //パレットコード
   739:       int p = pb << 4 | pc;
   740:       int c = spvPal16TS[p];
   741:       return String.format ("16x16 n=%d x=%d y=%d p=0x%02X c=0x%04X(%d,%d,%d,%d)",
   742:                             n >> 2, x, y, p, c,
   743:                             c >> 11, (c >> 6) & 31, (c >> 1) & 31, c & 1);
   744:     }  //if 8x8 else 16x16
   745:   }  //spvGetPixel
   746: 
   747:   //spvUpdateFrame ()
   748:   //  更新する
   749:   static void spvUpdateFrame () {
   750:     if (spvFrame == null) {  //未初期化
   751:       return;
   752:     }
   753:     //停止/動作
   754:     if (spvStopped != spvStoppedRequest) {
   755:       if (spvStoppedRequest) {  //動作→停止
   756:         spvSetStoppedOn ();
   757:       } else {  //停止→動作
   758:         spvSetStoppedOff ();
   759:       }
   760:     }
   761:     //パレットテーブルを作る
   762:     for (int i = 0; i < 256; i++) {
   763:       spvPal32TS[i] = spvPalTbl[spvPal16TS[i]];
   764:     }
   765:     //情報を集める
   766:     Arrays.fill (spvSizeArray, spvSizeNumber);
   767:     Arrays.fill (spvBlockArray, spvBlockNumber);
   768:     Arrays.fill (spvFlipArray, spvFlipNumber);
   769:     //自動の要素があるときだけ集める
   770:     if (spvSizeNumber < 0 ||
   771:         spvBlockNumber < 0 ||
   772:         spvFlipNumber < 0) {  //自動の要素がある
   773:       //スプライトから集める
   774:       for (int sn = 0; sn < SpriteScreen.sprNumberOfSprites; sn++) {  //スプライト番号
   775:         if ((256 <= sn && sn < 256 + 8) ||  //欠番
   776:             spvPrw[sn] == 0) {  //表示されていない
   777:           continue;
   778:         }
   779:         int n = spvNum[sn] << 2;  //8x8パターン番号
   780:         if (spvSizeArray[n >> 2] < 0) {
   781:           spvSizeArray[n >> 2] =
   782:             spvLastSizeArray[n >> 2] = 1;  //16x16
   783:         }
   784:         int pb = spvCol[sn] >> 4;  //パレットブロック
   785:         int vh = ((spvV[sn] ? 2 : 0) |
   786:                   (spvH[sn] ? 1 : 0));  //反転
   787:         for (int k = 0; k < 4; k++) {
   788:           if (spvBlockArray[n + k] < 0) {
   789:             spvBlockArray[n + k] =
   790:               spvLastBlockArray[n + k] = pb;
   791:           }
   792:           if (spvFlipArray[n + k] < 0) {
   793:             spvFlipArray[n + k] =
   794:               spvLastFlipArray[n + k] = vh;
   795:           }
   796:         }
   797:       }  //for sn
   798:       //バックグラウンドから集める
   799:       int tm = 0;  //表示されているテキストページのマスク
   800:       if ((SpriteScreen.sprReg4BgCtrlPort & 1) != 0) {  //BG0が表示されている
   801:         tm |= 1 << ((SpriteScreen.sprReg4BgCtrlPort >> 1) & 3);  //BG0に割り当てられているテキストページ
   802:       }
   803:       if (((SpriteScreen.sprReg8ResoPort & 3) == 0 ||  //256x256または
   804:            SpriteScreen.spr512bg1) &&  //512x512でBG1を表示かつ
   805:           (SpriteScreen.sprReg4BgCtrlPort & 8) != 0) {  //BG1が表示されている
   806:         tm |= 1 << ((SpriteScreen.sprReg4BgCtrlPort >> 4) & 3);  //BG1に割り当てられているテキストページ
   807:       }
   808:       if ((SpriteScreen.sprReg8ResoPort & 3) == 0) {  //256x256
   809:         for (int tp = 0; tp < 4; tp++) {  //テキストページ
   810:           if ((tm & (1 << tp)) == 0) {  //表示されていない
   811:             continue;
   812:           }
   813:           int o = 4096 * tp;  //テキスト配列インデックス開始位置
   814:           for (int i = 0; i < 4096; i++) {  //テキスト配列インデックス
   815:             int n = spvTNum[o + i] >> 3;  //8x8パターン番号
   816:             if (spvSizeArray[n >> 2] < 0) {
   817:               spvSizeArray[n >> 2] =
   818:                 spvLastSizeArray[n >> 2] = 0;  //8x8
   819:             }
   820:             int pb = spvTCol[o + i] >> 4;  //パレットブロック
   821:             int vh = ((spvTV[o + i] ? 2 : 0) |
   822:                       (spvTH[o + i] ? 1 : 0));  //反転
   823:             if (spvBlockArray[n] < 0) {
   824:               spvBlockArray[n] =
   825:                 spvLastBlockArray[n] = pb;
   826:             }
   827:             if (spvFlipArray[n] < 0) {
   828:               spvFlipArray[n] =
   829:                 spvLastFlipArray[n] = vh;
   830:             }
   831:           }  //for i
   832:         }  //for tp
   833:       } else {  //512x512
   834:         for (int tp = 0; tp < 4; tp++) {  //テキストページ
   835:           if ((tm & (1 << tp)) == 0) {  //表示されていない
   836:             continue;
   837:           }
   838:           int o = 4096 * tp;  //テキスト配列インデックス開始位置
   839:           for (int i = 0; i < 4096; i++) {  //テキスト配列インデックス
   840:             int n = spvTNum[o + i] >> 1;  //8x8パターン番号
   841:             if (spvSizeArray[n >> 2] < 0) {
   842:               spvSizeArray[n >> 2] =
   843:                 spvLastSizeArray[n >> 2] = 1;  //16x16
   844:             }
   845:             int pb = spvTCol[o + i] >> 4;  //パレットブロック
   846:             int vh = ((spvTV[o + i] ? 2 : 0) |
   847:                       (spvTH[o + i] ? 1 : 0));  //反転
   848:             for (int k = 0; k < 4; k++) {
   849:               if (spvBlockArray[n + k] < 0) {
   850:                 spvBlockArray[n + k] =
   851:                   spvLastBlockArray[n + k] = pb;
   852:               }
   853:               if (spvFlipArray[n + k] < 0) {
   854:                 spvFlipArray[n + k] =
   855:                   spvLastFlipArray[n + k] = vh;
   856:               }
   857:             }
   858:           }  //for i
   859:         }  //for tp
   860:       }  //if 256x256 else 512x512
   861:       //集められなかったものは前回と同じにする
   862:       for (int n = 0; n < 4096; n++) {  //16x16パターン番号
   863:         if (spvSizeArray[n] < 0) {
   864:           spvSizeArray[n] = spvLastSizeArray[n];
   865:         }
   866:       }
   867:       for (int n = 0; n < 4 * 4096; n++) {  //8x8パターン番号
   868:         if (spvBlockArray[n] < 0) {
   869:           spvBlockArray[n] = spvLastBlockArray[n];
   870:         }
   871:         if (spvFlipArray[n] < 0) {
   872:           spvFlipArray[n] = spvLastFlipArray[n];
   873:         }
   874:       }
   875:     }  //if 自動の要素がある
   876:     //パターンを表示する
   877:     int a = 32 * spvCellOffset;  //パターン配列インデックス
   878:     for (int cRow = 0; cRow <= spvRowsMask; cRow++) {  //セル行
   879:       int cY = spvRowToY (cRow);  //セル左上(0,0)Y座標
   880:       for (int cCol = 0; cCol <= spvColsMask; cCol++) {  //セル桁
   881:         int cX = spvColToX (cCol);  //セル左上(0,0)X座標
   882:         int cIndex = cX + spvImageWidth * cY;  //セル左上ビットマップインデックス
   883:         //背景色で塗り潰す
   884:         //  サイズが変わるときゴミが残らないようにする
   885:         int i = cIndex;
   886:         for (int v = 0; v < SPV_CELL_HEIGHT; v++) {
   887:           for (int h = 0; h < SPV_CELL_WIDTH; h++) {
   888:             spvBitmap[i + h] = SPV_BACKGROUND_RGB;
   889:           }
   890:           i += spvImageWidth;  //(+0,+1)
   891:         }
   892:         int n = (spvCellOffset + cCol + (cRow << spvColsBit)) << 2;  //8x8パターン番号
   893:         if (spvSizeArray[n >> 2] == 0) {  //8x8
   894:           for (int qCol = 0; qCol < 2; qCol++) {  //1/4セル桁
   895:             int qX = cX + SPV_CELL_WIDTH / 2 * qCol;  //1/4セル左上X座標
   896:             for (int qRow = 0; qRow < 2; qRow++) {  //1/4セル行
   897:               int qY = cY + SPV_CELL_HEIGHT / 2 * qRow;  //1/4セル左上Y座標
   898:               int qIndex = qX + spvImageWidth * qY;  //1/4セル左上ビットマップインデックス
   899:               int pb = spvBlockArray[n];  //パレットブロック
   900:               int vh = spvFlipArray[n];  //反転
   901:               int hMask = (vh & 1) != 0 ? 7 : 0;  //左右反転マスク
   902:               int vMask = (vh & 2) != 0 ? 15 : 0;  //上下反転マスク
   903:               //灰色の線とパレットブロックを表す点
   904:               i = qIndex + (hMask == 0 ? 0 + spvImageWidth * 1 :
   905:                             1 + 2 * 8 + spvImageWidth * 1);  //左上ビットマップインデックス
   906:               for (int v = 0; v <= 15; v++) {  //描画方向。上0→下15
   907:                 int vm = v ^ vMask;  //参照方向。上0→下15または下15→上0
   908:                 if (vm == pb) {  //パレットブロックの点
   909:                   spvBitmap[i] = SPV_BLOCK_DOT_RGB;
   910:                 } else if (vm < 8) {  //灰色の線
   911:                   spvBitmap[i] = SPV_GRAY_LINE_RGB;
   912:                 }
   913:                 i += spvImageWidth * 1;  //(+0,+1)
   914:               }
   915:               //パターン
   916:               pb <<= 4;  //パレットブロック<<4
   917:               vMask >>= 1;
   918:               i = qIndex + 1 + spvImageWidth * 1;  //左上(1,1)ビットマップインデックス
   919:               for (int v = 0; v <= 7; v++) {  //描画方向。上0→下7
   920:                 int vm = v ^ vMask;  //参照方向。上0→下7または下7→上0
   921:                 int d = spvPat[a + vm];
   922:                 for (int h = 7; 0 <= h; h--) {  //描画方向。左7→右0
   923:                   int hm = h ^ hMask;  //参照方向。左7→右0または右0→左7
   924:                   spvBitmap[i] =
   925:                     spvBitmap[i + 1] =
   926:                       spvBitmap[i + spvImageWidth] =
   927:                         spvBitmap[i + spvImageWidth + 1] = spvPal32TS[pb + ((d >> (hm << 2)) & 15)];
   928:                   i += 2;  //(+2,+0)
   929:                 }  //for h
   930:                 i += spvImageWidth * 2 - 2 * 8;  //(-2*8,+2)
   931:               }  //for v
   932:               a += 8;
   933:               n++;
   934:             }  //for qRow
   935:           }  //for qCol
   936:         } else {  //16x16
   937:           int pb = spvBlockArray[n];  //パレットブロック
   938:           int vh = spvFlipArray[n];  //反転
   939:           int hMask = (vh & 1) != 0 ? 15 : 0;  //左右反転マスク
   940:           int vMask = (vh & 2) != 0 ? 15 : 0;  //上下反転マスク
   941:           //灰色の線とパレットブロックの点
   942:           i = cIndex + (hMask == 0 ? 0 + spvImageWidth * 2 :
   943:                         2 + 2 * 16 + spvImageWidth * 2);  //左上ビットマップインデックス
   944:           for (int v = 0; v <= 15; v++) {  //描画方向。上0→下15
   945:             int vm = v ^ vMask;  //参照方向。上0→下15または下15→上0
   946:             if (vm == pb) {  //パレットブロックの点
   947:               spvBitmap[i] =
   948:                 spvBitmap[i + 1] =
   949:                   spvBitmap[i + spvImageWidth] =
   950:                     spvBitmap[i + spvImageWidth + 1] = SPV_BLOCK_DOT_RGB;
   951:             } else if (vm < 8) {  //灰色の線
   952:               spvBitmap[i] =
   953:                 spvBitmap[i + 1] =
   954:                   spvBitmap[i + spvImageWidth] =
   955:                     spvBitmap[i + spvImageWidth + 1] = SPV_GRAY_LINE_RGB;
   956:             }
   957:             i += spvImageWidth * 2;  //(+0,+2)
   958:           }
   959:           //パターン
   960:           pb <<= 4;  //パレットブロック<<4
   961:           i = cIndex + 2 + spvImageWidth * 2;  //左上(2,2)ビットマップインデックス
   962:           for (int v = 0; v <= 15; v++) {  //描画方向。上0→下15
   963:             int vm = v ^ vMask;  //参照方向。上0→下15または下15→上0
   964:             long d = ((long) spvPat[a + vm] << 32 |
   965:                       (long) spvPat[a + vm + 16] & 0xffffffffL);
   966:             for (int h = 15; 0 <= h; h--) {  //描画方向。左15→右0
   967:               int hm = h ^ hMask;  //参照方向。左15→右0または右0→左15
   968:               spvBitmap[i] =
   969:                 spvBitmap[i + 1] =
   970:                   spvBitmap[i + spvImageWidth] =
   971:                     spvBitmap[i + spvImageWidth + 1] = spvPal32TS[pb + ((int) (d >> (hm << 2)) & 15)];
   972:               i += 2;  //(+2,+0)
   973:             }  //for h
   974:             i += spvImageWidth * 2 - 2 * 16;  //(-2*16,+2)
   975:           }  //for v
   976:           a += 32;
   977:         }  //if 8x8 else 16x16
   978:       }  //for cCol
   979:     }  //for cRow
   980:     spvCanvas.repaint ();
   981:   }  //spvUpdateFrame
   982: 
   983:   //spvSetStoppedOn ()
   984:   //  停止する
   985:   static void spvSetStoppedOn () {
   986:     spvStopped = true;
   987:     spvStoppedRequest = true;
   988:     //現在の値をコピーする
   989:     System.arraycopy (SpriteScreen.sprNumPort, 0, spvCopiedNum, 0, SpriteScreen.SPR_COUNT);
   990:     System.arraycopy (SpriteScreen.sprColPort, 0, spvCopiedCol, 0, SpriteScreen.SPR_COUNT);
   991:     System.arraycopy (SpriteScreen.sprPrw, 0, spvCopiedPrw, 0, SpriteScreen.SPR_COUNT);
   992:     System.arraycopy (SpriteScreen.sprH, 0, spvCopiedH, 0, SpriteScreen.SPR_COUNT);
   993:     System.arraycopy (SpriteScreen.sprV, 0, spvCopiedV, 0, SpriteScreen.SPR_COUNT);
   994:     System.arraycopy (SpriteScreen.sprPatPort, 0, spvCopiedPat, 0, 32 * 4096);
   995:     System.arraycopy (SpriteScreen.sprTNum, 0, spvCopiedTNum, 0, 4096 * 4);
   996:     System.arraycopy (SpriteScreen.sprTColPort, 0, spvCopiedTCol, 0, 4096 * 4);
   997:     System.arraycopy (SpriteScreen.sprTH, 0, spvCopiedTH, 0, 4096 * 4);
   998:     System.arraycopy (SpriteScreen.sprTV, 0, spvCopiedTV, 0, 4096 * 4);
   999:     System.arraycopy (VideoController.vcnPalBase[VideoController.VCN_CONTRAST_SCALE * 15], 0, spvCopiedPalTbl, 0, 65536);
  1000:     System.arraycopy (VideoController.vcnPal16TSPort, 0, spvCopiedPal16TS, 0, 256);
  1001:     //コピーされた値を参照する
  1002:     spvNum = spvCopiedNum;
  1003:     spvCol = spvCopiedCol;
  1004:     spvPrw = spvCopiedPrw;
  1005:     spvH = spvCopiedH;
  1006:     spvV = spvCopiedV;
  1007:     spvPat = spvCopiedPat;
  1008:     spvTNum = spvCopiedTNum;
  1009:     spvTCol = spvCopiedTCol;
  1010:     spvTH = spvCopiedTH;
  1011:     spvTV = spvCopiedTV;
  1012:     spvPalTbl = spvCopiedPalTbl;
  1013:     spvPal16TS = spvCopiedPal16TS;
  1014:   }  //spvSetStoppedOn
  1015: 
  1016:   //spvSetStoppedOff ()
  1017:   //  停止を解除する
  1018:   static void spvSetStoppedOff () {
  1019:     spvStopped = false;
  1020:     spvStoppedRequest = false;
  1021:     //現在の値を参照する
  1022:     spvNum = SpriteScreen.sprNumPort;
  1023:     spvCol = SpriteScreen.sprColPort;
  1024:     spvPrw = SpriteScreen.sprPrw;
  1025:     spvH = SpriteScreen.sprH;
  1026:     spvV = SpriteScreen.sprV;
  1027:     spvPat = SpriteScreen.sprPatPort;
  1028:     spvTNum = SpriteScreen.sprTNum;
  1029:     spvTCol = SpriteScreen.sprTColPort;
  1030:     spvTH = SpriteScreen.sprTH;
  1031:     spvTV = SpriteScreen.sprTV;
  1032:     spvPalTbl = VideoController.vcnPalBase[VideoController.VCN_CONTRAST_SCALE * 15];
  1033:     spvPal16TS = VideoController.vcnPal16TSPort;
  1034:   }  //spvSetStoppedOff
  1035: 
  1036: }  //class SpritePatternViewer