1. /*
  2. * @(#)SwingGraphics.java 1.32 03/01/23
  3. *
  4. * Copyright 2003 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.32 01/23/03
  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. return g.create(x, y, width, height);
  147. }
  148. public void translate(int x,int y) {
  149. graphics.translate(x,y);
  150. if (TRACE) {
  151. System.out.println("translate: 0x" +
  152. Integer.toHexString(hashCode()) +
  153. " x=" + x + ", y=" + y +
  154. ", clipRect=" + clipRect +
  155. " current translate " + translateX + " " +
  156. translateY);
  157. }
  158. translateX += x;
  159. translateY += y;
  160. clipRect.x -= x;
  161. clipRect.y -= y;
  162. }
  163. public Color getColor() {
  164. return currentColor;
  165. }
  166. public void setColor(Color c) {
  167. graphics.setColor(c);
  168. currentColor = c;
  169. }
  170. public void setPaintMode() {
  171. graphics.setPaintMode();
  172. currentXORMode = null;
  173. }
  174. public void setXORMode(Color c1) {
  175. graphics.setXORMode(c1);
  176. currentXORMode = c1;
  177. }
  178. public Font getFont() {
  179. return currentFont;
  180. }
  181. public void setFont(Font font) {
  182. graphics.setFont(font);
  183. currentFont = font;
  184. }
  185. public FontMetrics getFontMetrics() {
  186. return graphics.getFontMetrics();
  187. }
  188. public FontMetrics getFontMetrics(Font f) {
  189. return graphics.getFontMetrics(f);
  190. }
  191. public Rectangle getClipBounds() {
  192. /* Clone rectangle since we can't return a const and don't want
  193. * the caller to change this rectangle. */
  194. return new Rectangle(clipRect);
  195. }
  196. public boolean isClipIntersecting(Rectangle r) {
  197. if (clipRect.x >= r.x + r.width || clipRect.x + clipRect.width <= r.x ||
  198. clipRect.y >= r.y + r.height || clipRect.y + clipRect.height <= r.y) {
  199. return false;
  200. }
  201. return !(clipRect.width == 0 || clipRect.height == 0 || r.width == 0 ||
  202. r.height == 0);
  203. }
  204. public int getClipX() {
  205. return clipRect.x;
  206. }
  207. public int getClipY() {
  208. return clipRect.y;
  209. }
  210. public int getClipWidth() {
  211. return clipRect.width;
  212. }
  213. public int getClipHeight() {
  214. return clipRect.height;
  215. }
  216. public void clipRect(int x, int y, int width, int height) {
  217. graphics.clipRect(x,y,width,height);
  218. _changeClip(x, y, width, height, false);
  219. }
  220. public void setClip(int x, int y, int width, int height) {
  221. graphics.setClip(x,y,width,height);
  222. _changeClip(x, y, width, height, true);
  223. }
  224. public Shape getClip() {
  225. return graphics.getClip();
  226. }
  227. public void setClip(Shape clip) {
  228. graphics.setClip(clip);
  229. if(clip instanceof Rectangle) {
  230. Rectangle r = (Rectangle) clip;
  231. _changeClip(r.x,r.y,r.width,r.height,true);
  232. }
  233. }
  234. public void copyArea(int x, int y, int width, int height,
  235. int dx, int dy) {
  236. if (DEBUG && (width <= 0 || height <= 0)) {
  237. System.out.println("bad size: " + width + "x" + height);
  238. Thread.dumpStack();
  239. }
  240. graphics.copyArea(x,y,width,height,dx,dy);
  241. }
  242. public void drawLine(int x1, int y1, int x2, int y2) {
  243. graphics.drawLine(x1,y1,x2,y2);
  244. }
  245. public void fillRect(int x, int y, int width, int height) {
  246. if (DEBUG && (width <= 0 || height <= 0)) {
  247. System.out.println("bad size: " + width + "x" + height);
  248. Thread.dumpStack();
  249. }
  250. graphics.fillRect(x,y,width,height);
  251. }
  252. public void drawRect(int x, int y, int width, int height) {
  253. if (DEBUG && (width <= 0 || height <= 0)) {
  254. System.out.println("bad size: " + width + "x" + height);
  255. Thread.dumpStack();
  256. }
  257. graphics.drawRect(x, y, width, height);
  258. }
  259. public void clearRect(int x, int y, int width, int height) {
  260. if (DEBUG && (width <= 0 || height <= 0)) {
  261. System.out.println("bad size: " + width + "x" + height);
  262. Thread.dumpStack();
  263. }
  264. graphics.clearRect(x,y,width,height);
  265. }
  266. public void drawRoundRect(int x, int y, int width, int height,
  267. int arcWidth, int arcHeight) {
  268. if (DEBUG && (width <= 0 || height <= 0)) {
  269. System.out.println("bad size: " + width + "x" + height);
  270. Thread.dumpStack();
  271. }
  272. graphics.drawRoundRect(x,y,width,height,arcWidth,arcHeight);
  273. }
  274. public void fillRoundRect(int x, int y, int width, int height,
  275. int arcWidth, int arcHeight) {
  276. if (DEBUG && (width <= 0 || height <= 0)) {
  277. System.out.println("bad size: " + width + "x" + height);
  278. Thread.dumpStack();
  279. }
  280. graphics.fillRoundRect(x,y,width,height,arcWidth,arcHeight);
  281. }
  282. public void draw3DRect(int x, int y, int width, int height,
  283. boolean raised) {
  284. graphics.draw3DRect(x, y, width, height, raised);
  285. }
  286. public void fill3DRect(int x, int y, int width, int height,
  287. boolean raised) {
  288. graphics.fill3DRect(x, y, width, height, raised);
  289. }
  290. public void drawOval(int x, int y, int width, int height) {
  291. if (DEBUG && (width <= 0 || height <= 0)) {
  292. System.out.println("bad size: " + width + "x" + height);
  293. Thread.dumpStack();
  294. }
  295. graphics.drawOval(x,y,width,height);
  296. }
  297. public void fillOval(int x, int y, int width, int height) {
  298. if (DEBUG && (width <= 0 || height <= 0)) {
  299. System.out.println("bad size: " + width + "x" + height);
  300. Thread.dumpStack();
  301. }
  302. graphics.fillOval(x,y,width,height);
  303. }
  304. public void drawArc(int x, int y, int width, int height,
  305. int startAngle, int arcAngle) {
  306. if (DEBUG && (width <= 0 || height <= 0)) {
  307. System.out.println("bad size: " + width + "x" + height);
  308. Thread.dumpStack();
  309. }
  310. graphics.drawArc(x,y,width,height,startAngle,arcAngle);
  311. }
  312. public void fillArc(int x, int y, int width, int height,
  313. int startAngle, int arcAngle) {
  314. if (DEBUG && (width <= 0 || height <= 0)) {
  315. System.out.println("bad size: " + width + "x" + height);
  316. Thread.dumpStack();
  317. }
  318. graphics.fillArc(x,y,width,height,startAngle,arcAngle);
  319. }
  320. public void drawPolyline(int[] xPoints, int[] yPoints,
  321. int nPoints) {
  322. graphics.drawPolyline(xPoints,yPoints,nPoints);
  323. }
  324. public void drawPolygon(int[] xPoints, int[] yPoints,
  325. int nPoints) {
  326. graphics.drawPolygon(xPoints,yPoints,nPoints);
  327. }
  328. public void drawPolygon(Polygon p) {
  329. graphics.drawPolygon(p);
  330. }
  331. public void fillPolygon(int[] xPoints, int[] yPoints,
  332. int nPoints) {
  333. graphics.fillPolygon(xPoints,yPoints,nPoints);
  334. }
  335. public void fillPolygon(Polygon p) {
  336. graphics.fillPolygon(p);
  337. }
  338. public void drawString(String str, int x, int y) {
  339. graphics.drawString(str,x,y);
  340. }
  341. public void drawString(AttributedCharacterIterator iterator, int x, int y) {
  342. graphics.drawString(iterator,x,y);
  343. }
  344. public void drawChars(char[] data, int offset, int length, int x, int y) {
  345. graphics.drawChars(data, offset, length, x, y);
  346. }
  347. public void drawBytes(byte[] data, int offset, int length, int x, int y) {
  348. graphics.drawBytes(data, offset, length, x, y);
  349. }
  350. public boolean drawImage(Image img, int x, int y,
  351. ImageObserver observer) {
  352. return graphics.drawImage(img,x,y,observer);
  353. }
  354. public boolean drawImage(Image img, int x, int y,
  355. int width, int height,
  356. ImageObserver observer) {
  357. if (DEBUG && (width <= 0 || height <= 0)) {
  358. System.out.println("bad size: " + width + "x" + height);
  359. Thread.dumpStack();
  360. }
  361. return graphics.drawImage(img,x,y,width,height,observer);
  362. }
  363. public boolean drawImage(Image img, int x, int y,
  364. Color bgcolor,
  365. ImageObserver observer) {
  366. return graphics.drawImage(img,x,y,bgcolor,observer);
  367. }
  368. public boolean drawImage(Image img, int x, int y,
  369. int width, int height,
  370. Color bgcolor,
  371. ImageObserver observer) {
  372. if (DEBUG && (width <= 0 || height <= 0)) {
  373. System.out.println("bad size: " + width + "x" + height);
  374. Thread.dumpStack();
  375. }
  376. return graphics.drawImage(img,x,y,width,height,bgcolor,observer);
  377. }
  378. public boolean drawImage(Image img,
  379. int dx1, int dy1, int dx2, int dy2,
  380. int sx1, int sy1, int sx2, int sy2,
  381. ImageObserver observer) {
  382. return graphics.drawImage(img,dx1,dy1,dx2,dy2,sx1,sy1,sx2,sy2,observer);
  383. }
  384. public boolean drawImage(Image img,
  385. int dx1, int dy1, int dx2, int dy2,
  386. int sx1, int sy1, int sx2, int sy2,
  387. Color bgcolor,
  388. ImageObserver observer) {
  389. return graphics.drawImage(img,dx1,dy1,dx2,dy2,sx1,sy1,sx2,sy2,bgcolor,observer);
  390. }
  391. /* Restore the shared sub-graphics object to its original state. */
  392. private void resetGraphics() {
  393. if (TRACE) {
  394. System.out.println("resetGraphics: 0x" +
  395. Integer.toHexString(hashCode()));
  396. }
  397. if (currentFont != previous.currentFont) {
  398. setFont(previous.currentFont);
  399. }
  400. if (currentColor != previous.currentColor) {
  401. setColor(previous.currentColor);
  402. }
  403. if (currentXORMode != previous.currentXORMode) {
  404. if (previous.currentXORMode == null) {
  405. setPaintMode();
  406. } else {
  407. setXORMode(previous.currentXORMode);
  408. }
  409. }
  410. if (translateX != 0 || translateY != 0) {
  411. translate(-translateX, -translateY);
  412. }
  413. if (clipRect.x != previous.clipRect.x ||
  414. clipRect.y != previous.clipRect.y ||
  415. clipRect.width != previous.clipRect.width ||
  416. clipRect.height != previous.clipRect.height) {
  417. setClip(previous.clipRect.x, previous.clipRect.y,
  418. previous.clipRect.width, previous.clipRect.height);
  419. }
  420. }
  421. public void dispose() {
  422. if (TRACE) {
  423. System.out.println(
  424. "dispose: 0x" + Integer.toHexString(hashCode()) +
  425. "(" + (previous == null ? "null" :
  426. Integer.toHexString(previous.hashCode())) + ")" +
  427. " graphics? " + (graphics != null) + " translate " +
  428. translateX + " " + translateY);
  429. }
  430. if (graphics != null) {
  431. if (previous != null) {
  432. // In stack - do a graphics state "pop".
  433. resetGraphics();
  434. } else {
  435. // Bottom of stack, truly dispose of the wrapped object.
  436. graphics.dispose();
  437. translateX = translateY = 0;
  438. }
  439. }
  440. else {
  441. translateX = translateY = 0;
  442. }
  443. graphics = null;
  444. SwingGraphics.recycleSwingGraphics(this);
  445. }
  446. public void finalize() {
  447. graphics.finalize();
  448. }
  449. public String toString() {
  450. String fontString = currentFont.toString();
  451. fontString = fontString.substring(fontString.indexOf('['));
  452. return "SwingGraphics(0x" + Integer.toHexString(hashCode()) +
  453. ") [subGraphics " +
  454. originalGraphics.getClass().getName() +
  455. "\n translate [x=" + translateX + ",y=" + translateY +
  456. "] clip [x=" + clipRect.x + ",y=" + clipRect.y +
  457. ",w=" + clipRect.width + ",h=" + clipRect.height +
  458. "]\n color [r=" + currentColor.getRed() +
  459. ",g=" + currentColor.getGreen() +
  460. ",b=" + currentColor.getBlue() +
  461. "] font " + fontString + "]";
  462. }
  463. public Rectangle getClipRect() {
  464. return graphics.getClipRect();
  465. }
  466. private void _changeClip(int x,int y,int w,int h,boolean set) {
  467. if(set) {
  468. clipRect.x = x;
  469. clipRect.y = y;
  470. clipRect.width = w;
  471. clipRect.height = h;
  472. } else {
  473. SwingUtilities.computeIntersection(x,y,w,h,clipRect);
  474. }
  475. }
  476. private static Stack pool = new Stack();
  477. private static void recycleSwingGraphics(SwingGraphics g) {
  478. synchronized (pool) {
  479. if (DEBUG) {
  480. if (pool.indexOf(g) != -1) {
  481. System.out.println("Tried to recycle the same graphics twice!");
  482. Thread.dumpStack();
  483. }
  484. }
  485. pool.push(g);
  486. }
  487. }
  488. private static SwingGraphics getRecycledSwingGraphics() {
  489. SwingGraphics r = null;
  490. synchronized (pool) {
  491. if (pool.size() > 0) {
  492. r = (SwingGraphics) pool.pop();
  493. }
  494. }
  495. return r;
  496. }
  497. }