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