1. /*
  2. * @(#)PixmapEngine.java 1.13 04/01/13
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package com.sun.java.swing.plaf.gtk;
  8. import java.awt.*;
  9. import java.security.AccessController;
  10. import java.util.*;
  11. import javax.swing.*;
  12. import sun.security.action.GetPropertyAction;
  13. /**
  14. * GTKEngine implementation that renders using images. The images to render
  15. * are dictated by the <code>PixmapStyle.Info</code>.
  16. *
  17. * @version 1.13, 01/13/04
  18. * @author Scott Violet
  19. */
  20. class PixmapEngine extends GTKEngine implements GTKConstants {
  21. /**
  22. * By default we don't use smooth scaling as it is currently not optimized.
  23. */
  24. private static final Object RENDERING_HINT;
  25. private int _clipX1;
  26. private int _clipX2;
  27. private int _clipY1;
  28. private int _clipY2;
  29. static {
  30. if ("true".equals((String)AccessController.doPrivileged(
  31. new GetPropertyAction("swing.pixmap.smoothScaling")))) {
  32. RENDERING_HINT = RenderingHints.VALUE_INTERPOLATION_BILINEAR;
  33. }
  34. else {
  35. RENDERING_HINT = null;
  36. }
  37. }
  38. public void paintSlider(SynthContext context, Graphics g, int state,
  39. int shadowType, String info,
  40. int x, int y, int w, int h, int orientation) {
  41. if (!paintPixmap(g, x, y, w, h, ((PixmapStyle)context.getStyle()).
  42. getInfo("SLIDER", info,state, shadowType, orientation,
  43. UNDEFINED, UNDEFINED), true)) {
  44. super.paintSlider(context, g, state, shadowType, info,
  45. x, y, w, h, orientation);
  46. }
  47. }
  48. public void paintHline(SynthContext context, Graphics g, int state,
  49. String info, int x, int y, int w, int h) {
  50. if (!paintPixmap(g, x, y, w, h, ((PixmapStyle)context.getStyle()).
  51. getInfo("HLINE", info, state, UNDEFINED, UNDEFINED,
  52. UNDEFINED, UNDEFINED), true)) {
  53. super.paintHline(context, g, state, info, x, y, w, h);
  54. }
  55. }
  56. public void paintVline(SynthContext context, Graphics g, int state,
  57. String info, int x, int y, int w, int h) {
  58. if (!paintPixmap(g, x, y, w, h, ((PixmapStyle)context.getStyle()).
  59. getInfo("VLINE", info, state, UNDEFINED, UNDEFINED,
  60. UNDEFINED, UNDEFINED), true)) {
  61. super.paintVline(context, g, state, info, x, y, w, h);
  62. }
  63. }
  64. public void paintArrow(SynthContext context, Graphics g, int state,
  65. int shadowType, int direction, String info,
  66. int x, int y, int w, int h) {
  67. if (!paintPixmap(g, x, y, w, h, ((PixmapStyle)context.getStyle()).
  68. getInfo("ARROW", info, state, shadowType, UNDEFINED,
  69. UNDEFINED, direction), true)) {
  70. super.paintArrow(context, g, state, shadowType, direction, info,
  71. x, y, w, h);
  72. }
  73. }
  74. public void paintBox(SynthContext context, Graphics g, int state,
  75. int shadowType, String info, int x, int y,
  76. int w, int h) {
  77. int orientation;
  78. Region id = context.getRegion();
  79. if (id == Region.SCROLL_BAR) {
  80. if (((JScrollBar)context.getComponent()).getOrientation() ==
  81. SwingConstants.HORIZONTAL) {
  82. orientation = GTKConstants.HORIZONTAL;
  83. }
  84. else {
  85. orientation = GTKConstants.VERTICAL;
  86. }
  87. }
  88. else if (id == Region.SLIDER_TRACK) {
  89. if (((JSlider)context.getComponent()).getOrientation() ==
  90. SwingConstants.HORIZONTAL) {
  91. orientation = GTKConstants.HORIZONTAL;
  92. }
  93. else {
  94. orientation = GTKConstants.VERTICAL;
  95. }
  96. }
  97. else {
  98. orientation = UNDEFINED;
  99. }
  100. if (!paintPixmap(g, x, y, w, h, ((PixmapStyle)context.getStyle()).
  101. getInfo("BOX", info, state, shadowType, orientation,
  102. UNDEFINED, UNDEFINED), true)) {
  103. super.paintBox(context, g, state, shadowType, info, x, y, w, h);
  104. }
  105. }
  106. public void paintBoxGap(SynthContext context, Graphics g, int state,
  107. int shadow, String key, int x, int y,
  108. int w, int h, int gapSide, int gapStart,
  109. int gapSize) {
  110. PixmapStyle.Info info = ((PixmapStyle)context.getStyle()).getInfo(
  111. "BOX_GAP", key, state, shadow, UNDEFINED, gapSide, UNDEFINED);
  112. if (info != null) {
  113. // Yes, this appears to paint before the gap does.
  114. paintPixmap(g, x, y, w, h, info, true);
  115. // Determine the size of the opposite axis of the gap.
  116. int size = 0;
  117. Image startImage = info.getGapStartImage();
  118. Image image = info.getGapImage();
  119. Image endImage = info.getGapEndImage();
  120. if (gapSide == LEFT || gapSide == RIGHT) {
  121. if (startImage != null) {
  122. size = startImage.getWidth(null);
  123. }
  124. else if (image != null) {
  125. size = image.getWidth(null);
  126. }
  127. else if (endImage != null) {
  128. size = endImage.getWidth(null);
  129. }
  130. }
  131. else {
  132. if (startImage != null) {
  133. size = startImage.getHeight(null);
  134. }
  135. else if (image != null) {
  136. size = image.getHeight(null);
  137. }
  138. else if (endImage != null) {
  139. size = endImage.getHeight(null);
  140. }
  141. }
  142. if (size <= 0) {
  143. // No matching images.
  144. return;
  145. }
  146. paintGapImage(g, x, y, w, h, startImage, info.getGapStartInsets(),
  147. gapSide, size, 0, gapStart);
  148. paintGapImage(g, x, y, w, h, image, info.getGapInsets(), gapSide,
  149. size, gapStart, gapSize);
  150. paintGapImage(g, x, y, w, h, endImage, info.getGapEndInsets(),
  151. gapSide, size, gapStart + gapSize,
  152. Integer.MAX_VALUE);
  153. }
  154. else {
  155. super.paintBoxGap(context, g, state, shadow, key, x, y, w, h,
  156. gapSide, gapStart,gapSize);
  157. }
  158. }
  159. public void paintHandle(SynthContext context, Graphics g, int paintState,
  160. int shadowType, String info, int x, int y,
  161. int w, int h, int orientation) {
  162. if (!paintPixmap(g, x, y, w, h, ((PixmapStyle)context.getStyle()).
  163. getInfo("HANDLE", info, paintState, shadowType,
  164. orientation, UNDEFINED, UNDEFINED), true)) {
  165. super.paintHandle(context, g, paintState, shadowType, info, x, y,
  166. w, h, orientation);
  167. }
  168. }
  169. public void paintOption(SynthContext context, Graphics g, int paintState,
  170. int shadowType, String info, int x, int y,
  171. int w, int h) {
  172. if (!paintPixmap(g, x, y, w, h, ((PixmapStyle)context.getStyle()).
  173. getInfo("OPTION", info, paintState, shadowType,
  174. UNDEFINED, UNDEFINED, UNDEFINED), true)) {
  175. super.paintOption(context, g, paintState, shadowType, info, x, y,
  176. w, h);
  177. }
  178. }
  179. public void paintFocus(SynthContext context, Graphics g, int state,
  180. String key, int x, int y, int w, int h) {
  181. if (!paintPixmap(g, x, y, w, h, ((PixmapStyle)context.getStyle()).
  182. getInfo( "FOCUS", key, state, UNDEFINED, UNDEFINED,
  183. UNDEFINED, UNDEFINED), true)) {
  184. super.paintFocus(context, g, state, key, x, y, w, h);
  185. }
  186. }
  187. public void paintShadow(SynthContext context, Graphics g, int state,
  188. int shadowType, String info, int x, int y,
  189. int w, int h) {
  190. if (!paintPixmap(g, x, y, w, h, ((PixmapStyle)context.getStyle()).
  191. getInfo("SHADOW", info, state, shadowType, UNDEFINED,
  192. UNDEFINED, UNDEFINED), false)) {
  193. super.paintShadow(context, g, state, shadowType, info, x, y, w, h);
  194. }
  195. }
  196. public void paintExpander(SynthContext context, Graphics g, int state,
  197. int expanderStyle, String info, int x,
  198. int y, int w, int h) {
  199. // It does not appear that there is a way to override this.
  200. super.paintExpander(context, g, state, expanderStyle, info, x, y, w,h);
  201. }
  202. public void paintCheck(SynthContext context, Graphics g, int state,
  203. int shadowType, String info, int x, int y,
  204. int w, int h) {
  205. if (!paintPixmap(g, x, y, w, h, ((PixmapStyle)context.getStyle()).
  206. getInfo("CHECK", info, state, shadowType, UNDEFINED,
  207. UNDEFINED, UNDEFINED), true)) {
  208. super.paintCheck(context, g, state, shadowType, info, x, y, w, h);
  209. }
  210. }
  211. public void paintExtension(SynthContext context, Graphics g, int state,
  212. int shadowType, String info, int x, int y,
  213. int w, int h, int placement) {
  214. if (!paintPixmap(g, x, y, w, h, ((PixmapStyle)context.getStyle()).
  215. getInfo("EXTENSION", info, state, shadowType,
  216. UNDEFINED, placement, UNDEFINED), true)) {
  217. super.paintExtension(context, g, state, shadowType, info, x, y,
  218. w, h, placement);
  219. }
  220. }
  221. public void paintFlatBox(SynthContext context, Graphics g, int state,
  222. String key, int x, int y, int w, int h) {
  223. if (!paintPixmap(g, x, y, w, h, ((PixmapStyle)context.getStyle()).
  224. getInfo("FLAT_BOX", key, state, UNDEFINED, UNDEFINED,
  225. UNDEFINED, UNDEFINED), true)) {
  226. super.paintFlatBox(context, g, state, key, x, y, w, h);
  227. }
  228. }
  229. /**
  230. * Paints a gap image. This renders the image into a portion of
  231. * the passed in region that is dictated
  232. * by the <code>gapSide</code> and <code>size</code> arguments. For
  233. * example, if <code>gapSide</code> is <code>GTKConstants.TOP</code>,
  234. * this will render the image into the space:
  235. * <table>
  236. * <tr><td>x origin<td> <code>x</code> + <code>gapStart</code>
  237. * <tr><td>y origin<td> <code>y</code>
  238. * <tr><td>width<td> <code>gapSize</code>
  239. * <tr><td>height<td> <code>size</code>
  240. * </table>
  241. *
  242. * @param g Graphics object to paint to
  243. * @param x X origin
  244. * @param y Y origin
  245. * @param w Width to draw to
  246. * @param h Height to draw to
  247. * @param image Image to paint
  248. * @param insets Insets dicatating fixed portion and scaled portion of
  249. * the image.
  250. * @param gapSide Side the gap is on, one of GTKConstants.LEFT,
  251. * GTKConstants.RIGHT, GTKConstants.TOP or GTKConstants.BOTTOM
  252. * @param size Size of the gap, either width or height, dependant upon
  253. * gapSide
  254. * @param gapStart Starting location of the gap. The axis the gap is
  255. * on is dictated by the gapSide
  256. * @param gapSize size of the gap
  257. */
  258. private void paintGapImage(Graphics g, int x, int y, int w, int h,
  259. Image image, Insets insets, int gapSide,
  260. int size, int gapStart, int gapSize) {
  261. if (image != null && gapSize > 0) {
  262. switch(gapSide) {
  263. case LEFT:
  264. paintImage(g, x, y + gapStart, Math.min(w, size),
  265. Math.min(h - y - gapStart, gapSize), image,insets, true,
  266. false, true);
  267. break;
  268. case RIGHT:
  269. paintImage(g, x + w - Math.min(w, size),
  270. y + gapStart, Math.min(w, size),
  271. Math.min(h - y - gapStart, gapSize), image,
  272. insets, true, false, true);
  273. break;
  274. case TOP:
  275. paintImage(g, x + gapStart, y, Math.min(w - x - gapStart,
  276. gapSize), Math.min(h, size), image, insets, true,
  277. false, true);
  278. break;
  279. case BOTTOM:
  280. paintImage(g, x + gapStart, y + h - Math.min(h, size),
  281. Math.min(w - x - gapStart, gapSize),
  282. Math.min(h, size), image, insets, true, false,true);
  283. break;
  284. }
  285. }
  286. }
  287. /**
  288. * Paints the image and overlay image from the passed in style.
  289. *
  290. * @param g Graphics object to paint to
  291. * @param x X origin
  292. * @param y Y origin
  293. * @param w Width to draw to
  294. * @param h Height to draw to
  295. * @param info Used to fetch image, insets and overlay image from
  296. */
  297. private boolean paintPixmap(Graphics g, int x, int y, int w, int h,
  298. PixmapStyle.Info info, boolean drawCenter) {
  299. if (info != null) {
  300. Rectangle clip = g.getClipBounds();
  301. _clipX1 = clip.x;
  302. _clipY1 = clip.y;
  303. _clipX2 = _clipX1 + clip.width;
  304. _clipY2 = _clipY1 + clip.height;
  305. paintImage(g, x, y, w, h, info.getImage(), info.getImageInsets(),
  306. info.getStretch(), false, drawCenter);
  307. paintImage(g, x, y, w, h, info.getOverlayImage(),
  308. info.getOverlayInsets(), info.getOverlayStretch(),
  309. true, drawCenter);
  310. return true;
  311. }
  312. return false;
  313. }
  314. /**
  315. * Paints the image in the specified region.
  316. *
  317. * @param g Graphics object to paint to
  318. * @param x X origin
  319. * @param y Y origin
  320. * @param w Width to draw to
  321. * @param h Height to draw to
  322. * @param image Image to render
  323. * @param insets Insets used to determine portion of image that is fixed.
  324. */
  325. private void paintImage(Graphics g, int x, int y, int w, int h,
  326. Image image, Insets insets, boolean stretch,
  327. boolean overlay, boolean drawCenter) {
  328. if (image == null) {
  329. return;
  330. }
  331. if (insets == null) {
  332. insets = GTKPainter.EMPTY_INSETS;
  333. }
  334. int iw = image.getWidth(null);
  335. int ih = image.getHeight(null);
  336. if (iw <= 0 || ih <= 0) {
  337. return;
  338. }
  339. Object lastHint;
  340. Object renderingHint = RENDERING_HINT;
  341. if (renderingHint != null && stretch) {
  342. Graphics2D g2 = (Graphics2D)g;
  343. lastHint = g2.getRenderingHint(RenderingHints.KEY_INTERPOLATION);
  344. if (lastHint == null) {
  345. lastHint = RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR;
  346. }
  347. g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
  348. renderingHint);
  349. }
  350. else {
  351. lastHint = null;
  352. }
  353. if (!stretch) {
  354. if (overlay) {
  355. g.drawImage(image, x + w / 2 - iw / 2, y + h / 2 - ih / 2,
  356. null);
  357. }
  358. else {
  359. int lastIY = 0;
  360. for (int yCounter = y, maxY = y + h; yCounter < maxY;
  361. yCounter += (ih - lastIY), lastIY = 0) {
  362. int lastIX = 0;
  363. for (int xCounter = x, maxX = x + w; xCounter < maxX;
  364. xCounter += (iw - lastIX), lastIX = 0) {
  365. int dx2 = Math.min(maxX, xCounter + iw - lastIX);
  366. int dy2 = Math.min(maxY, yCounter + ih - lastIY);
  367. if (intersectsClip(xCounter, yCounter, dx2, dy2)) {
  368. g.drawImage(image, xCounter, yCounter, dx2, dy2,
  369. lastIX, lastIY, lastIX + dx2 -xCounter,
  370. lastIY + dy2 - yCounter, null);
  371. }
  372. }
  373. }
  374. }
  375. }
  376. else {
  377. int it = insets.top;
  378. int il = insets.left;
  379. int ib = insets.bottom;
  380. int ir = insets.right;
  381. // Constrain the insets to the size of the image
  382. if (it + ib >= ih) {
  383. ib = it = Math.max(0, ih / 2 - 1);
  384. }
  385. if (il + ir >= iw) {
  386. il = ir = Math.max(0, iw / 2 - 1);
  387. }
  388. // Constrain the insets to the size of the region we're painting
  389. // in.
  390. if (it + ib > h) {
  391. it = ib = Math.max(2, h / 2 - 1);
  392. }
  393. if (il + ir > w) {
  394. il = ir = Math.max(2, w / 2 - 1);
  395. }
  396. // left
  397. if (il > 0 && it + ib < ih) {
  398. drawChunk(image, g, stretch, x, y + it, x + il, y + h - ib, 0,
  399. it, il, ih - ib, false);
  400. }
  401. // top left
  402. if (il > 0 && it > 0) {
  403. g.drawImage(image, x, y, x + il, y + it, 0, 0, il, it, null);
  404. }
  405. // top
  406. if (it > 0 && il + ir < iw) {
  407. drawChunk(image, g, stretch, x + il, y, x + w - ir, y + it,
  408. il, 0, iw - ir, it, true);
  409. }
  410. // top right
  411. if (ir < iw && it > 0) {
  412. g.drawImage(image, x + w - ir, y, x + w, y + it, iw - ir, 0,
  413. iw, it, null);
  414. }
  415. // right
  416. if (ir < iw && it + ib < ih) {
  417. drawChunk(image, g, stretch, x + w - ir, y + it, x + w,
  418. y + h - ib, iw - ir, it, iw, ih - ib, false);
  419. }
  420. // bottom right
  421. if (ir < iw && ib < ih) {
  422. g.drawImage(image, x + w - ir, y + h - ib, x + w, y + h,
  423. iw - ir, ih - ib, iw, ih, null);
  424. }
  425. // bottom
  426. if (il + ir < iw && ib > 0) {
  427. drawChunk(image, g, stretch, x + il, y + h - ib, x + w - ir,
  428. y + h, il, ih - ib, iw - ir, ih, true);
  429. }
  430. // bottom left
  431. if (il > 0 && ib > 0) {
  432. g.drawImage(image, x, y + h - ib, x + il,
  433. y + h, 0, ih - ib, il, ih, null);
  434. }
  435. // center
  436. if (drawCenter && il + ir < iw && it + ib < ih) {
  437. g.drawImage(image, x + il, y + it, x + w - ir, y + h - ib,
  438. il, it, iw - ir, ih - ib, null);
  439. }
  440. }
  441. if (lastHint != null) {
  442. ((Graphics2D)g).setRenderingHint(RenderingHints.KEY_INTERPOLATION,
  443. lastHint);
  444. }
  445. }
  446. /**
  447. * Draws a portion of an image, stretched or tiled.
  448. *
  449. * @param image Image to render.
  450. * @param g Graphics to render to
  451. * @param stretch Whether the image should be stretched or timed in the
  452. * provided space.
  453. * @param dx1 X origin to draw to
  454. * @param dy1 Y origin to draw to
  455. * @param dx2 End x location to draw to
  456. * @param dy2 End y location to draw to
  457. * @param sx1 X origin to draw from
  458. * @param sy1 Y origin to draw from
  459. * @param sx2 Max x location to draw from
  460. * @param sy2 Max y location to draw from
  461. * @param xDirection Used if the image is not stretched. If true it
  462. * indicates the image should be tiled along the x axis.
  463. */
  464. private void drawChunk(Image image, Graphics g, boolean stretch,
  465. int dx1, int dy1, int dx2, int dy2, int sx1,
  466. int sy1, int sx2, int sy2,
  467. boolean xDirection) {
  468. if (dx2 - dx1 <= 0 || dy2 - dy1 <= 0 ||
  469. !intersectsClip(dx1, dy1, dx2, dy2)) {
  470. // Bogus location, nothing to paint
  471. return;
  472. }
  473. if (stretch) {
  474. g.drawImage(image, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, null);
  475. }
  476. else {
  477. int xSize = sx2 - sx1;
  478. int ySize = sy2 - sy1;
  479. int deltaX;
  480. int deltaY;
  481. if (xDirection) {
  482. deltaX = xSize;
  483. deltaY = 0;
  484. }
  485. else {
  486. deltaX = 0;
  487. deltaY = ySize;
  488. }
  489. while (dx1 < dx2 && dy1 < dy2) {
  490. int newDX2 = Math.min(dx2, dx1 + xSize);
  491. int newDY2 = Math.min(dy2, dy1 + ySize);
  492. if (intersectsClip(dx1, dy1, newDX2, newDY2)) {
  493. g.drawImage(image, dx1, dy1, newDX2, newDY2,
  494. sx1, sy1, sx1 + newDX2 - dx1,
  495. sy1 + newDY2 - dy1, null);
  496. }
  497. dx1 += deltaX;
  498. dy1 += deltaY;
  499. }
  500. }
  501. }
  502. /**
  503. * Returns true if the passed in region intersects the clip.
  504. */
  505. private boolean intersectsClip(int x1, int y1, int x2, int y2) {
  506. return ((x2 < x1 || x2 > _clipX1) &&
  507. (y2 < y1 || y2 > _clipY1) &&
  508. (_clipX2 < _clipX1 || _clipX2 > x1) &&
  509. (_clipY2 < _clipY1 || _clipY2 > y1));
  510. }
  511. }