PaletteViewer.java
     1: //========================================================================================
     2: //  PaletteViewer.java
     3: //    en:Palette 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 javax.swing.*;
    21: 
    22: public class PaletteViewer {
    23: 
    24:   public static final boolean PLV_ON = true;
    25: 
    26:   //                group
    27:   //  +-------+-------+-------+-------+
    28:   //  |       |       |       |       |
    29:   //  | cell  | cell  | cell  | cell  |
    30:   //  |       |       |       |       |
    31:   //  +-------+-------+-------+-------+
    32:   //  |       |       |       |       |
    33:   //  | cell  | cell  | cell  | cell  |
    34:   //  |       |       |       |       |
    35:   //  +-------+-------+-------+-------+
    36:   //  |       |       |       |       |
    37:   //  | cell  | cell  | cell  | cell  |
    38:   //  |       |       |       |       |
    39:   //  +-------+-------+-------+-------+
    40:   //  |       |       |       |       |
    41:   //  | cell  | cell  | cell  | cell  |
    42:   //  |       |       |       |       |
    43:   //  +-------+-------+-------+-------+
    44:   //                      page
    45:   //  +-------------------------------------------+
    46:   //  |                  header                   |
    47:   //  |     +-------+ +-------+ +-------+ +-------+
    48:   //  |     |       | |       | |       | |       |
    49:   //  |     | group | | group | | group | | group |
    50:   //  |     |       | |       | |       | |       |
    51:   //  |     +-------+ +-------+ +-------+ +-------+
    52:   //  |     +-------+ +-------+ +-------+ +-------+
    53:   //  |     |       | |       | |       | |       |
    54:   //  |  h  | group | | group | | group | | group |
    55:   //  |  e  |       | |       | |       | |       |
    56:   //  |  a  +-------+-+-------+ +-------+ +-------+
    57:   //  |  d  +-------+ +-------+ +-------+ +-------+
    58:   //  |  e  |       | |       | |       | |       |
    59:   //  |  r  | group | | group | | group | | group |
    60:   //  |     |       | |       | |       | |       |
    61:   //  |     +-------+ +-------+ +-------+ +-------+
    62:   //  |     +-------+ +-------+ +-------+ +-------+
    63:   //  |     |       | |       | |       | |       |
    64:   //  |     | group | | group | | group | | group |
    65:   //  |     |       | |       | |       | |       |
    66:   //  +-----+-------+-+-------+ +-------+ +-------+
    67:   //                gap
    68:   //                          arrgt
    69:   //        0          1              2              3
    70:   //    +-------+  +-------+  +-------+-------+  +-------+
    71:   //    |       |  |       |  |       |       |  |       |
    72:   //    |   G   |  |  TS   |  |   G   |  TS   |  |   G   |
    73:   //    |       |  |       |  |       |       |  |       |
    74:   //    +-------+  +-------+  +-------+-------+  +-------+
    75:   //                                             |       |
    76:   //                                             |  TS   |
    77:   //                                             |       |
    78:   //                                             +-------+
    79: 
    80:   //セル
    81:   static final int PLV_CELL_WIDTH = 18;  //セルの幅。偶数
    82:   static final int PLV_CELL_HEIGHT = 18;  //セルの高さ
    83: 
    84:   //グループ
    85:   //  グループのサイズは4x4セルに固定
    86:   static final int PLV_GROUP_GAP = 2;  //グループの間隔
    87: 
    88:   //ヘッダ
    89:   static final String PLV_FONT_NAME = "Dialog";  //フォント名
    90:   static final int PLV_FONT_STYLE = Font.BOLD;  //フォントスタイル
    91:   static final int PLV_FONT_SIZE = 16;  //フォントサイズ
    92:   static final int PLV_HEADER_WIDTH = PLV_FONT_SIZE * 2;  //左ヘッダの幅
    93:   static final int PLV_HEADER_HEIGHT = PLV_FONT_SIZE * 2;  //上ヘッダの高さ
    94: 
    95:   //イメージ
    96:   static int plvImageWidth;  //イメージの幅
    97:   static int plvImageHeight;  //イメージの高さ
    98:   static BufferedImage plvBufferedImage;  //イメージ
    99:   static int[] plvBitmap;  //ビットマップ
   100:   static ScrollCanvas plvCanvas;  //キャンバス
   101: 
   102:   //色
   103:   static final int PLV_BACKGROUND_RGB = 0xff333333;  //背景色
   104:   static final int PLV_FOREGROUND_RGB = 0xffcccccc;  //文字色
   105: 
   106:   //ページ
   107:   static int plvCellOffset;  //開始セル番号
   108:   static int plvColsBit;  //セル桁数のビット
   109:   static int plvRowsBit;  //セル行数のビット
   110:   static int plvColsMask;  //セル桁数のマスク
   111:   static int plvRowsMask;  //セル行数のマスク
   112:   static int plvWidth;  //幅
   113:   static int plvHeight;  //高さ
   114: 
   115:   //配置
   116:   //  0 1  2 3
   117:   //  G TS H V
   118:   static final int PLV_DEF_ARRGT = 2;
   119:   static final int PLV_MIN_ARRGT = 0;
   120:   static final int PLV_MAX_ARRGT = 3;
   121:   static int plvArrgtNumber;  //配置番号
   122:   static JComboBox<String> plvArrgtComboBox;  //配置ドロップダウンリスト
   123:   static int plvArrgtLand;  //配置後の横方向のページ数。plvArrgtNumber==2?2:1
   124:   static int plvArrgtPort;  //配置後の縦方向のページ数。plvArrgtNumber==3?2:1
   125:   static int plvArrgtCols;  //配置後のセル桁数。plvArrgtLand<<plvColsBit
   126:   static int plvArrgtRows;  //配置後のセル行数。plvArrgtPort<<plvRowsBit
   127:   static int plvArrgtWidth;  //配置後の幅。plvWidth*plvArrgtLand
   128:   static int plvArrgtHeight;  //配置後の高さ。plvHeight*plvArrgtPort
   129: 
   130:   //倍率
   131:   //  -1  0 1 2 3 4
   132:   //  1/2 1 2 4 8 16
   133:   static final int PLV_DEF_SCALE = 0;
   134:   static final int PLV_MIN_SCALE = -1;
   135:   static final int PLV_MAX_SCALE = 4;
   136:   static int plvScaleNumber;  //倍率番号
   137:   static JComboBox<String> plvScaleComboBox;  //倍率ドロップダウンリスト
   138: 
   139:   //Hex
   140:   //  off    on
   141:   //  10進数 16進数
   142:   static final boolean PLV_DEF_HEX = false;
   143:   static boolean plvHex;
   144: 
   145:   //停止
   146:   static boolean plvStopped;  //true=停止中
   147:   static boolean plvStoppedRequest;
   148:   //  コピーされた値
   149:   static final int[] plvCopiedPalTbl = new int[65536];
   150:   static final int[] plvCopiedPal16G8 = new int[256];
   151:   static final int[] plvCopiedPal16TS = new int[256];
   152:   static final int[] plvCopiedPal8G16L = new int[256];
   153:   static final int[] plvCopiedPal8G16H = new int[256];
   154:   //  参照する値。停止中はコピーされた値、さもなくば現在の値
   155:   static int[] plvPalTbl;
   156:   static int[] plvPal16G8;
   157:   static int[] plvPal16TS;
   158:   static int[] plvPal8G16L;
   159:   static int[] plvPal8G16H;
   160:   //
   161:   static boolean plvG65536On;  //true=65536色表示あり
   162: 
   163:   //テキストフィールド
   164:   static JTextField plvTextField;
   165:   static boolean plvTextLocked;
   166: 
   167:   //ウインドウ
   168:   static JFrame plvFrame;
   169: 
   170:   //ポップアップメニュー
   171:   static JPopupMenu plvPopupMenu;
   172:   static int plvPageToCopy;
   173:   static int plvRowToCopy;
   174: 
   175:   //タイマー
   176:   public static final int PLV_INTERVAL = 10;
   177:   public static int plvTimer;
   178: 
   179:   //plvInit ()
   180:   //  初期化
   181:   public static void plvInit () {
   182:     //パラメータ
   183:     plvArrgtNumber = Settings.sgsGetInt ("plvarrgt", PLV_DEF_ARRGT, PLV_MIN_ARRGT, PLV_MAX_ARRGT);
   184:     plvHex = Settings.sgsGetOnOff ("plvhex", PLV_DEF_HEX);
   185:     plvScaleNumber = Settings.sgsGetInt ("plvscale", PLV_DEF_SCALE, PLV_MIN_SCALE, PLV_MAX_SCALE);
   186:     //ページ
   187:     plvCellOffset = 0;
   188:     plvColsBit =
   189:       plvRowsBit = 4;
   190:     plvColsMask = (1 << plvColsBit) - 1;
   191:     plvRowsMask = (1 << plvRowsBit) - 1;
   192:     plvWidth = (PLV_HEADER_WIDTH +
   193:                 (PLV_CELL_WIDTH << plvColsBit) +
   194:                 (PLV_GROUP_GAP << (plvColsBit - 2)) - PLV_GROUP_GAP);
   195:     plvHeight = (PLV_HEADER_HEIGHT +
   196:                  (PLV_CELL_HEIGHT << plvRowsBit) +
   197:                  (PLV_GROUP_GAP << (plvRowsBit - 2)) - PLV_GROUP_GAP);
   198:     //イメージ
   199:     plvImageWidth = plvWidth * 2;
   200:     plvImageHeight = plvHeight * 2;
   201:     //配置
   202:     plvSetArrgt (plvArrgtNumber);
   203:     //停止
   204:     plvSetStoppedOff ();
   205:     //ウインドウ
   206:     plvFrame = null;
   207:     //タイマー
   208:     plvTimer = 0;
   209:   }  //plvInit
   210: 
   211:   //plvTini ()
   212:   //  後始末
   213:   public static void plvTini () {
   214:     //パラメータ
   215:     Settings.sgsPutInt ("plvarrgt", plvArrgtNumber);
   216:     Settings.sgsPutOnOff ("plvhex", plvHex);
   217:     Settings.sgsPutInt ("plvscale", plvScaleNumber);
   218:   }  //plvTini
   219: 
   220:   //plvSetArrgt (number)
   221:   //  配置を設定する
   222:   static void plvSetArrgt (int number) {
   223:     plvArrgtNumber = number;
   224:     plvArrgtLand = plvArrgtNumber == 2 ? 2 : 1;
   225:     plvArrgtPort = plvArrgtNumber == 3 ? 2 : 1;
   226:     plvArrgtCols = plvArrgtLand << plvColsBit;
   227:     plvArrgtRows = plvArrgtPort << plvRowsBit;
   228:     plvArrgtWidth = plvWidth * plvArrgtLand;
   229:     plvArrgtHeight = plvHeight * plvArrgtPort;
   230:     if (plvCanvas != null) {
   231:       plvDrawHeader ();
   232:       plvCanvas.setImage (plvArrgtWidth, plvArrgtHeight);
   233:     }
   234:   }  //plvSetArrgt
   235: 
   236:   //x = plvColToX (col)
   237:   //  セル桁からX座標を求める
   238:   static int plvColToX (int col) {
   239:     int p = col >> plvColsBit;  //ページ
   240:     col &= plvColsMask;  //ページ内セル桁
   241:     int g = col >> 2;  //グループ
   242:     col &= 3;  //グループ内セル桁
   243:     return (plvWidth * p +
   244:             PLV_HEADER_WIDTH +
   245:             ((PLV_CELL_WIDTH << 2) + PLV_GROUP_GAP) * g +
   246:             PLV_CELL_WIDTH * col);
   247:   }  //plvColToX
   248: 
   249:   //y = plvRowToY (row)
   250:   //  セル行からY座標を求める
   251:   static int plvRowToY (int row) {
   252:     int p = row >> plvRowsBit;  //ページ
   253:     row &= plvRowsMask;  //ページ内セル行
   254:     int g = row >> 2;  //グループ
   255:     row &= 3;  //グループ内セル行
   256:     return (plvHeight * p +
   257:             PLV_HEADER_HEIGHT +
   258:             ((PLV_CELL_HEIGHT << 2) + PLV_GROUP_GAP) * g +
   259:             PLV_CELL_HEIGHT * row);
   260:   }  //plvRowToY
   261: 
   262:   //((x << 16) | col) = plvXToXCol (x)
   263:   //  X座標からセル内X座標とセル桁を求める。-1=範囲外
   264:   static int plvXToXCol (int x) {
   265:     if (x < 0 || plvArrgtWidth <= x) {  //表示範囲の外
   266:       return -1;
   267:     }
   268:     int p = x / plvWidth;  //ページ
   269:     x -= plvWidth * p;  //ページ内X座標
   270:     if (x < PLV_HEADER_WIDTH) {  //左ヘッダ
   271:       return -1;
   272:     }
   273:     x -= PLV_HEADER_WIDTH;
   274:     int g = x / ((PLV_CELL_WIDTH << 2) + PLV_GROUP_GAP);  //グループ
   275:     x -= ((PLV_CELL_WIDTH << 2) + PLV_GROUP_GAP) * g;  //グループ内X座標
   276:     if ((PLV_CELL_WIDTH << 2) <= x) {  //グループの間隔
   277:       return -1;
   278:     }
   279:     int col = x / PLV_CELL_WIDTH;  //グループ内セル桁
   280:     x -= PLV_CELL_WIDTH * col;  //セル内X座標
   281:     return (x << 16) | ((p << plvColsBit) + (g << 2) + col);
   282:   }  //plvXToXCol
   283: 
   284:   //((y << 16) | row) = plvYToYRow (y)
   285:   //  Y座標からセル内Y座標とセル行を求める。-1=範囲外
   286:   static int plvYToYRow (int y) {
   287:     if (y < 0 || plvArrgtHeight <= y) {  //表示範囲の外
   288:       return -1;
   289:     }
   290:     int p = y / plvHeight;  //ページ
   291:     y -= plvHeight * p;  //ページ内Y座標
   292:     if (y < PLV_HEADER_HEIGHT) {  //上ヘッダ
   293:       return -1;
   294:     }
   295:     y -= PLV_HEADER_HEIGHT;
   296:     int g = y / ((PLV_CELL_HEIGHT << 2) + PLV_GROUP_GAP);  //グループ
   297:     y -= ((PLV_CELL_HEIGHT << 2) + PLV_GROUP_GAP) * g;  //グループ内Y座標
   298:     if ((PLV_CELL_HEIGHT << 2) <= y) {  //グループの間隔
   299:       return -1;
   300:     }
   301:     int row = y / PLV_CELL_HEIGHT;  //グループ内セル行
   302:     y -= PLV_CELL_HEIGHT * row;  //セル内Y座標
   303:     return (y << 16) | ((p << plvRowsBit) + (g << 2) + row);
   304:   }  //plvYToYRow
   305: 
   306:   //plvStart ()
   307:   //  開始
   308:   public static void plvStart () {
   309:     if (RestorableFrame.rfmGetOpened (Settings.SGS_PLV_FRAME_KEY)) {
   310:       plvOpen ();
   311:     }
   312:   }  //plvStart
   313: 
   314:   //plvOpen ()
   315:   //  開く
   316:   public static void plvOpen () {
   317:     if (plvFrame == null) {
   318:       plvMakeFrame ();
   319:     } else {
   320:       plvUpdateFrame ();
   321:     }
   322:     XEiJ.dbgVisibleMask |= XEiJ.DBG_PLV_VISIBLE_MASK;
   323:     XEiJ.pnlExitFullScreen (false);
   324:     plvFrame.setVisible (true);
   325:   }  //plvOpen
   326: 
   327:   //plvMakeFrame ()
   328:   //  作る
   329:   //  ここでは開かない
   330:   static void plvMakeFrame () {
   331:     //イメージ
   332:     plvBufferedImage = new BufferedImage (plvImageWidth, plvImageHeight, BufferedImage.TYPE_INT_ARGB);
   333:     plvDrawHeader ();
   334:     //ビットマップ
   335:     plvBitmap = ((DataBufferInt) plvBufferedImage.getRaster ().getDataBuffer ()).getData ();
   336:     //キャンバス
   337:     plvCanvas = new ScrollCanvas (plvBufferedImage, plvArrgtWidth, plvArrgtHeight);
   338:     plvCanvas.setMatColor (new Color (PLV_BACKGROUND_RGB));
   339:     plvCanvas.setMinScaleShift (PLV_MIN_SCALE);
   340:     plvCanvas.setMaxScaleShift (PLV_MAX_SCALE);
   341:     plvCanvas.setScaleShift (plvScaleNumber);
   342:     //アクションリスナー
   343:     ActionListener listener = new ActionListener () {
   344:       @Override public void actionPerformed (ActionEvent ae) {
   345:         Object source = ae.getSource ();
   346:         String command = ae.getActionCommand ();
   347:         switch (command) {
   348:         case "Arrgt":
   349:           plvSetArrgt (plvArrgtComboBox.getSelectedIndex () + PLV_MIN_ARRGT);
   350:           if (XEiJ.mpuTask == null) {
   351:             plvUpdateFrame ();
   352:           }
   353:           break;
   354:         case "Scale":
   355:           plvScaleNumber = plvScaleComboBox.getSelectedIndex () + PLV_MIN_SCALE;
   356:           plvCanvas.setScaleShift (plvScaleNumber);
   357:           break;
   358:         case "Hex":
   359:           plvHex = (((JCheckBox) source).isSelected ());
   360:           plvDrawHeader ();
   361:           plvUpdateFrame ();
   362:           break;
   363:         case "Stop":
   364:           plvStoppedRequest = (((JCheckBox) source).isSelected ());
   365:           if (XEiJ.mpuTask == null) {
   366:             plvUpdateFrame ();
   367:           }
   368:           break;
   369:         case "Copy as hexadecimal":
   370:           if (0 <= plvPageToCopy) {
   371:             StringBuilder sb = new StringBuilder ();
   372:             for (int col = 0; col <= plvColsMask; col++) {
   373:               if (col != 0) {
   374:                 sb.append (' ');
   375:               }
   376:               int p = col + (plvRowToCopy << plvColsBit);
   377:               int c = (plvPageToCopy == 0 ? plvPal16G8 : plvPal16TS)[p];
   378:               sb.append (String.format ("%04X", c));
   379:             }
   380:             XEiJ.clpCopy (sb.toString ());
   381:             plvPageToCopy = -1;
   382:           }
   383:           break;
   384:         default:
   385:           System.out.println ("unknown action command " + command);
   386:         }
   387:       }
   388:     };
   389:     //テキストフィールド
   390:     plvTextField = new JTextField ();
   391:     plvTextField.setEditable (false);
   392:     plvTextField.setHorizontalAlignment (JTextField.CENTER);
   393:     //ウインドウ
   394:     plvFrame = Multilingual.mlnTitle (
   395:       ComponentFactory.createRestorableSubFrame (
   396:         Settings.SGS_PLV_FRAME_KEY,
   397:         "Palette Viewer",
   398:         null,
   399:         ComponentFactory.createBorderPanel (
   400:           0, 0,
   401:           //CENTER
   402:           plvCanvas,
   403:           //NORTH
   404:           ComponentFactory.createVerticalBox (
   405:             //1行目
   406:             ComponentFactory.createHorizontalBox (
   407:               Box.createHorizontalGlue (),
   408:               //
   409:               Multilingual.mlnText (
   410:                 ComponentFactory.createLabel ("Arrgt"),
   411:                 "ja", "配置"),
   412:               plvArrgtComboBox = ComponentFactory.createComboBox (
   413:                 plvArrgtNumber - PLV_MIN_ARRGT,
   414:                 "Arrgt",
   415:                 listener,
   416:                 "G", "TS", "H", "V"),
   417:               //
   418:               Multilingual.mlnText (
   419:                 ComponentFactory.createLabel ("Scale"),
   420:                 "ja", "倍率"),
   421:               plvScaleComboBox = ComponentFactory.createComboBox (
   422:                 plvScaleNumber - PLV_MIN_SCALE,
   423:                 "Scale",
   424:                 listener,
   425:                 "1/2", "1", "2", "4", "8", "16"),
   426:               //
   427:               Box.createHorizontalStrut (5),
   428:               ComponentFactory.createCheckBox (plvHex, "Hex", listener),
   429:               //
   430:               Box.createHorizontalGlue ()
   431:               ),  //HorizontalBox
   432:             //2行目
   433:             ComponentFactory.createHorizontalBox (
   434:               Box.createHorizontalGlue (),
   435:               //
   436:               plvTextField,
   437:               //
   438:               Box.createHorizontalStrut (5),
   439:               Multilingual.mlnText (
   440:                 ComponentFactory.createCheckBox (plvStoppedRequest, "Stop", listener),
   441:                 "ja", "停止"),
   442:               //
   443:               Box.createHorizontalGlue ()
   444:               )  //HorizontalBox
   445:             )  //VerticalBox
   446:           )  //BorderPanel
   447:         ),  //SubFrame
   448:       "ja", "パレットビュア");
   449:     //スケールシフトリスナー
   450:     plvCanvas.addScaleShiftListener (new ScrollCanvas.ScaleShiftListener () {
   451:       @Override public void scaleShiftChanged (int scaleShift) {
   452:         plvScaleNumber = scaleShift;
   453:         plvScaleComboBox.setSelectedIndex (plvScaleNumber - PLV_MIN_SCALE);
   454:       }
   455:     });
   456:     //ウインドウリスナー
   457:     ComponentFactory.addListener (
   458:       plvFrame,
   459:       new WindowAdapter () {
   460:         @Override public void windowClosing (WindowEvent we) {
   461:           XEiJ.dbgVisibleMask &= ~XEiJ.DBG_PLV_VISIBLE_MASK;
   462:         }
   463:       });
   464:     //ポップアップメニュー
   465:     plvPopupMenu = ComponentFactory.createPopupMenu (
   466:       Multilingual.mlnText (
   467:         ComponentFactory.createMenuItem ("Copy as hexadecimal", 'C', listener),
   468:         "ja", "16進数でコピー")
   469:       );
   470:     plvPageToCopy = -1;
   471:     //マウスリスナー
   472:     MouseAdapter ma = new MouseAdapter () {
   473:       @Override public void mouseClicked (MouseEvent me) {
   474:         plvTextLocked = !plvTextLocked;  //テキストフィールドのロックを反転する
   475:       }  //mouseClicked
   476:       @Override public void mouseMoved (MouseEvent me) {
   477:         if (!plvTextLocked) {  //テキストフィールドがロックされていないとき
   478:           MouseEvent2D me2D = (MouseEvent2D) me;
   479:           int x = (int) me2D.getX2D ();
   480:           int y = (int) me2D.getY2D ();
   481:           String s = plvGetPixel (x, y);
   482:           plvTextField.setText (s == null ? "" : s);  //テキストフィールドに表示する
   483:         }
   484:       }  //mouseMoved
   485:       @Override public void mousePressed (MouseEvent me) {
   486:         plvShowPopup (me);
   487:       }  //mousePressed
   488:       @Override public void mouseReleased (MouseEvent me) {
   489:         plvShowPopup (me);
   490:       }  //mouseReleased
   491:     };
   492:     plvCanvas.addMouseListener (ma);
   493:     plvCanvas.addMouseMotionListener (ma);
   494:   }  //plvMakeFrame
   495: 
   496:   //plvShowPopup (me)
   497:   //  ポップアップメニューを表示する
   498:   static void plvShowPopup (MouseEvent me) {
   499:     if (!me.isPopupTrigger ()) {
   500:       return;
   501:     }
   502:     MouseEvent2D me2D = (MouseEvent2D) me;
   503:     int x = (int) me2D.getX2D ();
   504:     int y = (int) me2D.getY2D ();
   505:     int col = plvXToXCol (x);  //セル内X座標<<16|セル桁
   506:     int row = plvYToYRow (y);  //セル内Y座標<<16|セル行
   507:     if (col < 0 || row < 0) {
   508:       return;
   509:     }
   510:     col = (char) col;  //セル桁
   511:     row = (char) row;  //セル行
   512:     if (col <= plvColsMask && row <= plvRowsMask && plvArrgtNumber != 1) {  //グラフィック
   513:       plvPageToCopy = 0;
   514:     } else {  //テキストとスプライト
   515:       plvPageToCopy = 1;
   516:     }
   517:     plvRowToCopy = row & plvRowsMask;
   518:     Point p = plvCanvas.getPopupPoint (me2D);
   519:     plvPopupMenu.show (plvCanvas, p.x, p.y);
   520:   }  //plvShowPopup
   521: 
   522:   //plvDrawHeader ()
   523:   //  ヘッダを描く
   524:   static void plvDrawHeader () {
   525:     Graphics2D g2 = plvBufferedImage.createGraphics ();
   526:     //背景
   527:     g2.setColor (new Color (PLV_BACKGROUND_RGB));
   528:     g2.fillRect (0, 0, plvArrgtWidth, plvArrgtHeight);
   529:     //ヘッダ
   530:     g2.setColor (new Color (PLV_FOREGROUND_RGB));
   531:     g2.setFont (new Font (PLV_FONT_NAME, PLV_FONT_STYLE, PLV_FONT_SIZE));
   532:     //  タイトル
   533:     if (plvArrgtNumber != 1) {
   534:       plvDrawString (g2,
   535:                      (PLV_HEADER_WIDTH + plvWidth) / 2,  //X座標
   536:                      plvRowToY (0) - PLV_FONT_SIZE * 13 / 12,  //Y座標
   537:                      "Graphic Palette",  //文字列
   538:                      SwingConstants.SOUTH);  //アンカー
   539:     }
   540:     if (plvArrgtNumber != 0) {
   541:       plvDrawString (g2,
   542:                      plvArrgtWidth - plvWidth + (PLV_HEADER_WIDTH + plvWidth) / 2,  //X座標
   543:                      plvArrgtHeight - plvHeight + plvRowToY (0) - PLV_FONT_SIZE * 13 / 12,  //Y座標
   544:                      "Text and Sprite Palette",  //文字列
   545:                      SwingConstants.SOUTH);  //アンカー
   546:     }
   547:     //  上
   548:     for (int row = 0; row < plvArrgtRows; row += (plvRowsMask + 1)) {
   549:       for (int col = 0; col < plvArrgtCols; col++) {
   550:         plvDrawString (g2,
   551:                        plvColToX (col) + PLV_CELL_WIDTH / 2,  //X座標
   552:                        plvRowToY (row) - PLV_FONT_SIZE / 12,  //Y座標
   553:                        String.format (plvHex ? "%X" : "%d", col & plvColsMask),  //文字列
   554:                        SwingConstants.SOUTH);  //アンカー
   555:       }  //for col
   556:     }  //for row
   557:     //  左
   558:     for (int col = 0; col < plvArrgtCols; col += (plvColsMask + 1)) {
   559:       for (int row = 0; row < plvArrgtRows; row++) {
   560:         plvDrawString (g2,
   561:                        plvColToX (col) - PLV_FONT_SIZE / 6,  //X座標
   562:                        plvRowToY (row) + PLV_CELL_HEIGHT / 2,  //Y座標
   563:                        String.format (plvHex ? "%X" : "%d",
   564:                                       plvCellOffset + ((row & plvRowsMask) << plvColsBit)),  //文字列
   565:                        SwingConstants.EAST);  //アンカー
   566:       }  //for row
   567:     }  //for col
   568:   }  //plvDrawHeader
   569: 
   570:   //plvDrawString (g, x, y, s, d)
   571:   //  文字列を描く
   572:   static void plvDrawString (Graphics2D g2, int x, int y, String s, int d) {
   573:     Font f = g2.getFont ();
   574:     FontRenderContext flc = g2.getFontRenderContext ();
   575:     TextLayout tl = new TextLayout (s, f, flc);
   576:     Rectangle2D r = tl.getBounds ();
   577:     int rx = (int) Math.round (r.getX ());
   578:     int ry = (int) Math.round (r.getY ());
   579:     int rw = (int) Math.round (r.getWidth ());
   580:     int rh = (int) Math.round (r.getHeight ());
   581:     switch (d) {
   582:     case SwingConstants.NORTH_WEST:
   583:       g2.drawString (s, x - rx, y - ry);
   584:       break;
   585:     case SwingConstants.NORTH:
   586:       g2.drawString (s, x - (rx + (rw >> 1)), y - ry);
   587:       break;
   588:     case SwingConstants.NORTH_EAST:
   589:       g2.drawString (s, x - (rx + rw), y - ry);
   590:       break;
   591:     case SwingConstants.WEST:
   592:       g2.drawString (s, x - rx, y - (ry + (rh >> 1)));
   593:       break;
   594:     case SwingConstants.CENTER:
   595:       g2.drawString (s, x - (rx + (rw >> 1)), y - (ry + (rh >> 1)));
   596:       break;
   597:     case SwingConstants.EAST:
   598:       g2.drawString (s, x - (rx + rw), y - (ry + (rh >> 1)));
   599:       break;
   600:     case SwingConstants.SOUTH_WEST:
   601:       g2.drawString (s, x - rx, y - (ry + rh));
   602:       break;
   603:     case SwingConstants.SOUTH:
   604:       g2.drawString (s, x - (rx + (rw >> 1)), y - (ry + rh));
   605:       break;
   606:     case SwingConstants.SOUTH_EAST:
   607:       g2.drawString (s, x - (rx + rw), y - (ry + rh));
   608:       break;
   609:     }
   610:   }  //plvDrawString
   611: 
   612:   //s = plvGetPixel (x, y)
   613:   //  座標からピクセルの情報を求める
   614:   static String plvGetPixel (int x, int y) {
   615:     int col = plvXToXCol (x);  //セル内X座標<<16|セル桁
   616:     int row = plvYToYRow (y);  //セル内Y座標<<16|セル行
   617:     if (col < 0 || row < 0) {  //セル範囲外
   618:       return null;
   619:     }
   620:     x = col >> 16;  //セル内X座標
   621:     y = row >> 16;  //セル内Y座標
   622:     col = (char) col;  //セル桁
   623:     row = (char) row;  //セル行
   624:     int p = (col & plvColsMask) + ((row & plvRowsMask) << plvColsBit);
   625:     if (col <= plvColsMask && row <= plvRowsMask && plvArrgtNumber != 1) {  //グラフィック
   626:       if (plvG65536On) {  //65536色表示あり
   627:         if (x == 0 || x == PLV_CELL_WIDTH - 1 ||
   628:             y == 0 || y == PLV_CELL_HEIGHT - 1 ||
   629:             x == PLV_CELL_WIDTH / 2 - 1 ||
   630:             x == PLV_CELL_WIDTH / 2) {  //パレット範囲外
   631:           return null;
   632:         }
   633:         int h = x < PLV_CELL_WIDTH / 2 ? 0 : 1;  //左半分と右半分
   634:         if ((p & 1) == 0) {  //下位
   635:           p += h;
   636:           int c = plvPal8G16L[p];
   637:           return String.format ("G p=0x??%02X c=0x??%02X(%d,%d,%d,%d)",
   638:                                 p, c,
   639:                                 c >> 11, (c >> 6) & 31, (c >> 1) & 31, c & 1);
   640:         } else {  //上位
   641:           p = (p & -2) + h;
   642:           int c = plvPal8G16H[p];
   643:           return String.format ("G p=0x%02X?? c=0x%02X??(%d,%d,%d,%d)",
   644:                                 p, c >> 8,
   645:                                 c >> 11, (c >> 6) & 31, (c >> 1) & 31, c & 1);
   646:         }
   647:       } else {  //16色、256色、65536色表示なし
   648:         if (x == 0 || x == PLV_CELL_WIDTH - 1 ||
   649:             y == 0 || y == PLV_CELL_HEIGHT - 1) {  //パレット範囲外
   650:           return null;
   651:         }
   652:         int c = plvPal16G8[p];
   653:         return String.format ("G p=0x%02X c=0x%04X(%d,%d,%d,%d)",
   654:                               p, c,
   655:                               c >> 11, (c >> 6) & 31, (c >> 1) & 31, c & 1);
   656:       }
   657:     } else {  //テキストとスプライト
   658:       if (x == 0 || x == PLV_CELL_WIDTH - 1 ||
   659:           y == 0 || y == PLV_CELL_HEIGHT - 1) {  //パレット範囲外
   660:         return null;
   661:       }
   662:       int c = plvPal16TS[p];
   663:       return String.format ("TS p=0x%02X c=0x%04X(%d,%d,%d,%d)",
   664:                             p, c,
   665:                             c >> 11, (c >> 6) & 31, (c >> 1) & 31, c & 1);
   666:     }
   667:   }  //plvGetPixel
   668: 
   669:   //plvUpdateFrame ()
   670:   //  更新する
   671:   static void plvUpdateFrame () {
   672:     if (plvFrame == null) {  //未初期化
   673:       return;
   674:     }
   675:     //停止/動作
   676:     if (plvStopped != plvStoppedRequest) {
   677:       if (plvStoppedRequest) {  //動作→停止
   678:         plvSetStoppedOn ();
   679:       } else {  //停止→動作
   680:         plvSetStoppedOff ();
   681:       }
   682:     }
   683:     if (!plvStopped) {
   684:       plvG65536On = ((VideoController.vcnReg1Port & 2) != 0 &&  //65536色
   685:                      (VideoController.vcnReg3Port & 15) != 0);  //表示あり
   686:     }
   687:     //パレットを表示する
   688:     for (int row = 0; row < plvArrgtRows; row++) {
   689:       int y = plvRowToY (row) + 1;
   690:       for (int col = 0; col < plvArrgtCols; col++) {
   691:         int x = plvColToX (col) + 1;
   692:         int i = x + plvImageWidth * y;
   693:         int p = (col & plvColsMask) + ((row & plvRowsMask) << plvColsBit);
   694:         if (col <= plvColsMask && row <= plvRowsMask && plvArrgtNumber != 1) {  //グラフィック
   695:           if (plvG65536On) {  //65536色表示あり
   696:             for (int v = 0; v < PLV_CELL_HEIGHT - 2; v++) {  //中央の壁
   697:               plvBitmap[i + PLV_CELL_WIDTH / 2 - 2] = PLV_BACKGROUND_RGB;
   698:               plvBitmap[i + PLV_CELL_WIDTH / 2 - 1] = PLV_BACKGROUND_RGB;
   699:               i += plvImageWidth;
   700:             }
   701:             i += -plvImageWidth * (PLV_CELL_HEIGHT - 2);
   702:             for (int h = 0; h < 2; h++) {  //左半分と右半分
   703:               int c = ((p & 1) == 0 ? plvPal8G16L : plvPal8G16H)[(p & -2) + h];
   704:               int d = plvPalTbl[c];
   705:               for (int v = 0; v < PLV_CELL_HEIGHT - 2; v++) {
   706:                 for (int u = 0; u < PLV_CELL_WIDTH / 2 - 2; u++) {
   707:                   plvBitmap[i + u] = d;
   708:                 }
   709:                 i += plvImageWidth;
   710:               }
   711:               i += PLV_CELL_WIDTH / 2 - plvImageWidth * (PLV_CELL_HEIGHT - 2);
   712:             }
   713:           } else {  //16色、256色、65536色表示なし
   714:             int c = plvPal16G8[p];
   715:             int d = plvPalTbl[c];
   716:             for (int v = 0; v < PLV_CELL_HEIGHT - 2; v++) {
   717:               for (int u = 0; u < PLV_CELL_WIDTH - 2; u++) {
   718:                 plvBitmap[i + u] = d;
   719:               }
   720:               i += plvImageWidth;
   721:             }
   722:           }
   723:         } else {  //テキストとスプライト
   724:           int c = plvPal16TS[p];
   725:           int d = plvPalTbl[c];
   726:           for (int v = 0; v < PLV_CELL_HEIGHT - 2; v++) {
   727:             for (int u = 0; u < PLV_CELL_WIDTH - 2; u++) {
   728:               plvBitmap[i + u] = d;
   729:             }
   730:             i += plvImageWidth;
   731:           }
   732:         }
   733:       }
   734:     }
   735:     plvCanvas.repaint ();
   736:   }  //plvUpdateFrame
   737: 
   738:   //plvSetStoppedOn ()
   739:   //  停止する
   740:   static void plvSetStoppedOn () {
   741:     plvStopped = true;
   742:     plvStoppedRequest = true;
   743:     //現在の値をコピーする
   744:     System.arraycopy (VideoController.vcnPalBase[VideoController.VCN_CONTRAST_SCALE * 15], 0, plvCopiedPalTbl, 0, 65536);
   745:     System.arraycopy (VideoController.vcnPal16G8, 0, plvCopiedPal16G8, 0, 256);
   746:     System.arraycopy (VideoController.vcnPal16TSPort, 0, plvCopiedPal16TS, 0, 256);
   747:     System.arraycopy (VideoController.vcnPal8G16L, 0, plvCopiedPal8G16L, 0, 256);
   748:     System.arraycopy (VideoController.vcnPal8G16H, 0, plvCopiedPal8G16H, 0, 256);
   749:     //コピーされた値を参照する
   750:     plvPalTbl = plvCopiedPalTbl;
   751:     plvPal16G8 = plvCopiedPal16G8;
   752:     plvPal16TS = plvCopiedPal16TS;
   753:     plvPal8G16L = plvCopiedPal8G16L;
   754:     plvPal8G16H = plvCopiedPal8G16H;
   755:   }  //plvSetStoppedOn
   756: 
   757:   //plvSetStoppedOff ()
   758:   //  停止を解除する
   759:   static void plvSetStoppedOff () {
   760:     plvStopped = false;
   761:     //現在の値を参照する
   762:     plvPalTbl = VideoController.vcnPalBase[VideoController.VCN_CONTRAST_SCALE * 15];
   763:     plvPal16G8 = VideoController.vcnPal16G8;
   764:     plvPal16TS = VideoController.vcnPal16TSPort;
   765:     plvPal8G16L = VideoController.vcnPal8G16L;
   766:     plvPal8G16H = VideoController.vcnPal8G16H;
   767:   }  //plvSetStoppedOff
   768: 
   769: }  //class PaletteViewer