Merged with mainline at revision 126347.
[official-gcc.git] / libjava / classpath / gnu / java / awt / peer / x / XGraphics.java
blob134d7d3305e2d45bd05942b7590e44591a2e86ec
1 /* XGraphics.java -- The Graphics implementation for X
2 Copyright (C) 2006 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
39 package gnu.java.awt.peer.x;
41 import gnu.x11.Colormap;
42 import gnu.x11.Data;
43 import gnu.x11.Display;
44 import gnu.x11.Drawable;
45 import gnu.x11.GC;
46 import gnu.x11.Pixmap;
47 import gnu.x11.Point;
48 import gnu.x11.image.ZPixmap;
50 import java.awt.AWTError;
51 import java.awt.Color;
52 import java.awt.Font;
53 import java.awt.FontMetrics;
54 import java.awt.Graphics;
55 import java.awt.Image;
56 import java.awt.Rectangle;
57 import java.awt.Shape;
58 import java.awt.Toolkit;
59 import java.awt.Transparency;
60 import java.awt.image.BufferedImage;
61 import java.awt.image.ImageObserver;
62 import java.awt.image.ImageProducer;
63 import java.text.AttributedCharacterIterator;
64 import java.util.HashMap;
66 public class XGraphics
67 extends Graphics
68 implements Cloneable
71 /**
72 * The X Drawable to draw on.
74 private Drawable xdrawable;
76 /**
77 * The X graphics context (GC).
79 private GC xgc;
81 /**
82 * The current translation.
84 private int translateX;
85 private int translateY;
87 /**
88 * The current clip. Possibly null.
90 private Rectangle clip;
92 /**
93 * The current font, possibly null.
95 private Font font;
97 /**
98 * The current foreground color, possibly null.
100 private Color foreground;
103 * Indicates if this object has been disposed.
105 private boolean disposed = false;
107 // TODO: Workaround for limitation in current Escher.
108 private Pixmap.Format pixmapFormat;
109 private int imageByteOrder;
110 private int pixelByteCount;
113 * Creates a new XGraphics on the specified X Drawable.
115 * @param d the X Drawable for which we create the Graphics
117 XGraphics(Drawable d)
119 xdrawable = d;
120 xgc = new GC(d);
121 translateX = 0;
122 translateY = 0;
123 clip = new Rectangle(0, 0, d.width, d.height);
125 Display display = xdrawable.display;
126 pixmapFormat = display.default_pixmap_format;
127 imageByteOrder = display.image_byte_order;
128 pixelByteCount = pixmapFormat.bits_per_pixel () / 8;
132 * Creates an exact copy of this graphics context.
134 * @return an exact copy of this graphics context
136 public Graphics create()
138 XGraphics copy = (XGraphics) clone();
139 return copy;
143 * Translates the origin by (x, y).
145 public void translate(int x, int y)
147 translateX += x;
148 translateY += y;
149 if (clip != null)
151 clip.x -= x;
152 clip.y -= y;
157 * Returns the current foreground color, possibly <code>null</code>.
159 * @return the current foreground color, possibly <code>null</code>
161 public Color getColor()
163 return foreground;
167 * Sets the current foreground color. A <code>null</code> value doesn't
168 * change the current setting.
170 * @param c the foreground color to set
172 public void setColor(Color c)
174 if (c != null)
176 XToolkit tk = (XToolkit) Toolkit.getDefaultToolkit();
177 HashMap colorMap = tk.colorMap;
178 gnu.x11.Color col = (gnu.x11.Color) colorMap.get(c);
179 if (col == null)
181 Colormap map = xdrawable.display.default_colormap;
182 col = map.alloc_color (c.getRed() * 256,
183 c.getGreen() * 256,
184 c.getBlue() * 256);
185 colorMap.put(c, col);
187 xgc.set_foreground(col);
188 foreground = c;
192 public void setPaintMode()
194 // FIXME: Implement this.
195 throw new UnsupportedOperationException("Not yet implemented");
198 public void setXORMode(Color color)
200 // FIXME: Implement this.
201 throw new UnsupportedOperationException("Not yet implemented");
205 * Returns the current font, possibly <code>null</code>.
207 * @return the current font, possibly <code>null</code>
209 public Font getFont()
211 return font;
215 * Sets the font on the graphics context. A <code>null</code> value doesn't
216 * change the current setting.
218 * @param f the font to set
220 public void setFont(Font f)
222 if (f != null)
224 XFontPeer xFontPeer = (XFontPeer) f.getPeer();
225 xgc.set_font(xFontPeer.getXFont());
226 font = f;
231 * Returns the font metrics for the specified font.
233 * @param font the font for which we want the font metrics
235 * @return the font metrics for the specified font
237 public FontMetrics getFontMetrics(Font font)
239 if (font == null)
241 if (this.font == null)
242 setFont(new Font("Dialog", Font.PLAIN, 12));
243 font = this.font;
245 XFontPeer xFontPeer = (XFontPeer) font.getPeer();
246 return xFontPeer.getFontMetrics(font);
250 * Returns the bounds of the current clip.
252 * @return the bounds of the current clip
254 public Rectangle getClipBounds()
256 return clip != null ? clip.getBounds() : null;
260 * Clips the current clip with the specified clip.
262 public void clipRect(int x, int y, int width, int height)
264 if (clip == null)
266 clip = new Rectangle(x, y, width, height);
268 else
270 computeIntersection(x, y, width, height, clip);
272 // Update the X clip setting.
273 setXClip(clip.x, clip.y, clip.width, clip.height);
277 * Returns <code>true</code> when the specified rectangle intersects with
278 * the current clip, <code>false</code> otherwise. This is overridden to
279 * avoid unnecessary creation of Rectangles via getBounds().
281 * @param x the x coordinate of the rectangle
282 * @param y the y coordinate of the rectangle
283 * @param w the width of the rectangle
284 * @param h the height of the rectangle
286 * @return <code>true</code> when the specified rectangle intersects with
287 * the current clip, <code>false</code> otherwise
289 public boolean hitClip(int x, int y, int w, int h)
291 boolean hit;
292 if (clip == null)
294 hit = true;
296 else
298 // It's easier to determine if the rectangle lies outside the clip,
299 // so we determine that and reverse the result (if it's not completely
300 // outside, it most likely hits the clip rectangle).
301 int x2 = x + w;
302 int y2 = y + h;
303 int clipX2 = clip.x + clip.width;
304 int clipY2 = clip.y + clip.height;
305 boolean outside = (x < clip.x && x2 < clip.x) // Left.
306 || (x > clipX2 && x2 > clipX2) // Right.
307 || (y < clip.y && y2 < clip.y) // Top.
308 || (y > clipY2 && y2 > clipY2); // Bottom.
309 hit = ! outside;
311 return hit;
314 public void setClip(int x, int y, int width, int height)
316 if (clip != null)
317 clip.setBounds(x, y, width, height);
318 else
319 clip = new Rectangle(x, y, width, height);
320 setXClip(clip.x, clip.y, clip.width, clip.height);
324 * Sets the clip on the X server GC. The coordinates are not yet translated,
325 * this will be performed by the X server.
327 * @param x the clip, X coordinate
328 * @param y the clip, Y coordinate
329 * @param w the clip, width
330 * @param h the clip, height
332 private void setXClip(int x, int y, int w, int h)
334 gnu.x11.Rectangle[] clipRects = new gnu.x11.Rectangle[] {
335 new gnu.x11.Rectangle(x, y, w, h) };
336 xgc.set_clip_rectangles(translateX, translateY, clipRects, GC.YX_BANDED);
339 public Shape getClip()
341 // Return a copy here, so nobody can trash our clip.
342 return clip == null ? null : clip.getBounds();
346 * Sets the current clip.
348 * @param c the clip to set
350 public void setClip(Shape c)
352 if (c != null)
354 Rectangle b;
355 if (c instanceof Rectangle)
357 b = (Rectangle) c;
359 else
361 b = c.getBounds();
363 clip.setBounds(b);
364 setXClip(b.x, b.y, b.width, b.height);
366 else
368 clip.setBounds(0, 0, xdrawable.width, xdrawable.height);
369 setXClip(0, 0, xdrawable.width, xdrawable.height);
373 public void copyArea(int x, int y, int width, int height, int dx, int dy)
375 // Clip and translate src rectangle.
376 int srcX = Math.min(Math.max(x, clip.x), clip.x + clip.width)
377 + translateX;
378 int srcY = Math.min(Math.max(y, clip.y), clip.y + clip.height)
379 + translateY;
380 int srcWidth = Math.min(Math.max(x + width, clip.x),
381 clip.x + clip.width) - x;
382 int srcHeight = Math.min(Math.max(y + height, clip.y),
383 clip.y + clip.height) - y;
384 xdrawable.copy_area(xdrawable, xgc, srcX, srcY, srcWidth, srcHeight,
385 srcX + dx, srcY + dy);
389 * Draws a line from point (x1, y1) to point (x2, y2).
391 public void drawLine(int x1, int y1, int x2, int y2)
393 //System.err.println("drawLine: " + (x1 + translateX) + ", " + ( y1 + translateY) + ", " + (x2 + translateX) + ", " + (y2 + translateY) + " on: " + xdrawable);
394 xdrawable.line(xgc, x1 + translateX, y1 + translateY,
395 x2 + translateX, y2 + translateY);
399 * Fills the specified rectangle.
401 public void fillRect(int x, int y, int width, int height)
403 xdrawable.rectangle(xgc, x + translateX, y + translateY,
404 width, height, true);
407 public void clearRect(int x, int y, int width, int height)
409 xgc.set_foreground(Color.WHITE.getRGB());
410 xdrawable.rectangle(xgc, x, y, width, height, true);
411 if (foreground != null)
412 xgc.set_foreground(foreground.getRGB());
415 public void drawRoundRect(int x, int y, int width, int height, int arcWidth,
416 int arcHeight)
418 // Draw 4 lines.
419 int arcRadiusX = arcWidth / 2;
420 int arcRadiusY = arcHeight / 2;
421 drawLine(x + arcRadiusX, y, x + width - arcRadiusX, y);
422 drawLine(x, y + arcRadiusY, x, y + height - arcRadiusY);
423 drawLine(x + arcRadiusX, y + height, x + width - arcRadiusX, y + height);
424 drawLine(x + width, y + arcRadiusY, x + width, y + height - arcRadiusY);
426 // Draw the 4 arcs at the corners.
427 // Upper left.
428 drawArc(x, y, arcWidth, arcHeight, 90, 90);
429 // Lower left.
430 drawArc(x, y + height - arcHeight, arcWidth, arcHeight, 180, 90);
431 // Upper right.
432 drawArc(x + width - arcWidth, y, arcWidth, arcHeight, 0, 90);
433 // Lower right.
434 drawArc(x + width - arcWidth, y + height - arcHeight, arcWidth, arcHeight,
435 270, 90);
438 public void fillRoundRect(int x, int y, int width, int height, int arcWidth,
439 int arcHeight)
441 // Fill the 3 rectangles that make up the inner area.
442 int arcRadiusX = arcWidth / 2;
443 int arcRadiusY = arcHeight / 2;
444 // Left.
445 fillRect(x, y + arcRadiusY, arcRadiusX, height - arcHeight);
446 // Middle.
447 fillRect(x + arcRadiusX, y, width - arcWidth, height);
448 // Right.
449 fillRect(x + width - arcRadiusX, y + arcRadiusY, arcRadiusX,
450 height - arcHeight);
452 // Fill the 4 arcs in the corners.
453 // Upper left.
454 fillArc(x, y, arcWidth, arcHeight, 90, 90);
455 // Lower left.
456 fillArc(x, y + height - arcHeight, arcWidth, arcHeight, 180, 90);
457 // Upper right.
458 fillArc(x + width - arcWidth, y, arcWidth, arcHeight, 0, 90);
459 // Lower right.
460 fillArc(x + width - arcWidth, y + height - arcHeight, arcWidth, arcHeight,
461 270, 90);
464 public void drawOval(int x, int y, int width, int height)
466 xdrawable.arc(xgc, x, y, width, height, 0, 360 * 64, false);
469 public void fillOval(int x, int y, int width, int height)
471 xdrawable.arc(xgc, x, y, width, height, 0, 360 * 64, true);
474 public void drawArc(int x, int y, int width, int height, int arcStart,
475 int arcAngle)
477 xdrawable.arc(xgc, x, y, width, height, arcStart * 64, arcAngle * 64, false);
480 public void fillArc(int x, int y, int width, int height, int arcStart,
481 int arcAngle)
483 xdrawable.arc(xgc, x, y, width, height, arcStart * 64, arcAngle * 64, true);
486 public void drawPolyline(int[] xPoints, int[] yPoints, int npoints)
488 int numPoints = Math.min(xPoints.length, yPoints.length);
489 Point[] points = new Point[numPoints];
490 // FIXME: Improve Escher API to accept arrays to avoid creation
491 // of many Point objects.
492 for (int i = 0; i < numPoints; i++)
493 points[i] = new Point(xPoints[i], yPoints[i]);
494 xdrawable.poly_line(xgc, points, Drawable.ORIGIN);
497 public void drawPolygon(int[] xPoints, int[] yPoints, int npoints)
499 int numPoints = Math.min(xPoints.length, yPoints.length);
500 Point[] points = new Point[numPoints];
501 // FIXME: Improve Escher API to accept arrays to avoid creation
502 // of many Point objects.
503 for (int i = 0; i < numPoints; i++)
504 points[i] = new Point(xPoints[i], yPoints[i]);
505 xdrawable.poly_line(xgc, points, Drawable.ORIGIN);
508 public void fillPolygon(int[] xPoints, int[] yPoints, int npoints)
510 int numPoints = Math.min(xPoints.length, yPoints.length);
511 Point[] points = new Point[numPoints];
512 // FIXME: Improve Escher API to accept arrays to avoid creation
513 // of many Point objects.
514 for (int i = 0; i < numPoints; i++)
515 points[i] = new Point(xPoints[i], yPoints[i]);
516 xdrawable.fill_poly(xgc, points, Drawable.COMPLEX, Drawable.ORIGIN);
520 * Draws the specified string at (x, y).
522 public void drawString(String string, int x, int y)
524 if (disposed)
525 throw new AWTError("XGraphics already disposed");
527 xdrawable.text(xgc, x + translateX, y + translateY, string);
530 public void drawString(AttributedCharacterIterator ci, int x, int y)
532 // FIXME: Implement this.
533 throw new UnsupportedOperationException("Not yet implemented");
537 * Draws the specified image on the drawable at position (x,y).
539 public boolean drawImage(Image image, int x, int y, ImageObserver observer)
541 if (image instanceof XImage)
543 XImage xim = (XImage) image;
544 Pixmap pm = xim.pixmap;
545 xdrawable.copy_area(pm, xgc, 0, 0, pm.width, pm.height,
546 x + translateX, y + translateY);
548 else if (image instanceof BufferedImage
549 && ((BufferedImage) image).getTransparency() != Transparency.OPAQUE)
551 BufferedImage bi = (BufferedImage) image;
552 int width = bi.getWidth();
553 int height = bi.getHeight();
554 Data img = xdrawable.image(x + translateX, y + translateY,
555 width, height, 0xFFFFFFFF, 2);
557 // Compute line byte count.
558 int lineBitCount = width * pixmapFormat.bits_per_pixel ();
559 int rem = lineBitCount % pixmapFormat.scanline_pad ();
560 int linePadCount = lineBitCount / pixmapFormat.scanline_pad ()
561 + (rem == 0 ? 0 : 1);
562 int lineByteCount = linePadCount * pixmapFormat.scanline_pad () / 8;
564 // Composite source and destination pixel data.
565 int[] trgb = new int[3]; // The device rgb pixels.
566 for (int yy = 0; yy < height; yy++)
568 for (int xx = 0; xx < width; xx++)
570 getRGB(xx, yy, img, trgb, lineByteCount);
571 int srgb = bi.getRGB(xx, yy);
572 float alpha = ((srgb >> 24) & 0xff) / 256F;
573 float tAlpha = 1.F - alpha;
574 int red = (srgb >> 16) & 0xFF;
575 int green = (srgb >> 8) & 0xFF;
576 int blue = (srgb) & 0xFF;
577 trgb[0] = (int) (trgb[0] * tAlpha + red * alpha);
578 trgb[1] = (int) (trgb[1] * tAlpha + green * alpha);
579 trgb[2] = (int) (trgb[2] * tAlpha + blue * alpha);
580 setRGB(xx, yy, img, trgb, lineByteCount);
584 // Now we have the transparent image composited onto the target
585 // Image, now we only must copy it to the Drawable.
586 ZPixmap pm = new ZPixmap(xdrawable.display);
587 pm.width = width;
588 pm.height = height;
589 pm.init();
590 System.arraycopy(img.data, 32, pm.data, 0, img.data.length - 32);
591 xdrawable.put_image(xgc, pm, x + translateX, y + translateY);
593 else
595 // Pre-render the image into an XImage.
596 ImageProducer source = image.getSource();
597 ImageConverter conv = new ImageConverter();
598 source.startProduction(conv);
599 XImage xim = conv.getXImage();
600 Pixmap pm = xim.pixmap;
601 xdrawable.copy_area(pm, xgc, 0, 0, pm.width, pm.height,
602 x + translateX, y + translateY);
604 return true;
608 * Helper method to work around limitation in the current Escher impl.
610 * @param x the x position
611 * @param y the y position
612 * @param img the image data
613 * @param rgb an 3-size array that holds the rgb values on method exit
615 private void getRGB(int x, int y, Data img, int[] rgb, int lineByteCount)
617 // TODO: Does this also work on non-RGB devices?
618 int i = y * lineByteCount + pixelByteCount * x;
619 if (imageByteOrder == gnu.x11.image.Image.LSB_FIRST)
620 {//if (i >= 5716-33) System.err.println("lbc: " + lineByteCount + ", " + pixelByteCount);
621 rgb[2] = img.data[32 + i];
622 rgb[1] = img.data[32 + i + 1];
623 rgb[0] = img.data[32 + i + 2];
625 else
626 { // MSB_FIRST
627 rgb[0] = img.data[32 + i];
628 rgb[1] = img.data[32 + i + 1];
629 rgb[2] = img.data[32 + i + 2];
635 * Helper method to work around limitation in the current Escher impl.
637 * @param x the x position
638 * @param y the y position
639 * @param img the image data
640 * @param rgb an 3-size array that holds the rgb values on method exit
642 private void setRGB(int x, int y, Data img, int[] rgb, int lineByteCount)
644 // TODO: Does this also work on non-RGB devices?
645 int i = y * lineByteCount + pixelByteCount * x;
646 if (imageByteOrder == gnu.x11.image.Image.LSB_FIRST)
648 img.data[32 + i] = (byte) rgb[2];
649 img.data[32 + i + 1] = (byte) rgb[1];
650 img.data[32 + i + 2] = (byte) rgb[0];
652 else
653 { // MSB_FIRST
654 img.data[32 + i] = (byte) rgb[0];
655 img.data[32 + i + 1] = (byte) rgb[1];
656 img.data[32 + i + 2] = (byte) rgb[2];
660 public boolean drawImage(Image image, int x, int y, int width, int height,
661 ImageObserver observer)
663 // FIXME: Implement this.
664 throw new UnsupportedOperationException("Not yet implemented");
667 public boolean drawImage(Image image, int x, int y, Color bgcolor,
668 ImageObserver observer)
670 // FIXME: Implement this.
671 throw new UnsupportedOperationException("Not yet implemented");
674 public boolean drawImage(Image image, int x, int y, int width, int height,
675 Color bgcolor, ImageObserver observer)
677 // FIXME: Implement this.
678 throw new UnsupportedOperationException("Not yet implemented");
681 public boolean drawImage(Image image, int dx1, int dy1, int dx2, int dy2,
682 int sx1, int sy1, int sx2, int sy2,
683 ImageObserver observer)
685 return drawImage(image, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, null,
686 observer);
689 public boolean drawImage(Image image, int dx1, int dy1, int dx2, int dy2,
690 int sx1, int sy1, int sx2, int sy2, Color bgcolor,
691 ImageObserver observer)
694 // FIXME: What to do with bgcolor?
696 // Scale the image.
697 int sw = image.getWidth(observer);
698 int sh = image.getHeight(observer);
699 double scaleX = Math.abs(dx2 - dx1) / (double) Math.abs(sx2 - sx1);
700 double scaleY = Math.abs(dy2 - dy1) / (double) Math.abs(sy2 - sy1);
701 Image scaled = image.getScaledInstance((int) (scaleX * sw),
702 (int) (scaleY * sh),
703 Image.SCALE_FAST);
705 // Scaled source coordinates.
706 int sx1s = (int) (scaleX * Math.min(sx1, sx2));
707 int sx2s = (int) (scaleX * Math.max(sx1, sx2));
709 // Temporarily clip to the target rectangle.
710 Rectangle old = clip;
711 clipRect(dx1, dy1, dx2 - dx1, dy2 - dy1);
713 // Draw scaled image.
714 boolean res = drawImage(scaled, dx1 - sx1s, dy1 - sx2s, observer);
716 // Reset clip.
717 setClip(old);
719 return res;
723 * Frees any resources associated with this object.
725 public void dispose()
727 if (! disposed)
729 xgc.free();
730 xdrawable.display.flush();
731 disposed = true;
735 // Additional helper methods.
738 * Creates and returns an exact copy of this XGraphics.
740 protected Object clone()
744 XGraphics copy = (XGraphics) super.clone();
745 copy.xgc = xgc.copy();
746 if (clip != null)
748 copy.clip = new Rectangle(clip);
749 copy.setXClip(clip.x, clip.y, clip.width, clip.height);
751 return copy;
753 catch (CloneNotSupportedException ex)
755 assert false;
757 return null;
761 * Computes the intersection between two rectangles and stores the result
762 * int the second rectangle.
764 * This method has been copied from {@link javax.swing.SwingUtilities}.
766 * @param x the x coordinate of the rectangle #1
767 * @param y the y coordinate of the rectangle #1
768 * @param w the width of the rectangle #1
769 * @param h the height of the rectangle #1
770 * @param rect the rectangle #2 and output rectangle
772 private static void computeIntersection(int x, int y, int w, int h,
773 Rectangle rect)
775 int x2 = (int) rect.x;
776 int y2 = (int) rect.y;
777 int w2 = (int) rect.width;
778 int h2 = (int) rect.height;
780 int dx = (x > x2) ? x : x2;
781 int dy = (y > y2) ? y : y2;
782 int dw = (x + w < x2 + w2) ? (x + w - dx) : (x2 + w2 - dx);
783 int dh = (y + h < y2 + h2) ? (y + h - dy) : (y2 + h2 - dy);
785 if (dw >= 0 && dh >= 0)
786 rect.setBounds(dx, dy, dw, dh);
787 else
788 rect.setBounds(0, 0, 0, 0);