001: /*******************************************************************************
002: * Copyright (c) 2007-2008 Kirill Grouchnikov and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *******************************************************************************/package org.jvnet.substance.bramble;
008:
009: import java.awt.*;
010: import java.awt.image.*;
011: import java.lang.reflect.Method;
012: import java.util.HashMap;
013: import java.util.Map;
014:
015: import javax.swing.JComponent;
016: import javax.swing.plaf.ComponentUI;
017:
018: import org.eclipse.swt.SWT;
019: import org.jvnet.substance.SubstanceFillBackgroundDelegate;
020: import org.jvnet.substance.SubstanceLookAndFeel;
021: import org.jvnet.substance.painter.text.AbstractTextPainter;
022: import org.jvnet.substance.painter.text.SubstanceTextPainter;
023: import org.jvnet.substance.utils.SubstanceCoreUtilities;
024: import org.jvnet.substance.utils.SubstanceSizeUtils;
025:
026: /**
027: * Text painter that uses SWT to render the text with native libraries.
028: *
029: * @author Kirill Grouchnikov
030: */
031: public class BrambleTextPainter extends AbstractTextPainter {
032: /**
033: * Background image to be used for aggregating the layers in AWT / Java2D
034: * painting. Is resized (made larger) as required based on the parameters
035: * passed to the {@link #init(JComponent, Rectangle, boolean)} method.
036: */
037: protected BufferedImage backgroundImage;
038:
039: /**
040: * Fill delegate - is used to paint the background and watermark overlays.
041: */
042: protected SubstanceFillBackgroundDelegate fillDelegate;
043:
044: /**
045: * The current clip set in {@link #init(JComponent, Rectangle, boolean)}.
046: * Is used to restrict the painting to the relevant area. For example,
047: * painting cells in a list is clipped to the area of the specific cell -
048: * this dramatically speeds up the "ping-pong" between AWT and SWT images.
049: */
050: protected java.awt.Rectangle currClip;
051:
052: /**
053: * Cache of loaded SWT fonts. Used to speed up font loading during the
054: * native text painting in {@link #renderSurface(Graphics)}.
055: */
056: private Map<String, org.eclipse.swt.graphics.Font> fontMap;
057:
058: /**
059: * Creates a new SWT-based text painter.
060: */
061: public BrambleTextPainter() {
062: this .fontMap = new HashMap<String, org.eclipse.swt.graphics.Font>();
063: this .fillDelegate = new SubstanceFillBackgroundDelegate();
064: }
065:
066: /*
067: * (non-Javadoc)
068: *
069: * @see org.jvnet.substance.painter.text.SubstanceTextPainter#needsBackgroundImage()
070: */
071: public boolean needsBackgroundImage() {
072: return true;
073: }
074:
075: /*
076: * (non-Javadoc)
077: *
078: * @see org.jvnet.substance.painter.text.SubstanceTextPainter#renderSurface(java.awt.Graphics)
079: */
080: public void renderSurface(Graphics g) {
081: Graphics2D graphics = (Graphics2D) g.create();
082: if (graphics.getClip() == null) {
083: graphics.setClip(currClip.x, currClip.y, currClip.width,
084: currClip.height);
085: }
086: Graphics2D biGraphics = (Graphics2D) backgroundImage
087: .getGraphics().create();
088: biGraphics.setClip(0, 0, this .comp.getWidth(), this .comp
089: .getHeight());
090: biGraphics.translate(-currClip.x, -currClip.y);
091: for (SubstanceTextPainter.BackgroundPaintingCallback callback : this .callbackList) {
092: callback.paintBackground(biGraphics);
093: }
094: biGraphics.dispose();
095:
096: org.eclipse.swt.graphics.Image swtImage = null;
097: org.eclipse.swt.graphics.TextLayout swtTextLayout = null;
098: org.eclipse.swt.graphics.GC swtGraphicsContext = null;
099:
100: try {
101: // int width = this.currBackgroundWidth;
102: // int height = this.currBackgroundHeight;// clip.height;
103: //
104: // if ((width == 0) || (height == 0))
105: // return;
106:
107: if ((this .currClip.height == 0)
108: || (this .currClip.width == 0))
109: return;
110:
111: org.eclipse.swt.widgets.Display swtDisplay = org.eclipse.swt.widgets.Display
112: .getDefault();
113:
114: org.eclipse.swt.graphics.PaletteData PALETTE_DATA = new org.eclipse.swt.graphics.PaletteData(
115: 0xFF0000, 0xFF00, 0xFF);
116: org.eclipse.swt.graphics.ImageData swtImageData = new org.eclipse.swt.graphics.ImageData(
117: this .currClip.width, this .currClip.height, 24,
118: PALETTE_DATA);
119: // int step = swtImageData.depth / 8;
120: byte[] data = swtImageData.data;
121: int[] awtPixels = null;
122: awtPixels = this .backgroundImage.getRGB(0, 0,
123: this .currClip.width, this .currClip.height,
124: awtPixels, 0, this .currClip.width);
125: // System.out.println(System.currentTimeMillis()
126: // + " : before AWT -> SWT pixel copy");
127: for (int i = 0; i < this .currClip.height; i++) {
128: int idx = i * swtImageData.bytesPerLine;
129: for (int j = 0; j < this .currClip.width; j++) {
130: int rgb = awtPixels[j + i * this .currClip.width];
131: for (int k = swtImageData.depth - 8; k >= 0; k -= 8) {
132: data[idx++] = (byte) ((rgb >> k) & 0xFF);
133: }
134: }
135: }
136: // System.out.println(System.currentTimeMillis()
137: // + " : after AWT -> SWT pixel copy");
138:
139: // Create SWT image and render some text in it
140: swtImage = new org.eclipse.swt.graphics.Image(swtDisplay,
141: swtImageData);
142: // System.out.println(System.currentTimeMillis()
143: // + " : after SWT image creation " + swtImageData.width + ":"
144: // + swtImageData.height);
145: swtGraphicsContext = new org.eclipse.swt.graphics.GC(
146: swtImage);
147: swtGraphicsContext.setTextAntialias(SWT.DEFAULT);
148:
149: // paint the text
150: boolean hasText = false;
151: swtTextLayout = new org.eclipse.swt.graphics.TextLayout(
152: swtDisplay);
153: for (AbstractTextPainter.TextLineInfo textLine : textLines) {
154: if ((textLine.text != null)
155: && (textLine.text.length() > 0)) {
156: hasText = true;
157:
158: org.eclipse.swt.graphics.Color swtColor = null;
159: org.eclipse.swt.graphics.Font swtFont = null;
160:
161: // color
162: java.awt.Color awtColor = textLine.color;
163: swtColor = new org.eclipse.swt.graphics.Color(
164: swtDisplay, awtColor.getRed(), awtColor
165: .getGreen(), awtColor.getBlue());
166: // swtColor = new org.eclipse.swt.graphics.Color(swtDisplay,
167: // 255, 0, 0);
168: swtGraphicsContext.setForeground(swtColor);
169:
170: org.eclipse.swt.graphics.Rectangle swtClipping = swtGraphicsContext
171: .getClipping();
172:
173: java.awt.Rectangle awtClipping = textLine.clip;
174: if (awtClipping != null) {
175: swtGraphicsContext.setClipping(awtClipping.x,
176: awtClipping.y, awtClipping.width,
177: awtClipping.height);
178: }
179:
180: org.eclipse.swt.graphics.Transform currSwtTransform = new org.eclipse.swt.graphics.Transform(
181: swtDisplay);
182: swtGraphicsContext.getTransform(currSwtTransform);
183: org.eclipse.swt.graphics.Transform newSwtTransform = null;
184: java.awt.geom.AffineTransform awtTransform = textLine.transform;
185: if (awtTransform != null) {
186: // swtGraphicsContext.setTransform();
187: newSwtTransform = new org.eclipse.swt.graphics.Transform(
188: swtDisplay, (float) awtTransform
189: .getScaleX(),
190: (float) awtTransform.getShearX(),
191: (float) awtTransform.getShearY(),
192: (float) awtTransform.getScaleY(),
193: (float) awtTransform.getTranslateX(),
194: (float) awtTransform.getTranslateY());
195: swtGraphicsContext
196: .setTransform(newSwtTransform);
197: }
198: // if (awtTransform != null) {
199: // org.eclipse.swt.graphics.Transform _transform = new
200: // org.eclipse.swt.graphics.Transform(
201: // swtDisplay);
202: // swtGraphicsContext.getTransform(_transform);
203: // System.out.println(_transform);
204: // System.out.println(swtGraphicsContext.getClipping());
205: // }
206:
207: // font
208: java.awt.Font awtFont = textLine.font;
209: // convert from AWT pixels into SWT points
210: int pixelSize = awtFont.getSize();
211: // int pointSize = (int) Math.round(pixelSize * 72.0 / DPI);
212: int pointSize = (int) Math.round(pixelSize
213: / SubstanceSizeUtils
214: .getPointsToPixelsRatio());
215: // convert AWT font style into SWT font style
216: int swtFontStyle = SWT.NORMAL;
217: if (awtFont.getStyle() == java.awt.Font.BOLD)
218: swtFontStyle = SWT.BOLD;
219: if (awtFont.getStyle() == (java.awt.Font.BOLD + java.awt.Font.ITALIC))
220: swtFontStyle = SWT.BOLD | SWT.ITALIC;
221: if (awtFont.getStyle() == java.awt.Font.ITALIC)
222: swtFontStyle = SWT.ITALIC;
223: // cache font instance - TODO: limit the cache size and
224: // dispose the removed elements
225: // long tstart = System.nanoTime();
226: String key = awtFont.getFamily() + ":" + pointSize
227: + ":" + swtFontStyle;
228: swtFont = fontMap.get(key);
229: if (swtFont == null) {
230: swtFont = new org.eclipse.swt.graphics.Font(
231: swtDisplay, awtFont.getFamily(),
232: pointSize, swtFontStyle);
233: fontMap.put(key, swtFont);
234: }
235: // long tend = System.nanoTime();
236: // swtFont = new org.eclipse.swt.graphics.Font(swtDisplay,
237: // "Segoe UI", 9, SWT.BOLD);
238:
239: // System.out.println(tend - tstart);
240:
241: swtTextLayout.setFont(swtFont);
242:
243: swtTextLayout.setText(textLine.text);
244: org.eclipse.swt.graphics.FontData swtFontData = swtFont
245: .getFontData()[0];
246: int deltaY = ((textLine.textRect.height + swtFontData
247: .getHeight()) % 2 == 1) ? 1 : 0;
248: // System.out.println(textLine.textRect.height + ":"
249: // + swtFD.getHeight());
250: swtTextLayout.draw(swtGraphicsContext,
251: textLine.textRect.x - this .currClip.x,
252: textLine.textRect.y + deltaY
253: - this .currClip.y);
254:
255: // uncomment to see the bounding box of the text
256: // swtGraphicsContext.drawRectangle(textLine.textRect.x,
257: // textLine.textRect.y, textLine.textRect.width - 1,
258: // textLine.textRect.height - 1);
259:
260: // System.out.println(textLine.text
261: // + "->"
262: // + swtTextLayout.getFont().getFontData()[0]
263: // .getHeight() + ":" + pointSize);
264:
265: if (textLine.mnemonicIndex >= 0) {
266: org.eclipse.swt.graphics.Point swtStartMnemonicPoint = swtTextLayout
267: .getLocation(textLine.mnemonicIndex,
268: false);
269: org.eclipse.swt.graphics.Point swtEndMnemonicPoint = swtTextLayout
270: .getLocation(textLine.mnemonicIndex,
271: true);
272: org.eclipse.swt.graphics.FontMetrics swtFontMetrics = swtTextLayout
273: .getLineMetrics(0);
274: int underlineY = textLine.textRect.y
275: + swtStartMnemonicPoint.y
276: + swtStartMnemonicPoint.y
277: + swtFontMetrics.getHeight()
278: - swtFontMetrics.getDescent() + 1
279: + deltaY - this .currClip.y;
280: int start = textLine.textRect.x
281: + swtStartMnemonicPoint.x + 1
282: - this .currClip.x;
283: int end = textLine.textRect.x
284: + swtEndMnemonicPoint.x - 1
285: - this .currClip.x;
286: swtGraphicsContext.drawLine(start, underlineY,
287: end, underlineY);
288: }
289:
290: // if (awtTransform != null) {
291: // System.out.println("Painted '" + textLine.text
292: // + "' at " + textLine.textRect + " on "
293: // + this.currBackgroundWidth + "*"
294: // + this.currBackgroundHeight + ", font "
295: // + swtFont.getFontData()[0].getName() + ":"
296: // + swtFont.getFontData()[0].getHeight()
297: // + ", color " + swtColor);
298: // }
299:
300: swtColor.dispose();
301: // swtFont.dispose();
302:
303: // very important - first restore the transform and only
304: // then the clipping. Otherwise the clipping will be
305: // computed incorrectly.
306: swtGraphicsContext.setTransform(currSwtTransform);
307: swtGraphicsContext.setClipping(swtClipping);
308:
309: currSwtTransform.dispose();
310: if (newSwtTransform != null)
311: newSwtTransform.dispose();
312: }
313: }
314:
315: if (!hasText && !this .toEnforceRenderOnNoTexts)
316: return;
317:
318: org.eclipse.swt.graphics.ImageData imageData = swtImage
319: .getImageData();
320:
321: // Create an AWT image for copying the bits from SWT image
322: BufferedImage bi = new BufferedImage(this .currClip.width,
323: this .currClip.height, BufferedImage.TYPE_INT_ARGB);
324:
325: int redMask = imageData.palette.redMask;
326: int redShift = imageData.palette.redShift;
327: int greenMask = imageData.palette.greenMask;
328: int greenShift = imageData.palette.greenShift;
329: int blueShift = imageData.palette.blueShift;
330: int blueMask = imageData.palette.blueMask;
331:
332: int[] lineData = new int[this .currClip.width];
333:
334: // Get the raw data buffer and write directly to it.
335: // Compared to BufferedImage.setRGB this speeds up the
336: // image creation by the factor of three.
337: WritableRaster srcRaster = bi.getRaster();
338: DataBufferInt dataBuffer = (DataBufferInt) srcRaster
339: .getDataBuffer();
340: int[] rawData = dataBuffer.getData();
341:
342: // System.out.println(System.currentTimeMillis()
343: // + " : before SWT -> AWT pixel copy");
344: for (int y = 0; y < this .currClip.height; y++) {
345: imageData.getPixels(0, y, this .currClip.width,
346: lineData, 0);
347: // Analyze each pixel value in the line
348: for (int x = 0; x < this .currClip.width; x++) {
349: // Extract the red, green and blue component -
350: // see org/eclipse/swt/internal/image/PngEncoder.java
351: int pixelValue = lineData[x];
352: int alpha = imageData.getAlpha(x, y);
353:
354: int red = pixelValue & redMask;
355: red = (redShift < 0) ? red >>> -redShift
356: : red << redShift;
357: int green = pixelValue & greenMask;
358: green = (greenShift < 0) ? green >>> -greenShift
359: : green << greenShift;
360: int blue = pixelValue & blueMask;
361: blue = (blueShift < 0) ? blue >>> -blueShift
362: : blue << blueShift;
363:
364: rawData[x + y * this .currClip.width] = alpha << 24
365: | red << 16 | green << 8 | blue;
366: }
367: }
368: // System.out.println(System.currentTimeMillis()
369: // + " : after SWT -> AWT pixel copy");
370:
371: // finally, paint the image
372: graphics.drawImage(bi, currClip.x, currClip.y, currClip.x
373: + currClip.width, currClip.y + currClip.height, 0,
374: 0, currClip.width, currClip.height, null);
375:
376: // System.out.println(System.currentTimeMillis()
377: // + " : after AWT painting");
378: // graphics.drawImage(bi, 0, 0, null);
379: } finally {
380: // don't forget to dispose to prevent the "No more handles"
381: // SWTError.
382: if (swtImage != null)
383: swtImage.dispose();
384: if (swtTextLayout != null)
385: swtTextLayout.dispose();
386: if (swtGraphicsContext != null)
387: swtGraphicsContext.dispose();
388:
389: graphics.dispose();
390: }
391:
392: }
393:
394: /*
395: * (non-Javadoc)
396: *
397: * @see org.jvnet.substance.painter.text.AbstractTextPainter#init(javax.swing.JComponent,
398: * java.awt.Rectangle, boolean)
399: */
400: @Override
401: public void init(JComponent comp, Rectangle clip,
402: boolean toEnforceRenderOnNoTexts) {
403: super .init(comp, clip, toEnforceRenderOnNoTexts);
404: this .currClip = new Rectangle();
405: if (clip != null) {
406: this .currClip.x = Math.max(0, clip.x);
407: this .currClip.y = Math.max(0, clip.y);
408: int origRight = clip.x + clip.width;
409: this .currClip.width = Math.min(comp.getWidth(), origRight
410: - this .currClip.x);
411: int origBottom = clip.y + clip.height;
412: this .currClip.height = Math.min(comp.getHeight(),
413: origBottom - clip.y);
414:
415: // System.out.println(System.currentTimeMillis() + " : background "
416: // + comp.getClass().getName() + " with clip ["
417: // + this.currClip.x + ":" + this.currClip.y + "] * ["
418: // + this.currClip.width + ":" + this.currClip.height + "]");
419: } else {
420: this .currClip.x = 0;
421: this .currClip.y = 0;
422: this .currClip.width = comp.getWidth();
423: this .currClip.height = comp.getHeight();
424:
425: // if (comp.isDisplayable() && comp.isShowing()) {
426: // Point tlOnScreen = comp.getLocationOnScreen();
427: // Point brOnScreen = new Point(tlOnScreen.x + comp.getWidth(),
428: // tlOnScreen.y + comp.getHeight());
429: // if (tlOnScreen.x < 0) {
430: // this.currClip.x = -tlOnScreen.x;
431: // this.currClip.width += tlOnScreen.x;
432: // }
433: // if (tlOnScreen.y < 0) {
434: // this.currClip.y = -tlOnScreen.y;
435: // this.currClip.height += tlOnScreen.y;
436: // }
437: // if (brOnScreen.x >= this.backgroundImage.getWidth()) {
438: // this.currClip.width -= (brOnScreen.x - this.backgroundImage
439: // .getWidth());
440: // }
441: // if (brOnScreen.y >= this.backgroundImage.getHeight()) {
442: // this.currClip.height -= (brOnScreen.y - this.backgroundImage
443: // .getHeight());
444: // }
445: // if (this.currClip.width < 0) {
446: // this.currClip.width = 0;
447: // }
448: // if (this.currClip.height < 0) {
449: // this.currClip.height = 0;
450: // }
451: // // System.err.println(comp.getClass().getName());
452: // // System.err.println(comp.getWidth() + ":" + comp.getHeight() +
453: // " at " + tlOnScreen + " to " + brOnScreen);
454: // // System.err.println(" --> " + this.currClip);
455: // }
456: }
457:
458: if ((this .backgroundImage == null)
459: || (this .backgroundImage.getWidth() < this .currClip.width)
460: || (this .backgroundImage.getHeight() < this .currClip.height)) {
461: // allocate new background image since the current one is either
462: // null or too small
463: this .backgroundImage = SubstanceCoreUtilities
464: .getBlankImage(this .currClip.width,
465: this .currClip.height);
466: }
467: }
468:
469: public void setBackgroundFill(JComponent comp,
470: Color backgroundFillColor, boolean toOverlayWatermark,
471: int watermarkOffsetX, int watermarkOffsetY) {
472: if (this .toIgnoreBackgroundFill(comp))
473: return;
474:
475: Graphics g = this .backgroundImage.getGraphics();
476: Graphics2D g2d = (Graphics2D) g.create();
477:
478: // Special handling of containers that have custom background fill
479: // (not solid, for example status bar from SwingX or even title pane
480: // under decorated mode). Since SWT doesn't support transparent
481: // background, we're ignoring the component background color and
482: // painting the container's relevant part instead
483: Container parentSpecial = SubstanceCoreUtilities
484: .getSpecialBackgroundFillContainer(comp);
485: if (parentSpecial != null) {
486: // compute the offsets
487: int dx = 0;
488: int dy = 0;
489: Component c = comp;
490: while (c != parentSpecial) {
491: dx += c.getX();
492: dy += c.getY();
493: c = c.getParent();
494: }
495: // get the parent's UI delegate
496: try {
497: Method getUI = parentSpecial.getClass().getMethod(
498: "getUI", new Class[0]);
499: if (getUI != null) {
500: ComponentUI parentUI = (ComponentUI) getUI.invoke(
501: parentSpecial, new Object[0]);
502: Graphics2D g2dForParent = (Graphics2D) g2d.create();
503: g2dForParent.translate(-dx, -dy);
504: parentUI.update(g2dForParent,
505: (JComponent) parentSpecial);
506: g2dForParent.dispose();
507: }
508: } catch (Throwable t) {
509: }
510: } else {
511: if (backgroundFillColor != null) {
512: g2d.setColor(backgroundFillColor);
513: g2d.fillRect(0, 0, comp.getWidth(), comp.getHeight());
514: // if (comp instanceof JMenuItem) {
515: // System.out.println("Filling " + ((JMenuItem) comp).getText()
516: // + "[" + ((JMenuItem) comp).isEnabled() + "] with "
517: // + backgroundFillColor.getRed() + ":"
518: // + backgroundFillColor.getGreen() + ":"
519: // + backgroundFillColor.getBlue());
520: // }
521: }
522: }
523:
524: if (comp.isShowing()
525: && (SubstanceCoreUtilities.toDrawWatermark(comp) || SubstanceCoreUtilities
526: .toBleedWatermark(comp)) && toOverlayWatermark) {
527: g2d.translate(-watermarkOffsetX, -watermarkOffsetY);
528: SubstanceLookAndFeel.getCurrentWatermark()
529: .drawWatermarkImage(g2d, comp,
530: this .currClip.x + watermarkOffsetX,
531: this .currClip.y + watermarkOffsetY,
532: this .currClip.width, this .currClip.height);
533: g2d.translate(watermarkOffsetX, watermarkOffsetY);
534: }
535: // System.out.println("Fill "
536: // + this.currBackgroundWidth
537: // + "*"
538: // + this.currBackgroundHeight
539: // + " with "
540: // + backgroundFillColor
541: // + " from "
542: // + comp.getClass().getName()
543: // + (backgroundPaintingCallback != null ? " callback"
544: // : " no callback"));
545:
546: // g2d.setColor(Color.green);
547: // g2d.setComposite(AlphaComposite.SrcOver);
548: // g2d.fillRect(currClip.x, currClip.y, currClip.width,
549: // currClip.height);
550:
551: g2d.dispose();
552: }
553:
554: /*
555: * (non-Javadoc)
556: *
557: * @see org.jvnet.substance.painter.text.SubstanceTextPainter#attachVerticalText(javax.swing.JComponent,
558: * java.awt.Rectangle, java.lang.String, int, java.awt.Font,
559: * java.awt.Color, java.awt.Rectangle, boolean)
560: */
561: public void attachVerticalText(JComponent comp, Rectangle textRect,
562: String text, int mnemonicIndex, Font font, Color color,
563: Rectangle clip, boolean isFromBottomToTop) {
564: java.awt.geom.AffineTransform at = null;
565:
566: FontMetrics fm = comp.getFontMetrics(font);
567: if (isFromBottomToTop) {
568: at = java.awt.geom.AffineTransform.getTranslateInstance(
569: textRect.x - fm.getDescent(), textRect.y
570: + textRect.height);
571: at.rotate(Math.PI / 2);
572: } else {
573: at = java.awt.geom.AffineTransform.getTranslateInstance(
574: textRect.x + textRect.width + fm.getDescent(),
575: textRect.y);
576: at.rotate(-Math.PI / 2);
577: }
578: Rectangle newRect = new Rectangle(0, 0, textRect.width,
579: textRect.height);
580: this .textLines.add(new TextLineInfo(newRect, text,
581: mnemonicIndex, font, color, clip, at));
582: }
583:
584: /*
585: * (non-Javadoc)
586: *
587: * @see org.jvnet.substance.painter.text.SubstanceTextPainter#isNative()
588: */
589: public boolean isNative() {
590: return true;
591: }
592:
593: /*
594: * (non-Javadoc)
595: *
596: * @see org.jvnet.substance.painter.text.SubstanceTextPainter#dispose()
597: */
598: public void dispose() {
599: this .backgroundImage = null;
600: for (org.eclipse.swt.graphics.Font swtFont : fontMap.values())
601: swtFont.dispose();
602: fontMap.clear();
603: }
604:
605: /*
606: * (non-Javadoc)
607: *
608: * @see org.jvnet.substance.painter.text.SubstanceTextPainter#getTextBounds(java.awt.Component,
609: * java.awt.Font, java.lang.String)
610: */
611: public java.awt.Dimension getTextBounds(Component comp,
612: java.awt.Font awtFont, String text) {
613: org.eclipse.swt.graphics.TextLayout swtTextLayout = null;
614: try {
615: org.eclipse.swt.widgets.Display swtDisplay = org.eclipse.swt.widgets.Display
616: .getDefault();
617:
618: swtTextLayout = new org.eclipse.swt.graphics.TextLayout(
619: swtDisplay);
620: org.eclipse.swt.graphics.Font swtFont = null;
621:
622: double pointsToPixelsRatio = SubstanceSizeUtils
623: .getPointsToPixelsRatio();
624:
625: // convert from AWT pixels into SWT points
626: int pixelSize = awtFont.getSize();
627: // int pointSize = (int) Math.round(pixelSize * 72.0 / DPI);
628: int pointSize = (int) Math.round(pixelSize
629: / pointsToPixelsRatio);
630: // convert AWT font style into SWT font style
631: int swtFontStyle = SWT.NORMAL;
632: if (awtFont.getStyle() == java.awt.Font.BOLD)
633: swtFontStyle = SWT.BOLD;
634: if (awtFont.getStyle() == (java.awt.Font.BOLD + java.awt.Font.ITALIC))
635: swtFontStyle = SWT.BOLD | SWT.ITALIC;
636: if (awtFont.getStyle() == java.awt.Font.ITALIC)
637: swtFontStyle = SWT.ITALIC;
638: // cache font instance - TODO: limit the cache size and
639: // dispose the removed elements
640: // long tstart = System.nanoTime();
641: String key = awtFont.getFamily() + ":" + pointSize + ":"
642: + swtFontStyle;
643: swtFont = fontMap.get(key);
644: if (swtFont == null) {
645: swtFont = new org.eclipse.swt.graphics.Font(swtDisplay,
646: awtFont.getFamily(), pointSize, swtFontStyle);
647: fontMap.put(key, swtFont);
648: }
649:
650: swtTextLayout.setFont(swtFont);
651: swtTextLayout.setText(text);
652:
653: org.eclipse.swt.graphics.Rectangle swtBounds = swtTextLayout
654: .getBounds();
655: return new java.awt.Dimension(swtBounds.width,
656: swtBounds.height);
657: } finally {
658: if (swtTextLayout != null)
659: swtTextLayout.dispose();
660: }
661: }
662: }
|