1. /*
  2. * @(#)SwingGraphics.java 1.29 01/11/29
  3. *
  4. * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.swing;
  8. /**
  9. * A private graphics to access clip bounds without creating a new
  10. * rectangle
  11. *
  12. * @version 1.29 11/29/01
  13. * @author Arnaud Weber
  14. */
  15. import java.awt.Color;
  16. import java.awt.Font;
  17. import java.awt.FontMetrics;
  18. import java.awt.Graphics;
  19. import java.awt.Image;
  20. import java.awt.Polygon;
  21. import java.awt.Rectangle;
  22. import java.awt.Shape;
  23. import java.awt.image.ImageObserver;
  24. import java.util.Stack;
  25. import java.text.AttributedCharacterIterator;
  26. class SwingGraphics extends Graphics implements GraphicsWrapper {
  27. Graphics graphics;
  28. Graphics originalGraphics;
  29. Rectangle clipRect;
  30. Color currentColor;
  31. Font currentFont;
  32. Color currentXORMode; // If null, graphics object is in normal paint mode.
  33. int translateX = 0; // translation delta since initialization
  34. int translateY = 0;
  35. /* The SwingGraphics this object was cloned from, if any. */
  36. SwingGraphics previous;
  37. /* diagnostic aids -- should be false for production builds. */
  38. private static final boolean TRACE = false; // trace creates and disposes
  39. private static final boolean VERBOSE = false; // show reuse hits/misses
  40. private static final boolean DEBUG = false; // show bad params, misc.
  41. public Graphics create() {
  42. return createSwingGraphics(this);
  43. }
  44. public Graphics create(int x,int y,int w,int h) {
  45. if (DEBUG && (w <= 0 || h <= 0)) {
  46. System.out.println("bad size: " + w + "x" + h);
  47. Thread.dumpStack();
  48. }
  49. return createSwingGraphics(this, x, y, w, h);
  50. }
  51. public Graphics subGraphics() {
  52. return graphics;
  53. }
  54. SwingGraphics(Graphics g) {
  55. if(g == null) {
  56. Thread.dumpStack();
  57. }
  58. init(g);
  59. }
  60. void init(Graphics g) {
  61. if (g instanceof SwingGraphics) {
  62. /* Clone an existing SwingGraphics object. The dispose method
  63. * must be called to reset the shared sub-graphics object to
  64. * the same state it was in when cloned. */
  65. SwingGraphics sg = (SwingGraphics)g;
  66. originalGraphics = sg.originalGraphics;
  67. graphics = sg.graphics;
  68. previous = sg;
  69. if (clipRect == null) {
  70. clipRect = new Rectangle(sg.clipRect.x,
  71. sg.clipRect.y,
  72. sg.clipRect.width,
  73. sg.clipRect.height);
  74. } else {
  75. clipRect.x = sg.clipRect.x;
  76. clipRect.y = sg.clipRect.y;
  77. clipRect.width = sg.clipRect.width;
  78. clipRect.height = sg.clipRect.height;
  79. }
  80. currentColor = sg.currentColor;
  81. currentFont = sg.currentFont;
  82. currentXORMode = sg.currentXORMode;
  83. if (VERBOSE) {
  84. System.out.print('.'); // '.' means "cache" hit
  85. System.out.flush();
  86. }
  87. } else {
  88. /* Initialize using a non-SwingGraphics Graphics object. The
  89. * original object is cloned to prevent damage, and its initial
  90. * state recorded. */
  91. originalGraphics = g;
  92. graphics = g.create();
  93. previous = null;
  94. Rectangle cr = g.getClipBounds();
  95. if (cr == null) {
  96. if (clipRect == null) {
  97. // Not a recycled SwingGraphics, allocate Rectangle.
  98. clipRect = new Rectangle(0, 0, Integer.MAX_VALUE,
  99. Integer.MAX_VALUE);
  100. } else {
  101. // Reuse recycled SwingGraphics' existing Rectangle.
  102. clipRect.x = clipRect.y = 0;
  103. clipRect.width = clipRect.height = Integer.MAX_VALUE;
  104. }
  105. } else {
  106. // Save returned Rectangle.
  107. clipRect = cr;
  108. }
  109. currentColor = g.getColor();
  110. currentFont = g.getFont();
  111. /* We're cheating a bit here -- there's no way to query what
  112. * the current screen mode or XOR color is with the Graphics
  113. * API, but Swing apps don't see the original graphics object,
  114. * only SwingGraphics wrappers. We're therefore assuming
  115. * that XOR mode isn't set when wrapping a graphics object,
  116. * on the assumption that the app wasn't twisted enough to
  117. * get a hold of a "raw" graphics object and set its
  118. * XOR mode before passing it on to Swing. If it is that
  119. * twisted, it deserved whatever screen it gets. :-)
  120. */
  121. currentXORMode = null;
  122. if (VERBOSE) {
  123. System.out.print('*'); // '.' means "cache" miss
  124. System.out.flush();
  125. }
  126. }
  127. }
  128. public static Graphics createSwingGraphics(Graphics g) {
  129. if (g == null) {
  130. Thread.dumpStack();
  131. return null;
  132. }
  133. return g.create();
  134. }
  135. /**
  136. * Create a SwingGraphics from another Graphics object, and set its clip
  137. * to be the intersection of the first Graphics object's clip rect.
  138. * Graphics.create() normally does this, but Microsoft's SDK for Java
  139. * 2.0 doesn't set the clip of the returned object. Since this method
  140. * is supposed to emulate what Graphics.create() does, all potential
  141. * bugs should be first checked with that method before changing the
  142. * behavior here.
  143. */
  144. static Graphics createSwingGraphics(Graphics g, int x, int y,
  145. int width, int height) {
  146. // Disable SwingGraphics wrapping on 1.2...
  147. return g.create(x, y, width, height);
  148. }
  149. public void translate(int x,int y) {
  150. graphics.translate(x,y);
  151. if (TRACE) {
  152. System.out.println("translate: 0x" +
  153. Integer.toHexString(hashCode()) +
  154. " x=" + x + ", y=" + y +
  155. ", clipRect=" + clipRect +
  156. " current translate " + translateX + " " +
  157. translateY);
  158. }
  159. translateX += x;
  160. translateY += y;
  161. clipRect.x -= x;
  162. clipRect.y -= y;
  163. }
  164. public Color getColor() {
  165. return currentColor;
  166. }
  167. public void setColor(Color c) {
  168. graphics.setColor(c);
  169. currentColor = c;
  170. }
  171. public void setPaintMode() {
  172. graphics.setPaintMode();
  173. currentXORMode = null;
  174. }
  175. public void setXORMode(Color c1) {
  176. graphics.setXORMode(c1);
  177. currentXORMode = c1;
  178. }
  179. public Font getFont() {
  180. return currentFont;
  181. }
  182. public void setFont(Font font) {
  183. graphics.setFont(font);
  184. currentFont = font;
  185. }
  186. public FontMetrics getFontMetrics() {
  187. return graphics.getFontMetrics();
  188. }
  189. public FontMetrics getFontMetrics(Font f) {
  190. return graphics.getFontMetrics(f);
  191. }
  192. public Rectangle getClipBounds() {
  193. /* Clone rectangle since we can't return a const and don't want
  194. * the caller to change this rectangle. */
  195. return new Rectangle(clipRect);
  196. }
  197. public boolean isClipIntersecting(Rectangle r) {
  198. if (clipRect.x >= r.x + r.width || clipRect.x + clipRect.width <= r.x ||
  199. clipRect.y >= r.y + r.height || clipRect.y + clipRect.height <= r.y) {
  200. return false;
  201. }
  202. return !(clipRect.width == 0 || clipRect.height == 0 || r.width == 0 ||
  203. r.height == 0);
  204. }
  205. public int getClipX() {
  206. return clipRect.x;
  207. }
  208. public int getClipY() {
  209. return clipRect.y;
  210. }
  211. public int getClipWidth() {
  212. return clipRect.width;
  213. }
  214. public int getClipHeight() {
  215. return clipRect.height;
  216. }
  217. public void clipRect(int x, int y, int width, int height) {
  218. graphics.clipRect(x,y,width,height);
  219. _changeClip(x, y, width, height, false);
  220. }
  221. public void setClip(int x, int y, int width, int height) {
  222. graphics.setClip(x,y,width,height);
  223. _changeClip(x, y, width, height, true);
  224. }
  225. public Shape getClip() {
  226. return graphics.getClip();
  227. }
  228. public void setClip(Shape clip) {
  229. graphics.setClip(clip);
  230. if(clip instanceof Rectangle) {
  231. Rectangle r = (Rectangle) clip;
  232. _changeClip(r.x,r.y,r.width,r.height,true);
  233. }
  234. }
  235. public void copyArea(int x, int y, int width, int height,
  236. int dx, int dy) {
  237. if (DEBUG && (width <= 0 || height <= 0)) {
  238. System.out.println("bad size: " + width + "x" + height);
  239. Thread.dumpStack();
  240. }
  241. graphics.copyArea(x,y,width,height,dx,dy);
  242. }
  243. public void drawLine(int x1, int y1, int x2, int y2) {
  244. graphics.drawLine(x1,y1,x2,y2);
  245. }
  246. public void fillRect(int x, int y, int width, int height) {
  247. if (DEBUG && (width <= 0 || height <= 0)) {
  248. System.out.println("bad size: " + width + "x" + height);
  249. Thread.dumpStack();
  250. }
  251. graphics.fillRect(x,y,width,height);
  252. }
  253. public void drawRect(int x, int y, int width, int height) {
  254. if (DEBUG && (width <= 0 || height <= 0)) {
  255. System.out.println("bad size: " + width + "x" + height);
  256. Thread.dumpStack();
  257. }
  258. graphics.drawRect(x, y, width, height);
  259. }
  260. public void clearRect(int x, int y, int width, int height) {
  261. if (DEBUG && (width <= 0 || height <= 0)) {
  262. System.out.println("bad size: " + width + "x" + height);
  263. Thread.dumpStack();
  264. }
  265. graphics.clearRect(x,y,width,height);
  266. }
  267. public void drawRoundRect(int x, int y, int width, int height,
  268. int arcWidth, int arcHeight) {
  269. if (DEBUG && (width <= 0 || height <= 0)) {
  270. System.out.println("bad size: " + width + "x" + height);
  271. Thread.dumpStack();
  272. }
  273. graphics.drawRoundRect(x,y,width,height,arcWidth,arcHeight);
  274. }
  275. public void fillRoundRect(int x, int y, int width, int height,
  276. int arcWidth, int arcHeight) {
  277. if (DEBUG && (width <= 0 || height <= 0)) {
  278. System.out.println("bad size: " + width + "x" + height);
  279. Thread.dumpStack();
  280. }
  281. graphics.fillRoundRect(x,y,width,height,arcWidth,arcHeight);
  282. }
  283. public void draw3DRect(int x, int y, int width, int height,
  284. boolean raised) {
  285. graphics.draw3DRect(x, y, width, height, raised);
  286. }
  287. public void fill3DRect(int x, int y, int width, int height,
  288. boolean raised) {
  289. graphics.fill3DRect(x, y, width, height, raised);
  290. }
  291. public void drawOval(int x, int y, int width, int height) {
  292. if (DEBUG && (width <= 0 || height <= 0)) {
  293. System.out.println("bad size: " + width + "x" + height);
  294. Thread.dumpStack();
  295. }
  296. graphics.drawOval(x,y,width,height);
  297. }
  298. public void fillOval(int x, int y, int width, int height) {
  299. if (DEBUG && (width <= 0 || height <= 0)) {
  300. System.out.println("bad size: " + width + "x" + height);
  301. Thread.dumpStack();
  302. }
  303. graphics.fillOval(x,y,width,height);
  304. }
  305. public void drawArc(int x, int y, int width, int height,
  306. int startAngle, int arcAngle) {
  307. if (DEBUG && (width <= 0 || height <= 0)) {
  308. System.out.println("bad size: " + width + "x" + height);
  309. Thread.dumpStack();
  310. }
  311. graphics.drawArc(x,y,width,height,startAngle,arcAngle);
  312. }
  313. public void fillArc(int x, int y, int width, int height,
  314. int startAngle, int arcAngle) {
  315. if (DEBUG && (width <= 0 || height <= 0)) {
  316. System.out.println("bad size: " + width + "x" + height);
  317. Thread.dumpStack();
  318. }
  319. graphics.fillArc(x,y,width,height,startAngle,arcAngle);
  320. }
  321. public void drawPolyline(int[] xPoints, int[] yPoints,
  322. int nPoints) {
  323. graphics.drawPolyline(xPoints,yPoints,nPoints);
  324. }
  325. public void drawPolygon(int[] xPoints, int[] yPoints,
  326. int nPoints) {
  327. graphics.drawPolygon(xPoints,yPoints,nPoints);
  328. }
  329. public void drawPolygon(Polygon p) {
  330. graphics.drawPolygon(p);
  331. }
  332. public void fillPolygon(int[] xPoints, int[] yPoints,
  333. int nPoints) {
  334. graphics.fillPolygon(xPoints,yPoints,nPoints);
  335. }
  336. public void fillPolygon(Polygon p) {
  337. graphics.fillPolygon(p);
  338. }
  339. public void drawString(String str, int x, int y) {
  340. graphics.drawString(str,x,y);
  341. }
  342. public void drawString(AttributedCharacterIterator iterator, int x, int y) {
  343. graphics.drawString(iterator,x,y);
  344. }
  345. public void drawChars(char[] data, int offset, int length, int x, int y) {
  346. graphics.drawChars(data, offset, length, x, y);
  347. }
  348. public void drawBytes(byte[] data, int offset, int length, int x, int y) {
  349. graphics.drawBytes(data, offset, length, x, y);
  350. }
  351. public boolean drawImage(Image img, int x, int y,
  352. ImageObserver observer) {
  353. return graphics.drawImage(img,x,y,observer);
  354. }
  355. public boolean drawImage(Image img, int x, int y,
  356. int width, int height,
  357. ImageObserver observer) {
  358. if (DEBUG && (width <= 0 || height <= 0)) {
  359. System.out.println("bad size: " + width + "x" + height);
  360. Thread.dumpStack();
  361. }
  362. return graphics.drawImage(img,x,y,width,height,observer);
  363. }
  364. public boolean drawImage(Image img, int x, int y,
  365. Color bgcolor,
  366. ImageObserver observer) {
  367. return graphics.drawImage(img,x,y,bgcolor,observer);
  368. }
  369. public boolean drawImage(Image img, int x, int y,
  370. int width, int height,
  371. Color bgcolor,
  372. ImageObserver observer) {
  373. if (DEBUG && (width <= 0 || height <= 0)) {
  374. System.out.println("bad size: " + width + "x" + height);
  375. Thread.dumpStack();
  376. }
  377. return graphics.drawImage(img,x,y,width,height,bgcolor,observer);
  378. }
  379. public boolean drawImage(Image img,
  380. int dx1, int dy1, int dx2, int dy2,
  381. int sx1, int sy1, int sx2, int sy2,
  382. ImageObserver observer) {
  383. return graphics.drawImage(img,dx1,dy1,dx2,dy2,sx1,sy1,sx2,sy2,observer);
  384. }
  385. public boolean drawImage(Image img,
  386. int dx1, int dy1, int dx2, int dy2,
  387. int sx1, int sy1, int sx2, int sy2,
  388. Color bgcolor,
  389. ImageObserver observer) {
  390. return graphics.drawImage(img,dx1,dy1,dx2,dy2,sx1,sy1,sx2,sy2,bgcolor,observer);
  391. }
  392. /* Restore the shared sub-graphics object to its original state. */
  393. private void resetGraphics() {
  394. if (TRACE) {
  395. System.out.println("resetGraphics: 0x" +
  396. Integer.toHexString(hashCode()));
  397. }
  398. if (currentFont != previous.currentFont) {
  399. setFont(previous.currentFont);
  400. }
  401. if (currentColor != previous.currentColor) {
  402. setColor(previous.currentColor);
  403. }
  404. if (currentXORMode != previous.currentXORMode) {
  405. if (previous.currentXORMode == null) {
  406. setPaintMode();
  407. } else {
  408. setXORMode(previous.currentXORMode);
  409. }
  410. }
  411. if (translateX != 0 || translateY != 0) {
  412. translate(-translateX, -translateY);
  413. }
  414. if (clipRect.x != previous.clipRect.x ||
  415. clipRect.y != previous.clipRect.y ||
  416. clipRect.width != previous.clipRect.width ||
  417. clipRect.height != previous.clipRect.height) {
  418. setClip(previous.clipRect.x, previous.clipRect.y,
  419. previous.clipRect.width, previous.clipRect.height);
  420. }
  421. }
  422. public void dispose() {
  423. if (TRACE) {
  424. System.out.println(
  425. "dispose: 0x" + Integer.toHexString(hashCode()) +
  426. "(" + (previous == null ? "null" :
  427. Integer.toHexString(previous.hashCode())) + ")" +
  428. " graphics? " + (graphics != null) + " translate " +
  429. translateX + " " + translateY);
  430. }
  431. if (graphics != null) {
  432. if (previous != null) {
  433. // In stack - do a graphics state "pop".
  434. resetGraphics();
  435. } else {
  436. // Bottom of stack, truly dispose of the wrapped object.
  437. graphics.dispose();
  438. translateX = translateY = 0;
  439. }
  440. }
  441. else {
  442. translateX = translateY = 0;
  443. }
  444. graphics = null;
  445. SwingGraphics.recycleSwingGraphics(this);
  446. }
  447. public void finalize() {
  448. graphics.finalize();
  449. }
  450. public String toString() {
  451. String fontString = currentFont.toString();
  452. fontString = fontString.substring(fontString.indexOf('['));
  453. return "SwingGraphics(0x" + Integer.toHexString(hashCode()) +
  454. ") [subGraphics " +
  455. originalGraphics.getClass().getName() +
  456. "\n translate [x=" + translateX + ",y=" + translateY +
  457. "] clip [x=" + clipRect.x + ",y=" + clipRect.y +
  458. ",w=" + clipRect.width + ",h=" + clipRect.height +
  459. "]\n color [r=" + currentColor.getRed() +
  460. ",g=" + currentColor.getGreen() +
  461. ",b=" + currentColor.getBlue() +
  462. "] font " + fontString + "]";
  463. }
  464. public Rectangle getClipRect() {
  465. return graphics.getClipRect();
  466. }
  467. private void _changeClip(int x,int y,int w,int h,boolean set) {
  468. if(set) {
  469. clipRect.x = x;
  470. clipRect.y = y;
  471. clipRect.width = w;
  472. clipRect.height = h;
  473. } else {
  474. SwingUtilities.computeIntersection(x,y,w,h,clipRect);
  475. }
  476. }
  477. private static Stack pool = new Stack();
  478. private static void recycleSwingGraphics(SwingGraphics g) {
  479. synchronized (pool) {
  480. if (DEBUG) {
  481. if (pool.indexOf(g) != -1) {
  482. System.out.println("Tried to recycle the same graphics twice!");
  483. Thread.dumpStack();
  484. }
  485. }
  486. pool.push(g);
  487. }
  488. }
  489. private static SwingGraphics getRecycledSwingGraphics() {
  490. SwingGraphics r = null;
  491. synchronized (pool) {
  492. if (pool.size() > 0) {
  493. r = (SwingGraphics) pool.pop();
  494. }
  495. }
  496. return r;
  497. }
  498. static {
  499. // Warn if running wrong version of this class for this JDK.
  500. if (!SwingUtilities.is1dot2) {
  501. System.err.println("warning: running 1.2 version of SwingGraphics");
  502. }
  503. }
  504. }