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