37 import java.awt.Point;
38 import java.awt.Rectangle;
39 import java.awt.Window;
40 import java.awt.event.ContainerEvent;
41 import java.awt.event.ContainerListener;
42 import java.awt.image.BufferedImage;
43 import java.awt.image.DataBufferInt;
44 import java.beans.PropertyChangeEvent;
45 import java.beans.PropertyChangeListener;
46 import java.security.AccessController;
47 import javax.swing.JComponent;
48
49 import javax.swing.JLayeredPane;
50 import javax.swing.JPanel;
51 import javax.swing.JRootPane;
52 import javax.swing.LayoutFocusTraversalPolicy;
53 import javax.swing.RepaintManager;
54 import javax.swing.RootPaneContainer;
55 import javax.swing.SwingUtilities;
56
57 import sun.awt.LightweightFrame;
58 import sun.security.action.GetPropertyAction;
59 import sun.swing.SwingUtilities2.RepaintListener;
60
61 /**
62 * The frame serves as a lightweight container which paints its content
63 * to an offscreen image and provides access to the image's data via the
64 * {@link LightweightContent} interface. Note, that it may not be shown
65 * as a standalone toplevel frame. Its purpose is to provide functionality
66 * for lightweight embedding.
67 *
68 * @author Artem Ananiev
69 * @author Anton Tarasov
70 */
71 @SuppressWarnings("serial") // JDK-implementation class
72 public final class JLightweightFrame extends LightweightFrame implements RootPaneContainer {
73
74 private final JRootPane rootPane = new JRootPane();
75
76 private LightweightContent content;
77
78 private Component component;
79 private JPanel contentPane;
80
81 private BufferedImage bbImage;
82
83 /**
84 * {@code copyBufferEnabled}, true by default, defines the following strategy.
85 * A duplicating (copy) buffer is created for the original pixel buffer.
86 * The copy buffer is synchronized with the original buffer every time the
87 * latter changes. {@code JLightweightFrame} passes the copy buffer array
88 * to the {@link LightweightContent#imageBufferReset} method. The code spot
89 * which synchronizes two buffers becomes the only critical section guarded
90 * by the lock (managed with the {@link LightweightContent#paintLock()},
91 * {@link LightweightContent#paintUnlock()} methods).
92 */
93 private boolean copyBufferEnabled;
94 private int[] copyBuffer;
95
96 private PropertyChangeListener layoutSizeListener;
97 private RepaintListener repaintListener;
98
99 static {
100 SwingAccessor.setJLightweightFrameAccessor(new SwingAccessor.JLightweightFrameAccessor() {
101 @Override
102 public void updateCursor(JLightweightFrame frame) {
103 frame.updateClientCursor();
104 }
105 });
106 }
107
108 /**
109 * Constructs a new, initially invisible {@code JLightweightFrame}
110 * instance.
111 */
112 public JLightweightFrame() {
113 super();
114 copyBufferEnabled = "true".equals(AccessController.
115 doPrivileged(new GetPropertyAction("swing.jlf.copyBufferEnabled", "true")));
116
117 add(rootPane, BorderLayout.CENTER);
118 setFocusTraversalPolicy(new LayoutFocusTraversalPolicy());
119 if (getGraphicsConfiguration().isTranslucencyCapable()) {
120 setBackground(new Color(0, 0, 0, 0));
121 }
122
123 layoutSizeListener = new PropertyChangeListener() {
124 @Override
125 public void propertyChange(PropertyChangeEvent e) {
127
128 if ("preferredSize".equals(e.getPropertyName())) {
129 content.preferredSizeChanged(d.width, d.height);
130
131 } else if ("maximumSize".equals(e.getPropertyName())) {
132 content.maximumSizeChanged(d.width, d.height);
133
134 } else if ("minimumSize".equals(e.getPropertyName())) {
135 content.minimumSizeChanged(d.width, d.height);
136 }
137 }
138 };
139
140 repaintListener = (JComponent c, int x, int y, int w, int h) -> {
141 Window jlf = SwingUtilities.getWindowAncestor(c);
142 if (jlf != JLightweightFrame.this) {
143 return;
144 }
145 Point p = SwingUtilities.convertPoint(c, x, y, jlf);
146 Rectangle r = new Rectangle(p.x, p.y, w, h).intersection(
147 new Rectangle(0, 0, bbImage.getWidth(), bbImage.getHeight()));
148
149 if (!r.isEmpty()) {
150 notifyImageUpdated(r.x, r.y, r.width, r.height);
151 }
152 };
153
154 SwingAccessor.getRepaintManagerAccessor().addRepaintListener(
155 RepaintManager.currentManager(this), repaintListener);
156 }
157
158 @Override
159 public void dispose() {
160 SwingAccessor.getRepaintManagerAccessor().removeRepaintListener(
161 RepaintManager.currentManager(this), repaintListener);
162 super.dispose();
163 }
164
165 /**
166 * Sets the {@link LightweightContent} instance for this frame.
167 * The {@code JComponent} object returned by the
181 Dimension d = this.component.getPreferredSize();
182 content.preferredSizeChanged(d.width, d.height);
183
184 d = this.component.getMaximumSize();
185 content.maximumSizeChanged(d.width, d.height);
186
187 d = this.component.getMinimumSize();
188 content.minimumSizeChanged(d.width, d.height);
189
190 initInterior();
191 }
192
193 @Override
194 public Graphics getGraphics() {
195 if (bbImage == null) return null;
196
197 Graphics2D g = bbImage.createGraphics();
198 g.setBackground(getBackground());
199 g.setColor(getForeground());
200 g.setFont(getFont());
201 return g;
202 }
203
204 /**
205 * {@inheritDoc}
206 *
207 * @see LightweightContent#focusGrabbed()
208 */
209 @Override
210 public void grabFocus() {
211 if (content != null) content.focusGrabbed();
212 }
213
214 /**
215 * {@inheritDoc}
216 *
217 * @see LightweightContent#focusUngrabbed()
218 */
219 @Override
220 public void ungrabFocus() {
221 if (content != null) content.focusUngrabbed();
222 }
223
224 private void syncCopyBuffer(boolean reset, int x, int y, int w, int h) {
225 content.paintLock();
226 try {
227 int[] srcBuffer = ((DataBufferInt)bbImage.getRaster().getDataBuffer()).getData();
228 if (reset) {
229 copyBuffer = new int[srcBuffer.length];
230 }
231 int linestride = bbImage.getWidth();
232
233 for (int i=0; i<h; i++) {
234 int from = (y + i) * linestride + x;
235 System.arraycopy(srcBuffer, from, copyBuffer, from, w);
236 }
237 } finally {
238 content.paintUnlock();
239 }
240 }
241
242 private void notifyImageUpdated(int x, int y, int width, int height) {
243 if (copyBufferEnabled) {
244 syncCopyBuffer(false, x, y, width, height);
245 }
246 content.imageUpdated(x, y, width, height);
247 }
248
249 @SuppressWarnings("serial") // anonymous class inside
250 private void initInterior() {
251 contentPane = new JPanel() {
252 @Override
253 public void paint(Graphics g) {
254 if (!copyBufferEnabled) {
255 content.paintLock();
256 }
257 try {
258 super.paint(g);
259
260 final Rectangle clip = g.getClipBounds() != null ?
261 g.getClipBounds() :
262 new Rectangle(0, 0, contentPane.getWidth(), contentPane.getHeight());
263
264 clip.x = Math.max(0, clip.x);
265 clip.y = Math.max(0, clip.y);
266 clip.width = Math.min(contentPane.getWidth(), clip.width);
267 clip.height = Math.min(contentPane.getHeight(), clip.height);
268
269 EventQueue.invokeLater(new Runnable() {
270 @Override
271 public void run() {
272 notifyImageUpdated(clip.x, clip.y, clip.width, clip.height);
273 }
274 });
275 } finally {
276 if (!copyBufferEnabled) {
277 content.paintUnlock();
278 }
279 }
280 }
281 @Override
282 protected boolean isPaintingOrigin() {
283 return true;
284 }
285 };
286 contentPane.setLayout(new BorderLayout());
287 contentPane.add(component);
288 if ("true".equals(AccessController.
289 doPrivileged(new GetPropertyAction("swing.jlf.contentPaneTransparent", "false"))))
290 {
291 contentPane.setOpaque(false);
292 }
306 public void componentRemoved(ContainerEvent e) {
307 Component c = JLightweightFrame.this.component;
308 if (e.getChild() == c) {
309 c.removePropertyChangeListener(layoutSizeListener);
310 }
311 }
312 });
313 }
314
315 @SuppressWarnings("deprecation")
316 @Override public void reshape(int x, int y, int width, int height) {
317 super.reshape(x, y, width, height);
318
319 if (width == 0 || height == 0) {
320 return;
321 }
322 if (!copyBufferEnabled) {
323 content.paintLock();
324 }
325 try {
326 if ((bbImage == null) || (width != bbImage.getWidth()) || (height != bbImage.getHeight())) {
327 boolean createBB = true;
328 int newW = width;
329 int newH = height;
330 if (bbImage != null) {
331 int oldW = bbImage.getWidth();
332 int oldH = bbImage.getHeight();
333 if ((oldW >= newW) && (oldH >= newH)) {
334 createBB = false;
335 } else {
336 if (oldW >= newW) {
337 newW = oldW;
338 } else {
339 newW = Math.max((int)(oldW * 1.2), width);
340 }
341 if (oldH >= newH) {
342 newH = oldH;
343 } else {
344 newH = Math.max((int)(oldH * 1.2), height);
345 }
346 }
347 }
348 if (createBB) {
349 BufferedImage oldBB = bbImage;
350 bbImage = new BufferedImage(newW, newH, BufferedImage.TYPE_INT_ARGB_PRE);
351 if (oldBB != null) {
352 Graphics g = bbImage.getGraphics();
353 try {
354 g.drawImage(oldBB, 0, 0, newW, newH, null);
355 } finally {
356 g.dispose();
357 oldBB.flush();
358 }
359 }
360 int[] pixels = ((DataBufferInt)bbImage.getRaster().getDataBuffer()).getData();
361 if (copyBufferEnabled) {
362 syncCopyBuffer(true, 0, 0, width, height);
363 pixels = copyBuffer;
364 }
365 content.imageBufferReset(pixels, 0, 0, width, height, bbImage.getWidth());
366 return;
367 }
368 }
369 content.imageReshaped(0, 0, width, height);
370
371 } finally {
372 if (!copyBufferEnabled) {
373 content.paintUnlock();
374 }
375 }
376 }
377
378 @Override
379 public JRootPane getRootPane() {
380 return rootPane;
381 }
382
383 @Override
384 public void setContentPane(Container contentPane) {
385 getRootPane().setContentPane(contentPane);
386 }
387
388 @Override
389 public Container getContentPane() {
390 return getRootPane().getContentPane();
391 }
392
393 @Override
394 public void setLayeredPane(JLayeredPane layeredPane) {
395 getRootPane().setLayeredPane(layeredPane);
396 }
397
|
37 import java.awt.Point;
38 import java.awt.Rectangle;
39 import java.awt.Window;
40 import java.awt.event.ContainerEvent;
41 import java.awt.event.ContainerListener;
42 import java.awt.image.BufferedImage;
43 import java.awt.image.DataBufferInt;
44 import java.beans.PropertyChangeEvent;
45 import java.beans.PropertyChangeListener;
46 import java.security.AccessController;
47 import javax.swing.JComponent;
48
49 import javax.swing.JLayeredPane;
50 import javax.swing.JPanel;
51 import javax.swing.JRootPane;
52 import javax.swing.LayoutFocusTraversalPolicy;
53 import javax.swing.RepaintManager;
54 import javax.swing.RootPaneContainer;
55 import javax.swing.SwingUtilities;
56
57 import sun.awt.DisplayChangedListener;
58 import sun.awt.LightweightFrame;
59 import sun.security.action.GetPropertyAction;
60 import sun.swing.SwingUtilities2.RepaintListener;
61
62 /**
63 * The frame serves as a lightweight container which paints its content
64 * to an offscreen image and provides access to the image's data via the
65 * {@link LightweightContent} interface. Note, that it may not be shown
66 * as a standalone toplevel frame. Its purpose is to provide functionality
67 * for lightweight embedding.
68 *
69 * @author Artem Ananiev
70 * @author Anton Tarasov
71 */
72 @SuppressWarnings("serial") // JDK-implementation class
73 public final class JLightweightFrame extends LightweightFrame implements RootPaneContainer {
74
75 private final JRootPane rootPane = new JRootPane();
76
77 private LightweightContent content;
78
79 private Component component;
80 private JPanel contentPane;
81
82 private BufferedImage bbImage;
83
84 private volatile int scaleFactor = 1;
85
86 /**
87 * {@code copyBufferEnabled}, true by default, defines the following strategy.
88 * A duplicating (copy) buffer is created for the original pixel buffer.
89 * The copy buffer is synchronized with the original buffer every time the
90 * latter changes. {@code JLightweightFrame} passes the copy buffer array
91 * to the {@link LightweightContent#imageBufferReset} method. The code spot
92 * which synchronizes two buffers becomes the only critical section guarded
93 * by the lock (managed with the {@link LightweightContent#paintLock()},
94 * {@link LightweightContent#paintUnlock()} methods).
95 */
96 private static boolean copyBufferEnabled;
97 private int[] copyBuffer;
98
99 private PropertyChangeListener layoutSizeListener;
100 private RepaintListener repaintListener;
101
102 static {
103 SwingAccessor.setJLightweightFrameAccessor(new SwingAccessor.JLightweightFrameAccessor() {
104 @Override
105 public void updateCursor(JLightweightFrame frame) {
106 frame.updateClientCursor();
107 }
108 });
109 copyBufferEnabled = "true".equals(AccessController.
110 doPrivileged(new GetPropertyAction("swing.jlf.copyBufferEnabled", "true")));
111 }
112
113 /**
114 * Constructs a new, initially invisible {@code JLightweightFrame}
115 * instance.
116 */
117 public JLightweightFrame() {
118 super();
119 copyBufferEnabled = "true".equals(AccessController.
120 doPrivileged(new GetPropertyAction("swing.jlf.copyBufferEnabled", "true")));
121
122 add(rootPane, BorderLayout.CENTER);
123 setFocusTraversalPolicy(new LayoutFocusTraversalPolicy());
124 if (getGraphicsConfiguration().isTranslucencyCapable()) {
125 setBackground(new Color(0, 0, 0, 0));
126 }
127
128 layoutSizeListener = new PropertyChangeListener() {
129 @Override
130 public void propertyChange(PropertyChangeEvent e) {
132
133 if ("preferredSize".equals(e.getPropertyName())) {
134 content.preferredSizeChanged(d.width, d.height);
135
136 } else if ("maximumSize".equals(e.getPropertyName())) {
137 content.maximumSizeChanged(d.width, d.height);
138
139 } else if ("minimumSize".equals(e.getPropertyName())) {
140 content.minimumSizeChanged(d.width, d.height);
141 }
142 }
143 };
144
145 repaintListener = (JComponent c, int x, int y, int w, int h) -> {
146 Window jlf = SwingUtilities.getWindowAncestor(c);
147 if (jlf != JLightweightFrame.this) {
148 return;
149 }
150 Point p = SwingUtilities.convertPoint(c, x, y, jlf);
151 Rectangle r = new Rectangle(p.x, p.y, w, h).intersection(
152 new Rectangle(0, 0, bbImage.getWidth() / scaleFactor,
153 bbImage.getHeight() / scaleFactor));
154
155 if (!r.isEmpty()) {
156 notifyImageUpdated(r.x, r.y, r.width, r.height);
157 }
158 };
159
160 SwingAccessor.getRepaintManagerAccessor().addRepaintListener(
161 RepaintManager.currentManager(this), repaintListener);
162 }
163
164 @Override
165 public void dispose() {
166 SwingAccessor.getRepaintManagerAccessor().removeRepaintListener(
167 RepaintManager.currentManager(this), repaintListener);
168 super.dispose();
169 }
170
171 /**
172 * Sets the {@link LightweightContent} instance for this frame.
173 * The {@code JComponent} object returned by the
187 Dimension d = this.component.getPreferredSize();
188 content.preferredSizeChanged(d.width, d.height);
189
190 d = this.component.getMaximumSize();
191 content.maximumSizeChanged(d.width, d.height);
192
193 d = this.component.getMinimumSize();
194 content.minimumSizeChanged(d.width, d.height);
195
196 initInterior();
197 }
198
199 @Override
200 public Graphics getGraphics() {
201 if (bbImage == null) return null;
202
203 Graphics2D g = bbImage.createGraphics();
204 g.setBackground(getBackground());
205 g.setColor(getForeground());
206 g.setFont(getFont());
207 g.scale(scaleFactor, scaleFactor);
208 return g;
209 }
210
211 /**
212 * {@inheritDoc}
213 *
214 * @see LightweightContent#focusGrabbed()
215 */
216 @Override
217 public void grabFocus() {
218 if (content != null) content.focusGrabbed();
219 }
220
221 /**
222 * {@inheritDoc}
223 *
224 * @see LightweightContent#focusUngrabbed()
225 */
226 @Override
227 public void ungrabFocus() {
228 if (content != null) content.focusUngrabbed();
229 }
230
231 @Override
232 public int getScaleFactor() {
233 return scaleFactor;
234 }
235
236 @Override
237 public void notifyDisplayChanged(final int scaleFactor) {
238 if (scaleFactor != this.scaleFactor) {
239 if (!copyBufferEnabled) content.paintLock();
240 try {
241 if (bbImage != null) {
242 resizeBuffer(getWidth(), getHeight(), scaleFactor);
243 }
244 } finally {
245 if (!copyBufferEnabled) content.paintUnlock();
246 }
247 this.scaleFactor = scaleFactor;
248 }
249 if (getPeer() instanceof DisplayChangedListener) {
250 ((DisplayChangedListener)getPeer()).displayChanged();
251 }
252 repaint();
253 }
254
255 @Override
256 public void addNotify() {
257 super.addNotify();
258 if (getPeer() instanceof DisplayChangedListener) {
259 ((DisplayChangedListener)getPeer()).displayChanged();
260 }
261 }
262
263 private void syncCopyBuffer(boolean reset, int x, int y, int w, int h, int scale) {
264 content.paintLock();
265 try {
266 int[] srcBuffer = ((DataBufferInt)bbImage.getRaster().getDataBuffer()).getData();
267 if (reset) {
268 copyBuffer = new int[srcBuffer.length];
269 }
270 int linestride = bbImage.getWidth();
271
272 x *= scale;
273 y *= scale;
274 w *= scale;
275 h *= scale;
276
277 for (int i=0; i<h; i++) {
278 int from = (y + i) * linestride + x;
279 System.arraycopy(srcBuffer, from, copyBuffer, from, w);
280 }
281 } finally {
282 content.paintUnlock();
283 }
284 }
285
286 private void notifyImageUpdated(int x, int y, int width, int height) {
287 if (copyBufferEnabled) {
288 syncCopyBuffer(false, x, y, width, height, scaleFactor);
289 }
290 content.imageUpdated(x, y, width, height);
291 }
292
293 @SuppressWarnings("serial") // anonymous class inside
294 private void initInterior() {
295 contentPane = new JPanel() {
296 @Override
297 public void paint(Graphics g) {
298 if (!copyBufferEnabled) {
299 content.paintLock();
300 }
301 try {
302 super.paint(g);
303
304 final Rectangle clip = g.getClipBounds() != null ?
305 g.getClipBounds() :
306 new Rectangle(0, 0, contentPane.getWidth(), contentPane.getHeight());
307
308 clip.x = Math.max(0, clip.x);
309 clip.y = Math.max(0, clip.y);
310 clip.width = Math.min(contentPane.getWidth(), clip.width);
311 clip.height = Math.min(contentPane.getHeight(), clip.height);
312
313 EventQueue.invokeLater(new Runnable() {
314 @Override
315 public void run() {
316 Rectangle c = contentPane.getBounds().intersection(clip);
317 notifyImageUpdated(c.x, c.y, c.width, c.height);
318 }
319 });
320 } finally {
321 if (!copyBufferEnabled) {
322 content.paintUnlock();
323 }
324 }
325 }
326 @Override
327 protected boolean isPaintingOrigin() {
328 return true;
329 }
330 };
331 contentPane.setLayout(new BorderLayout());
332 contentPane.add(component);
333 if ("true".equals(AccessController.
334 doPrivileged(new GetPropertyAction("swing.jlf.contentPaneTransparent", "false"))))
335 {
336 contentPane.setOpaque(false);
337 }
351 public void componentRemoved(ContainerEvent e) {
352 Component c = JLightweightFrame.this.component;
353 if (e.getChild() == c) {
354 c.removePropertyChangeListener(layoutSizeListener);
355 }
356 }
357 });
358 }
359
360 @SuppressWarnings("deprecation")
361 @Override public void reshape(int x, int y, int width, int height) {
362 super.reshape(x, y, width, height);
363
364 if (width == 0 || height == 0) {
365 return;
366 }
367 if (!copyBufferEnabled) {
368 content.paintLock();
369 }
370 try {
371 boolean createBB = (bbImage == null);
372 int newW = width;
373 int newH = height;
374 if (bbImage != null) {
375 int imgWidth = bbImage.getWidth() / scaleFactor;
376 int imgHeight = bbImage.getHeight() / scaleFactor;
377 if (width != imgWidth || height != imgHeight) {
378 createBB = true;
379 if (bbImage != null) {
380 int oldW = imgWidth;
381 int oldH = imgHeight;
382 if ((oldW >= newW) && (oldH >= newH)) {
383 createBB = false;
384 } else {
385 if (oldW >= newW) {
386 newW = oldW;
387 } else {
388 newW = Math.max((int)(oldW * 1.2), width);
389 }
390 if (oldH >= newH) {
391 newH = oldH;
392 } else {
393 newH = Math.max((int)(oldH * 1.2), height);
394 }
395 }
396 }
397 }
398 }
399 if (createBB) {
400 resizeBuffer(newW, newH, scaleFactor);
401 return;
402 }
403 content.imageReshaped(0, 0, width, height);
404
405 } finally {
406 if (!copyBufferEnabled) {
407 content.paintUnlock();
408 }
409 }
410 }
411
412 private void resizeBuffer(int width, int height, int newScaleFactor) {
413 bbImage = new BufferedImage(width*newScaleFactor,height*newScaleFactor,
414 BufferedImage.TYPE_INT_ARGB_PRE);
415 int[] pixels= ((DataBufferInt)bbImage.getRaster().getDataBuffer()).getData();
416 if (copyBufferEnabled) {
417 syncCopyBuffer(true, 0, 0, width, height, newScaleFactor);
418 pixels = copyBuffer;
419 }
420 content.imageBufferReset(pixels, 0, 0, width, height,
421 width * newScaleFactor, newScaleFactor);
422 }
423
424 @Override
425 public JRootPane getRootPane() {
426 return rootPane;
427 }
428
429 @Override
430 public void setContentPane(Container contentPane) {
431 getRootPane().setContentPane(contentPane);
432 }
433
434 @Override
435 public Container getContentPane() {
436 return getRootPane().getContentPane();
437 }
438
439 @Override
440 public void setLayeredPane(JLayeredPane layeredPane) {
441 getRootPane().setLayeredPane(layeredPane);
442 }
443
|