OSDN Git Service

java9以降であればリフレクションによるスケールの確認は不要とする。
[charactermanaj/CharacterManaJ.git] / src / main / java / charactermanaj / ui / util / ScaleSupport.java
1 package charactermanaj.ui.util;
2
3 import java.awt.Component;
4 import java.awt.Dimension;
5 import java.awt.GraphicsConfiguration;
6 import java.awt.GraphicsDevice;
7 import java.awt.GraphicsEnvironment;
8 import java.awt.Toolkit;
9 import java.awt.geom.AffineTransform;
10 import java.lang.reflect.Field;
11 import java.lang.reflect.Method;
12
13 import charactermanaj.util.JavaVersionUtils;
14
15 /**
16  * スクリーンのスケールを取得する。
17  *
18  * Java8であれば自動スケールがかかっていないので、マニュアルでスケール倍して座標を補正する。
19  * Java11であれば自動スケールがかかっている。
20  */
21 public class ScaleSupport {
22
23         private static final int resolution;
24
25         private static final float computeScale;
26
27         private static final boolean noNeedCheckScaleByReflection;
28
29         private double scaleX;
30
31         private double scaleY;
32
33         private double manualScaleX;
34
35         private double manualScaleY;
36
37         private boolean retina;
38
39         static {
40                 // デフォルトのスクリーン解像度を取得する
41                 Toolkit tk = Toolkit.getDefaultToolkit();
42                 resolution = tk.getScreenResolution();
43
44                 // スクリーン解像度と標準のDPIから必要とされるスケールを計算する。
45                 // ただし、Windowsでない場合はスケール1の等倍にする。
46                 // (Retinaの場合はシステム側でスケールされるのでアプリ側でスケールする必要はないため)
47                 float dpi = System.getProperty("os.name").startsWith("Windows") ? 96f : resolution;
48                 computeScale = resolution / dpi;
49
50                 // Java9以降であればアフィン変換パラメータでスケールを確認できるので
51                 // リフレクションを使ったスケールの確認は不要である。
52                 noNeedCheckScaleByReflection = JavaVersionUtils.getJavaVersion() >= 9;
53         }
54
55         private ScaleSupport(double scaleX, double scaleY) {
56                 this.scaleX = scaleX;
57                 this.scaleY = scaleY;
58                 if (scaleX > 1 || scaleY > 1 || Boolean.getBoolean("disableScaleCalibrate")) {
59                         // システム側でスケールがかかっていれば、アプリ側ではスケールする必要はない。
60                         // もしくはシステムプロパティでアプリによるスケールを無効にしている場合。
61                         manualScaleX = 1;
62                         manualScaleY = 1;
63                 } else {
64                         // システム側でスケールがかかっていない場合はアプリ側でスケールする
65                         // スクリーン解像度とDPIから必要なスケールを求める
66                         manualScaleX = computeScale;
67                         manualScaleY = computeScale;
68                 }
69         }
70
71         public static int getScreenResolution() {
72                 return resolution;
73         }
74
75         public static float getScreenScale() {
76                 return computeScale;
77         }
78
79         private void setRetina(boolean retina) {
80                 this.retina = retina;
81         }
82
83         public boolean isRetina() {
84                 return retina;
85         }
86
87         /**
88          * コンポーネントのグラフィクス設定からスケールを取得する。
89          * まだ画面に関連付けられていない場合はnullを返す。
90          * @param comp
91          * @return
92          */
93         public static ScaleSupport getInstance(Component comp) {
94                 GraphicsConfiguration gconf = comp.getGraphicsConfiguration();
95                 if (gconf == null) {
96                         return null;
97                 }
98                 return getInstance(gconf);
99         }
100
101         public static ScaleSupport getDefault() {
102                 GraphicsEnvironment genv = GraphicsEnvironment.getLocalGraphicsEnvironment();
103                 GraphicsDevice gdev = genv.getDefaultScreenDevice();
104                 return getInstance(gdev.getDefaultConfiguration());
105         }
106
107         public static ScaleSupport getInstance(GraphicsConfiguration gconf) {
108                 // java9以降であれば、GraphicsConfigurationのデフォルトのアフィン変換に
109                 // スクリーンのスケールがかけられている。
110                 AffineTransform trans = gconf.getDefaultTransform();
111                 double scaleX = trans.getScaleX();
112                 double scaleY = trans.getScaleY();
113
114                 boolean retina = false;
115                 if (scaleX == 1 && scaleY == 1 && !noNeedCheckScaleByReflection) {
116                         // Java8まではデフォルトのアフィン変換はスクリーンスケールは設定されていないので
117                         // 等倍を返してきた場合は、グラフィクスデバイスがスケールメソッドをもっているかリフレクションで確かめる。
118                         // Mac版のJava8であればスケーメメソッドをもっている。
119                         // (Java9以降であれば確認は不要である。)
120                         // http://hg.openjdk.java.net/jdk9/client/jdk/file/1089d8a8a6e1/src/java.desktop/macosx/classes/sun/awt/CGraphicsDevice.java
121                         // https://www.programcreek.com/java-api-examples/?code=SensorsINI/jaer/jaer-master/src/net/sf/jaer/graphics/ChipCanvas.java
122                         final GraphicsDevice device = gconf.getDevice();
123                         Object scaleObj = null;
124                         try {
125                                 // public methodがあれば、そちらを試す。
126                                 Method methodGetScaleFactor = device.getClass().getMethod("getScaleFactor");
127                                 scaleObj = methodGetScaleFactor.invoke(device);
128
129                         } catch (Exception ex) {
130                                 try {
131                                         // sun.awt.CGraphicsDevice固有の内部フィールドを試す
132                                         Field field = device.getClass().getDeclaredField("scale");
133                                         field.setAccessible(true);
134                                         scaleObj = field.get(device);
135                                 } catch (Exception ex2) {
136                                         // 何もしない
137                                 }
138                         }
139                         if (scaleObj instanceof Number) {
140                                 int scale = ((Number) scaleObj).intValue();
141                                 scaleX = scaleY = scale;
142                                 if (scale >= 2) {
143                                         retina = true;
144                                 }
145                         }
146                 }
147                 ScaleSupport inst = new ScaleSupport(scaleX, scaleY);
148                 inst.setRetina(retina);
149                 return inst;
150         }
151
152         public double getDefaultScaleX() {
153                 return scaleX;
154         }
155
156         public double getDefaultScaleY() {
157                 return scaleY;
158         }
159
160         public double getManualScaleX() {
161                 return manualScaleX;
162         }
163
164         public double getManualScaleY() {
165                 return manualScaleY;
166         }
167
168         public Dimension manualScaled(Dimension dim) {
169                 return new Dimension((int) (dim.getWidth() * manualScaleX), (int) (dim.getHeight() * manualScaleY));
170         }
171 }