/* * $Id$ * * Copyright (C) 2003-2015 JNode.org * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; If not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ package org.jnode.awt.util; import java.awt.Color; import java.awt.Font; import java.awt.Graphics; import java.awt.Image; import java.awt.Rectangle; import java.awt.Toolkit; import java.awt.image.AreaAveragingScaleFilter; import java.awt.image.BufferedImage; import java.awt.image.ColorModel; import java.awt.image.ComponentColorModel; import java.awt.image.CropImageFilter; import java.awt.image.DataBuffer; import java.awt.image.DirectColorModel; import java.awt.image.FilteredImageSource; import java.awt.image.ImageObserver; import java.awt.image.IndexColorModel; import java.awt.image.PixelGrabber; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.text.AttributedCharacterIterator; import org.jnode.awt.JNodeToolkit; import org.jnode.driver.video.Surface; import org.jnode.driver.video.util.AbstractSurface; import sun.awt.image.ToolkitImage; /** * @author Levente S\u00e1ntha */ public class BasicSurfaceGraphics extends BasicGraphics { private final AbstractSurface surface; private int mode = Surface.PAINT_MODE; protected BasicSurfaceGraphics(AbstractSurface surface) { this.surface = surface; } protected BasicSurfaceGraphics(BasicSurfaceGraphics g) { super(g); this.surface = g.surface; this.mode = g.mode; } /** * Clears the specified rectangle by filling it with the background * color of the current drawing surface. This operation does not * use the current paint mode. * <p/> * Beginning with Java 1.1, the background color * of offscreen images may be system dependent. Applications should * use <code>setColor</code> followed by <code>fillRect</code> to * ensure that an offscreen image is cleared to a specific color. * * @param x the <i>x</i> coordinate of the rectangle to clear. * @param y the <i>y</i> coordinate of the rectangle to clear. * @param width the width of the rectangle to clear. * @param height the height of the rectangle to clear. * @see java.awt.Graphics#fillRect(int, int, int, int) * @see java.awt.Graphics#drawRect * @see java.awt.Graphics#setColor(java.awt.Color) * @see java.awt.Graphics#setPaintMode * @see java.awt.Graphics#setXORMode(java.awt.Color) */ public void clearRect(int x, int y, int width, int height) { Rectangle r = new Rectangle(x, y, width, height); _transform(r); if (clip != null) r = clip.intersection(r); surface.fillRect(r.x, r.y, r.width, r.height, Color.BLACK.getRGB(), Surface.PAINT_MODE); surface.update(r.x, r.y, r.width, r.height); } /** * Copies an area of the component by a distance specified by * <code>dx</code> and <code>dy</code>. From the point specified * by <code>x</code> and <code>y</code>, this method * copies downwards and to the right. To copy an area of the * component to the left or upwards, specify a negative value for * <code>dx</code> or <code>dy</code>. * If a portion of the source rectangle lies outside the bounds * of the component, or is obscured by another window or component, * <code>copyArea</code> will be unable to copy the associated * pixels. The area that is omitted can be refreshed by calling * the component's <code>paint</code> method. * * @param x the <i>x</i> coordinate of the source rectangle. * @param y the <i>y</i> coordinate of the source rectangle. * @param width the width of the source rectangle. * @param height the height of the source rectangle. * @param dx the horizontal distance to copy the pixels. * @param dy the vertical distance to copy the pixels. */ public void copyArea(final int x, final int y, final int width, final int height, final int dx, final int dy) { //source area Rectangle sr = new Rectangle(x, y, width, height); _transform(sr); if (clip != null) sr = clip.intersection(sr); //destination area Rectangle dr = new Rectangle(x + dx, y + dy, width, height); _transform(dr); if (clip != null) dr = clip.intersection(dr); final int w = Math.min(sr.width, dr.width); final int h = Math.min(sr.height, dr.height); if (dr.x != sr.x + dx) sr.x = dr.x - dx; if (dr.y != sr.y + dy) sr.y = dr.y - dy; surface.copyArea(sr.x, sr.y, w, h, dx, dy); } /** * Draws the outline of a circular or elliptical arc * covering the specified rectangle. * <p/> * The resulting arc begins at <code>startAngle</code> and extends * for <code>arcAngle</code> degrees, using the current color. * Angles are interpreted such that 0 degrees * is at the 3 o'clock position. * A positive value indicates a counter-clockwise rotation * while a negative value indicates a clockwise rotation. * <p/> * The center of the arc is the center of the rectangle whose origin * is (<i>x</i>, <i>y</i>) and whose size is specified by the * <code>width</code> and <code>height</code> arguments. * <p/> * The resulting arc covers an area * <code>width + 1</code> pixels wide * by <code>height + 1</code> pixels tall. * <p/> * The angles are specified relative to the non-square extents of * the bounding rectangle such that 45 degrees always falls on the * line from the center of the ellipse to the upper right corner of * the bounding rectangle. As a result, if the bounding rectangle is * noticeably longer in one axis than the other, the angles to the * start and end of the arc segment will be skewed farther along the * longer axis of the bounds. * * @param x the <i>x</i> coordinate of the * upper-left corner of the arc to be drawn. * @param y the <i>y</i> coordinate of the * upper-left corner of the arc to be drawn. * @param width the width of the arc to be drawn. * @param height the height of the arc to be drawn. * @param startAngle the beginning angle. * @param arcAngle the angular extent of the arc, * relative to the start angle. * @see java.awt.Graphics#fillArc */ public void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle) { //todo implement it org.jnode.vm.Unsafe.debug("SimpleSurfaceGraphics.drawArc() not implemented\n"); } /** * Draws as much of the specified area of the specified image as is * currently available, scaling it on the fly to fit inside the * specified area of the destination drawable surface. Transparent pixels * do not affect whatever pixels are already there. * <p/> * This method returns immediately in all cases, even if the * image area to be drawn has not yet been scaled, dithered, and converted * for the current output device. * If the current output representation is not yet complete then * <code>drawImage</code> returns <code>false</code>. As more of * the image becomes available, the process that loads the image notifies * the specified image observer. * <p/> * This method always uses the unscaled version of the image * to render the scaled rectangle and performs the required * scaling on the fly. It does not use a cached, scaled version * of the image for this operation. Scaling of the image from source * to destination is performed such that the first coordinate * of the source rectangle is mapped to the first coordinate of * the destination rectangle, and the second source coordinate is * mapped to the second destination coordinate. The subimage is * scaled and flipped as needed to preserve those mappings. * * @param img the specified image to be drawn. This method does * nothing if <code>img</code> is null. * @param dx1 the <i>x</i> coordinate of the first corner of the * destination rectangle. * @param dy1 the <i>y</i> coordinate of the first corner of the * destination rectangle. * @param dx2 the <i>x</i> coordinate of the second corner of the * destination rectangle. * @param dy2 the <i>y</i> coordinate of the second corner of the * destination rectangle. * @param sx1 the <i>x</i> coordinate of the first corner of the * source rectangle. * @param sy1 the <i>y</i> coordinate of the first corner of the * source rectangle. * @param sx2 the <i>x</i> coordinate of the second corner of the * source rectangle. * @param sy2 the <i>y</i> coordinate of the second corner of the * source rectangle. * @param observer object to be notified as more of the image is * scaled and converted. * @return <code>false</code> if the image pixels are still changing; * <code>true</code> otherwise. * @see java.awt.Image * @see java.awt.image.ImageObserver * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int) * @since JDK1.1 */ public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer) { return img == null || drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, null, observer); } /** * Draws as much of the specified area of the specified image as is * currently available, scaling it on the fly to fit inside the * specified area of the destination drawable surface. * <p/> * Transparent pixels are drawn in the specified background color. * This operation is equivalent to filling a rectangle of the * width and height of the specified image with the given color and then * drawing the image on top of it, but possibly more efficient. * <p/> * This method returns immediately in all cases, even if the * image area to be drawn has not yet been scaled, dithered, and converted * for the current output device. * If the current output representation is not yet complete then * <code>drawImage</code> returns <code>false</code>. As more of * the image becomes available, the process that loads the image notifies * the specified image observer. * <p/> * This method always uses the unscaled version of the image * to render the scaled rectangle and performs the required * scaling on the fly. It does not use a cached, scaled version * of the image for this operation. Scaling of the image from source * to destination is performed such that the first coordinate * of the source rectangle is mapped to the first coordinate of * the destination rectangle, and the second source coordinate is * mapped to the second destination coordinate. The subimage is * scaled and flipped as needed to preserve those mappings. * * @param img the specified image to be drawn. This method does * nothing if <code>img</code> is null. * @param dx1 the <i>x</i> coordinate of the first corner of the * destination rectangle. * @param dy1 the <i>y</i> coordinate of the first corner of the * destination rectangle. * @param dx2 the <i>x</i> coordinate of the second corner of the * destination rectangle. * @param dy2 the <i>y</i> coordinate of the second corner of the * destination rectangle. * @param sx1 the <i>x</i> coordinate of the first corner of the * source rectangle. * @param sy1 the <i>y</i> coordinate of the first corner of the * source rectangle. * @param sx2 the <i>x</i> coordinate of the second corner of the * source rectangle. * @param sy2 the <i>y</i> coordinate of the second corner of the * source rectangle. * @param bgcolor the background color to paint under the * non-opaque portions of the image. * @param observer object to be notified as more of the image is * scaled and converted. * @return <code>false</code> if the image pixels are still changing; * <code>true</code> otherwise. * @see java.awt.Image * @see java.awt.image.ImageObserver * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int) * @since JDK1.1 */ public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, Color bgcolor, ImageObserver observer) { if (img == null) return true; if (dx1 == dx2 || dy1 == dy2 || sx1 == sx2 || sy1 == sy2) return true; final int widthImage; final int heightImage; final int xImage; final int yImage; if (sx2 > sx1) { widthImage = sx2 - sx1 + 1; xImage = sx1; } else { widthImage = sx1 - sx2 + 1; xImage = sx2; } if (sy2 > sy1) { heightImage = sy2 - sy1 + 1; yImage = sy1; } else { heightImage = sy1 - sy2 + 1; yImage = sy2; } final int widthDest; final int heightDest; final int xDest; final int yDest; if (dx2 > dx1) { widthDest = dx2 - dx1 + 1; xDest = dx1; } else { widthDest = dx1 - dx2 + 1; xDest = dx2; } if (dy2 > dy1) { heightDest = dy2 - dy1 + 1; yDest = dy1; } else { heightDest = dy1 - dy2 + 1; yDest = dy2; } final Image imageArea; if (img.getWidth(observer) == widthImage && img.getHeight(observer) == heightImage) { imageArea = img; } else { // Extract the image with a CropImageFilter imageArea = new ToolkitImage(new FilteredImageSource(img.getSource(), new CropImageFilter(xImage, yImage, widthImage, heightImage))); } if (widthImage == widthDest && heightImage == heightDest) { if (bgcolor == null) { return drawImage(imageArea, xDest, yDest, observer); } else { return drawImage(imageArea, xDest, yDest, bgcolor, observer); } } else { if (bgcolor == null) { return drawImage(imageArea, xDest, yDest, widthDest, heightDest, observer); } else { return drawImage(imageArea, xDest, yDest, widthDest, heightDest, bgcolor, observer); } } } /* public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, Color bgcolor, ImageObserver observer) { if (img == null) return true; if (dx1 == dx2 || dy1 == dy2) { return true; } if (sx1 == sx2 || sy1 == sy2) { return true; } final int widthImage; final int heightImage; final int xImage; final int yImage; if (sx2 > sx1) { widthImage = sx2 - sx1 + 1; xImage = sx1; } else { widthImage = sx1 - sx2 + 1; xImage = sx2; } if (sy2 > sy1) { heightImage = sy2 - sy1 + 1; yImage = sy1; } else { heightImage = sy1 - sy2 + 1; yImage = sy2; } final int widthDest; final int heightDest; final int xDest; final int yDest; if (dx2 > dx1) { widthDest = dx2 - dx1 + 1; xDest = dx1; } else { widthDest = dx1 - dx2 + 1; xDest = dx2; } if (dy2 > dy1) { heightDest = dy2 - dy1 + 1; yDest = dy1; } else { heightDest = dy1 - dy2 + 1; yDest = dy2; } // Extract the image with a CropImageFilter final Image imageArea = new ToolkitImage(new FilteredImageSource(img.getSource(), new CropImageFilter(xImage, yImage, widthImage, heightImage))); if (bgcolor == null) { return drawImage(imageArea, xDest, yDest, widthDest, heightDest, observer); } else { return drawImage(imageArea, xDest, yDest, widthDest, heightDest, bgcolor, observer); } } */ /** * Draws as much of the specified image as is currently available. * The image is drawn with its top-left corner at * (<i>x</i>, <i>y</i>) in this graphics context's coordinate * space. Transparent pixels are drawn in the specified * background color. * <p/> * This operation is equivalent to filling a rectangle of the * width and height of the specified image with the given color and then * drawing the image on top of it, but possibly more efficient. * <p/> * This method returns immediately in all cases, even if the * complete image has not yet been loaded, and it has not been dithered * and converted for the current output device. * <p/> * If the image has completely loaded and its pixels are * no longer being changed, then * <code>drawImage</code> returns <code>true</code>. * Otherwise, <code>drawImage</code> returns <code>false</code> * and as more of * the image becomes available * or it is time to draw another frame of animation, * the process that loads the image notifies * the specified image observer. * * @param img the specified image to be drawn. This method does * nothing if <code>img</code> is null. * @param x the <i>x</i> coordinate. * @param y the <i>y</i> coordinate. * @param bgcolor the background color to paint under the * non-opaque portions of the image. * @param observer object to be notified as more of * the image is converted. * @return <code>false</code> if the image pixels are still changing; * <code>true</code> otherwise. * @see java.awt.Image * @see java.awt.image.ImageObserver * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int) */ public boolean drawImage(Image img, int x, int y, Color bgcolor, ImageObserver observer) { if (img == null) return true; try { Raster rast = getCompatibleRaster(img); Rectangle r = new Rectangle(x, y, rast.getWidth(), rast.getHeight()); _transform(r); final int tx = r.x, ty = r.y; if (clip != null) r = clip.intersection(r); if (!r.isEmpty()) { surface.drawCompatibleRaster(rast, r.x - tx, r.y - ty, r.x, r.y, r.width, r.height, bgcolor); surface.update(r.x, r.y, r.width, r.height); } return true; } catch (InterruptedException ie) { return false; } } /** * Draws as much of the specified image as is currently available. * The image is drawn with its top-left corner at * (<i>x</i>, <i>y</i>) in this graphics context's coordinate * space. Transparent pixels in the image do not affect whatever * pixels are already there. * <p/> * This method returns immediately in all cases, even if the * complete image has not yet been loaded, and it has not been dithered * and converted for the current output device. * <p/> * If the image has completely loaded and its pixels are * no longer being changed, then * <code>drawImage</code> returns <code>true</code>. * Otherwise, <code>drawImage</code> returns <code>false</code> * and as more of * the image becomes available * or it is time to draw another frame of animation, * the process that loads the image notifies * the specified image observer. * * @param img the specified image to be drawn. This method does * nothing if <code>img</code> is null. * @param x the <i>x</i> coordinate. * @param y the <i>y</i> coordinate. * @param observer object to be notified as more of * the image is converted. * @return <code>false</code> if the image pixels are still changing; * <code>true</code> otherwise. * @see java.awt.Image * @see java.awt.image.ImageObserver * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int) */ public boolean drawImage(Image img, int x, int y, ImageObserver observer) { if (img == null) return true; try { Raster rast = getCompatibleRaster(img); Rectangle r = new Rectangle(x, y, rast.getWidth(), rast.getHeight()); _transform(r); final int tx = r.x, ty = r.y; if (clip != null) r = clip.intersection(r); if (!r.isEmpty()) { surface.drawCompatibleRaster(rast, r.x - tx, r.y - ty, r.x, r.y, r.width, r.height, null); surface.update(r.x, r.y, r.width, r.height); } return true; } catch (InterruptedException ie) { return false; } } /** * Draws as much of the specified image as has already been scaled * to fit inside the specified rectangle. * <p/> * The image is drawn inside the specified rectangle of this * graphics context's coordinate space, and is scaled if * necessary. Transparent pixels are drawn in the specified * background color. * This operation is equivalent to filling a rectangle of the * width and height of the specified image with the given color and then * drawing the image on top of it, but possibly more efficient. * <p/> * This method returns immediately in all cases, even if the * entire image has not yet been scaled, dithered, and converted * for the current output device. * If the current output representation is not yet complete then * <code>drawImage</code> returns <code>false</code>. As more of * the image becomes available, the process that loads the image notifies * the specified image observer. * <p/> * A scaled version of an image will not necessarily be * available immediately just because an unscaled version of the * image has been constructed for this output device. Each size of * the image may be cached separately and generated from the original * data in a separate image production sequence. * * @param img the specified image to be drawn. This method does * nothing if <code>img</code> is null. * @param x the <i>x</i> coordinate. * @param y the <i>y</i> coordinate. * @param width the width of the rectangle. * @param height the height of the rectangle. * @param bgcolor the background color to paint under the * non-opaque portions of the image. * @param observer object to be notified as more of * the image is converted. * @return <code>false</code> if the image pixels are still changing; * <code>true</code> otherwise. * @see java.awt.Image * @see java.awt.image.ImageObserver * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int) */ public boolean drawImage(Image img, int x, int y, int width, int height, Color bgcolor, ImageObserver observer) { if (img == null) return true; if (img.getWidth(observer) == width && img.getHeight(observer) == height) return drawImage(img, x, y, bgcolor, observer); return drawImage(new ToolkitImage(new FilteredImageSource(img.getSource(), new AreaAveragingScaleFilter(width, height))), x, y, bgcolor, observer); } /** * Draws as much of the specified image as has already been scaled * to fit inside the specified rectangle. * <p/> * The image is drawn inside the specified rectangle of this * graphics context's coordinate space, and is scaled if * necessary. Transparent pixels do not affect whatever pixels * are already there. * <p/> * This method returns immediately in all cases, even if the * entire image has not yet been scaled, dithered, and converted * for the current output device. * If the current output representation is not yet complete, then * <code>drawImage</code> returns <code>false</code>. As more of * the image becomes available, the process that loads the image notifies * the image observer by calling its <code>imageUpdate</code> method. * <p/> * A scaled version of an image will not necessarily be * available immediately just because an unscaled version of the * image has been constructed for this output device. Each size of * the image may be cached separately and generated from the original * data in a separate image production sequence. * * @param img the specified image to be drawn. This method does * nothing if <code>img</code> is null. * @param x the <i>x</i> coordinate. * @param y the <i>y</i> coordinate. * @param width the width of the rectangle. * @param height the height of the rectangle. * @param observer object to be notified as more of * the image is converted. * @return <code>false</code> if the image pixels are still changing; * <code>true</code> otherwise. * @see java.awt.Image * @see java.awt.image.ImageObserver * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int) */ public boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer) { if (img == null) return true; if (img.getWidth(observer) == width && img.getHeight(observer) == height) return drawImage(img, x, y, observer); return drawImage(new ToolkitImage(new FilteredImageSource(img.getSource(), new AreaAveragingScaleFilter(width, height))), x, y, observer); } /** * Draws a line, using the current color, between the points * <code>(x1, y1)</code> and <code>(x2, y2)</code> * in this graphics context's coordinate system. * * @param x1 the first point's <i>x</i> coordinate. * @param y1 the first point's <i>y</i> coordinate. * @param x2 the second point's <i>x</i> coordinate. * @param y2 the second point's <i>y</i> coordinate. */ public void drawLine(int x1, int y1, int x2, int y2) { x1 += origin.x; y1 += origin.y; x2 += origin.x; y2 += origin.y; if (clip == null || (clip.contains(x1, y1) && clip.contains(x2, y2))) { surface.drawLine(x1, y1, x2, y2, color.getRGB(), mode); } else { //todo optimize clipping //copute clipped line coordinates with the Liang-Barsjky alogorithm drawLineClipped(x1, x2, y1, y2); } if (x1 == x2) { if (y1 == y2) surface.update(x1, y1, 2, 2); else if (y1 < y2) surface.update(x1, y1, 2, y2 - y1 + 2); else surface.update(x1, y2, 2, y1 - y2 + 2); } else if (y1 == y2) { if (x1 < x2) surface.update(x1, y1, x2 - x1 + 2, 2); else surface.update(x2, y1, x1 - x2 + 2, 2); } else { if (x1 < x2 && y1 < y2) surface.update(x1, y1, x2 - x1 + 2, y2 - y1 + 2); else if (x1 < x2 && y1 > y2) surface.update(x1, y2, x2 - x1 + 2, y1 - y2 + 2); else if (x1 > x2 && y1 < y2) surface.update(x2, y1, x1 - x2 + 2, y2 - y1 + 2); else if (x1 > x2 && y1 > y2) surface.update(x2, y2, x1 - x2 + 2, y1 - y2 + 2); } } private void drawLineClipped(int x1, int x2, int y1, int y2) { final int incx, incy; int countx, county; final int sizex = Math.abs(x2 - x1); final int sizey = Math.abs(y2 - y1); if (x1 > x2) { incx = -1; } else { incx = 1; } if (y1 > y2) { incy = -1; } else { incy = 1; } countx = x1; county = y1; drawPixelClipped(x1, y1); if (sizex >= sizey) { int y = sizex >> 1; final int loops = sizex; for (int i = 0; i < loops; i++) { y += sizey; if (y >= sizex) { y -= sizex; county += incy; } countx += incx; drawPixelClipped(countx, county); } } else { int x = sizey >> 1; final int loops = sizey; for (int i = 0; i < loops; i++) { x += sizex; if (x >= sizey) { x -= sizey; countx += incx; } county += incy; drawPixelClipped(countx, county); } } } private void drawPixelClipped(int x, int y) { if (clip == null || clip.contains(x, y)) surface.drawPixel(x, y, color.getRGB(), mode); } /** * Draws the outline of an oval. * The result is a circle or ellipse that fits within the * rectangle specified by the <code>x</code>, <code>y</code>, * <code>width</code>, and <code>height</code> arguments. * <p/> * The oval covers an area that is * <code>width + 1</code> pixels wide * and <code>height + 1</code> pixels tall. * * @param x the <i>x</i> coordinate of the upper left * corner of the oval to be drawn. * @param y the <i>y</i> coordinate of the upper left * corner of the oval to be drawn. * @param width the width of the oval to be drawn. * @param height the height of the oval to be drawn. * @see java.awt.Graphics#fillOval */ public void drawOval(int x, int y, int width, int height) { //todo org.jnode.vm.Unsafe.debug("SimpleSurfaceGraphics.drawOval() Not implemented\n"); } /** * Draws a closed polygon defined by * arrays of <i>x</i> and <i>y</i> coordinates. * Each pair of (<i>x</i>, <i>y</i>) coordinates defines a point. * <p/> * This method draws the polygon defined by <code>nPoint</code> line * segments, where the first <code>nPoint - 1</code> * line segments are line segments from * <code>(xPoints[i - 1], yPoints[i - 1])</code> * to <code>(xPoints[i], yPoints[i])</code>, for * 1 ≤ <i>i</i> ≤ <code>nPoints</code>. * The figure is automatically closed by drawing a line connecting * the final point to the first point, if those points are different. * * @param xPoints a an array of <code>x</code> coordinates. * @param yPoints a an array of <code>y</code> coordinates. * @param nPoints a the total number of points. * @see java.awt.Graphics#fillPolygon * @see java.awt.Graphics#drawPolyline */ public void drawPolygon(int xPoints[], int yPoints[], int nPoints) { //todo org.jnode.vm.Unsafe.debug("SimpleSurfaceGraphics.drawPlygon() Not implemented\n"); } /** * Draws a sequence of connected lines defined by * arrays of <i>x</i> and <i>y</i> coordinates. * Each pair of (<i>x</i>, <i>y</i>) coordinates defines a point. * The figure is not closed if the first point * differs from the last point. * * @param xPoints an array of <i>x</i> points * @param yPoints an array of <i>y</i> points * @param nPoints the total number of points * @see java.awt.Graphics#drawPolygon(int[], int[], int) * @since JDK1.1 */ public void drawPolyline(int xPoints[], int yPoints[], int nPoints) { //todo org.jnode.vm.Unsafe.debug("SimpleSurfaceGraphics.drawPolyLine() Not implemented\n"); } /** * Draws an outlined round-cornered rectangle using this graphics * context's current color. The left and right edges of the rectangle * are at <code>x</code> and <code>x + width</code>, * respectively. The top and bottom edges of the rectangle are at * <code>y</code> and <code>y + height</code>. * * @param x the <i>x</i> coordinate of the rectangle to be drawn. * @param y the <i>y</i> coordinate of the rectangle to be drawn. * @param width the width of the rectangle to be drawn. * @param height the height of the rectangle to be drawn. * @param arcWidth the horizontal diameter of the arc * at the four corners. * @param arcHeight the vertical diameter of the arc * at the four corners. * @see java.awt.Graphics#fillRoundRect */ public void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) { //todo org.jnode.vm.Unsafe.debug("SimpleSurfaceGraphics.drawRoundRect() Not implemented\n"); } /** * Renders the text of the specified iterator applying its attributes * in accordance with the specification of the * {@link java.awt.font.TextAttribute TextAttribute} class. * <p/> * The baseline of the leftmost character is at position * (<i>x</i>, <i>y</i>) in this graphics context's coordinate system. * * @param iterator the iterator whose text is to be drawn * @param x the <i>x</i> coordinate. * @param y the <i>y</i> coordinate. * @throws NullPointerException if <code>iterator</code> is * <code>null</code>. * @see java.awt.Graphics#drawBytes * @see java.awt.Graphics#drawChars */ public void drawString(AttributedCharacterIterator iterator, int x, int y) { //todo org.jnode.vm.Unsafe.debug("SimpleSurfaceGraphics.drawString() Not implemented\n"); } /** * Draws the text given by the specified string, using this * graphics context's current font and color. The baseline of the * leftmost character is at position (<i>x</i>, <i>y</i>) in this * graphics context's coordinate system. * * @param str the string to be drawn. * @param x the <i>x</i> coordinate. * @param y the <i>y</i> coordinate. * @throws NullPointerException if <code>str</code> is <code>null</code>. * @see java.awt.Graphics#drawBytes * @see java.awt.Graphics#drawChars */ public void drawString(String str, final int x, final int y) { int xx = x + origin.x; int yy = y + origin.y; try { final Font font = getFont(); if (font != null) { JNodeToolkit tk = ((JNodeToolkit) Toolkit.getDefaultToolkit()); if (tk == null) { System.err.println("Toolkit is null"); return; } if (tk.getFontManager() == null) { System.err.println("FontManager is null"); return; } tk.getFontManager().drawText(surface, this.clip, null, str, font, xx, yy, getColor()); } } catch (Throwable t) { t.printStackTrace(); } } /** * Fills a circular or elliptical arc covering the specified rectangle. * <p/> * The resulting arc begins at <code>startAngle</code> and extends * for <code>arcAngle</code> degrees. * Angles are interpreted such that 0 degrees * is at the 3 o'clock position. * A positive value indicates a counter-clockwise rotation * while a negative value indicates a clockwise rotation. * <p/> * The center of the arc is the center of the rectangle whose origin * is (<i>x</i>, <i>y</i>) and whose size is specified by the * <code>width</code> and <code>height</code> arguments. * <p/> * The resulting arc covers an area * <code>width + 1</code> pixels wide * by <code>height + 1</code> pixels tall. * <p/> * The angles are specified relative to the non-square extents of * the bounding rectangle such that 45 degrees always falls on the * line from the center of the ellipse to the upper right corner of * the bounding rectangle. As a result, if the bounding rectangle is * noticeably longer in one axis than the other, the angles to the * start and end of the arc segment will be skewed farther along the * longer axis of the bounds. * * @param x the <i>x</i> coordinate of the * upper-left corner of the arc to be filled. * @param y the <i>y</i> coordinate of the * upper-left corner of the arc to be filled. * @param width the width of the arc to be filled. * @param height the height of the arc to be filled. * @param startAngle the beginning angle. * @param arcAngle the angular extent of the arc, * relative to the start angle. * @see java.awt.Graphics#drawArc */ public void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle) { //todo org.jnode.vm.Unsafe.debug("SimpleSurfaceGraphics.fillArc() Not implemented\n"); } /** * Fills an oval bounded by the specified rectangle with the * current color. * * @param x the <i>x</i> coordinate of the upper left corner * of the oval to be filled. * @param y the <i>y</i> coordinate of the upper left corner * of the oval to be filled. * @param width the width of the oval to be filled. * @param height the height of the oval to be filled. * @see java.awt.Graphics#drawOval */ public void fillOval(int x, int y, int width, int height) { //todo org.jnode.vm.Unsafe.debug("SimpleSurfaceGraphics.fillOval() Not implemented\n"); } /** * Fills a closed polygon defined by * arrays of <i>x</i> and <i>y</i> coordinates. * <p/> * This method draws the polygon defined by <code>nPoint</code> line * segments, where the first <code>nPoint - 1</code> * line segments are line segments from * <code>(xPoints[i - 1], yPoints[i - 1])</code> * to <code>(xPoints[i], yPoints[i])</code>, for * 1 ≤ <i>i</i> ≤ <code>nPoints</code>. * The figure is automatically closed by drawing a line connecting * the final point to the first point, if those points are different. * <p/> * The area inside the polygon is defined using an * even-odd fill rule, also known as the alternating rule. * * @param xPoints a an array of <code>x</code> coordinates. * @param yPoints a an array of <code>y</code> coordinates. * @param nPoints a the total number of points. * @see java.awt.Graphics#drawPolygon(int[], int[], int) */ public void fillPolygon(int xPoints[], int yPoints[], int nPoints) { //todo org.jnode.vm.Unsafe.debug("SimpleSurfaceGraphics.fillPolygon Not implemented\n"); } /** * Fills the specified rectangle. * The left and right edges of the rectangle are at * <code>x</code> and <code>x + width - 1</code>. * The top and bottom edges are at * <code>y</code> and <code>y + height - 1</code>. * The resulting rectangle covers an area * <code>width</code> pixels wide by * <code>height</code> pixels tall. * The rectangle is filled using the graphics context's current color. * * @param x the <i>x</i> coordinate * of the rectangle to be filled. * @param y the <i>y</i> coordinate * of the rectangle to be filled. * @param width the width of the rectangle to be filled. * @param height the height of the rectangle to be filled. * @see java.awt.Graphics#clearRect * @see java.awt.Graphics#drawRect */ public void fillRect(int x, int y, int width, int height) { Rectangle r = new Rectangle(x, y, width, height); _transform(r); if (clip != null) r = clip.intersection(r); try { surface.fillRect(r.x, r.y, r.width, r.height, color.getRGB(), Surface.PAINT_MODE); surface.update(r.x, r.y, r.width, r.height); } catch (Exception ex) { org.jnode.vm.Unsafe.debugStackTrace(ex); } } /** * Fills the specified rounded corner rectangle with the current color. * The left and right edges of the rectangle * are at <code>x</code> and <code>x + width - 1</code>, * respectively. The top and bottom edges of the rectangle are at * <code>y</code> and <code>y + height - 1</code>. * * @param x the <i>x</i> coordinate of the rectangle to be filled. * @param y the <i>y</i> coordinate of the rectangle to be filled. * @param width the width of the rectangle to be filled. * @param height the height of the rectangle to be filled. * @param arcWidth the horizontal diameter * of the arc at the four corners. * @param arcHeight the vertical diameter * of the arc at the four corners. * @see java.awt.Graphics#drawRoundRect */ public void fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) { //todo org.jnode.vm.Unsafe.debug("SimpleSurfaceGraphics.fillRoundRect() Not implemented\n"); } /** * Creates a new <code>Graphics</code> object that is * a copy of this <code>Graphics</code> object. * * @return a new graphics context that is a copy of * this graphics context. */ public Graphics create() { return new BasicSurfaceGraphics(this); } /** * Gets the Raster of a given image. * * @param image * @return Raster * @throws InterruptedException */ private Raster getCompatibleRaster(Image image) throws InterruptedException { final ColorModel dstModel = surface.getColorModel(); if (image instanceof BufferedImage) { final BufferedImage b_image = (BufferedImage) image; // We have a direct raster final Raster raster = b_image.getRaster(); if (dstModel.isCompatibleRaster(raster)) { // Raster is compatible, return without changes return raster; } else { // Convert it into a compatible raster return createCompatibleRaster(raster, b_image.getColorModel()); } } else if (image instanceof RenderedImage) { final RenderedImage r_image = (RenderedImage) image; // We have a direct raster final Raster raster = r_image.getData(); if (dstModel.isCompatibleRaster(raster)) { // Raster is compatible, return without changes return raster; } else { // Convert it into a compatible raster return createCompatibleRaster(raster, r_image.getColorModel()); } } else { // Convert it to a raster final PixelGrabber grabber = new PixelGrabber(image, 0, 0, image.getWidth(null), image.getHeight(null), true); if (grabber.grabPixels(10000)) { final int w = grabber.getWidth(); final int h = grabber.getHeight(); final WritableRaster raster = dstModel.createCompatibleWritableRaster(w, h); final int[] pixels = (int[]) grabber.getPixels(); Object dataElems = null; for (int y = 0; y < h; y++) { final int ofsY = y * w; for (int x = 0; x < w; x++) { final int rgb = pixels[ofsY + x]; dataElems = dstModel.getDataElements(rgb, dataElems); raster.setDataElements(x, y, dataElems); } } return raster; } else { throw new IllegalArgumentException("Cannot grab pixels"); } } } /** * Create a raster that is compatible with the surface and contains * data derived from the given raster. * * @param raster * @return the new raster */ private Raster createCompatibleRaster(Raster raster, ColorModel model) { //todo optimize final ColorModel dst_model = surface.getColorModel(); final int[] samples = new int[4]; final int w = raster.getWidth(); final int h = raster.getHeight(); final WritableRaster dst_raster = dst_model.createCompatibleWritableRaster(w, h); if (dst_model instanceof DirectColorModel) { if ((model instanceof DirectColorModel) || (model instanceof ComponentColorModel)) { for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { dst_raster.setPixel(x, y, raster.getPixel(x, y, samples)); } } } else if (model instanceof IndexColorModel) { final IndexColorModel icm = (IndexColorModel) model; for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { int sample = raster.getSample(x, y, 0); samples[0] = icm.getRed(sample); samples[1] = icm.getGreen(sample); samples[2] = icm.getBlue(sample); samples[3] = icm.getAlpha(sample); dst_raster.setPixel(x, y, samples); } } } else { // TODO implement special case of a custom ColorModel not based on jdk concrete classes return raster; } } else if (dst_model instanceof IndexColorModel) { // remark : the VGA driver is using an IndexColorModel final Object srcElements = createArray(model); final Object dstElements = createArray(dst_model); for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { // get and convert source pixel to rgb raster.getDataElements(x, y, srcElements); int rgb = model.getRGB(srcElements); // convert rgb to destination model dst_model.getDataElements(rgb, dstElements); // set destination pixel dst_raster.setDataElements(x, y, dstElements); } } } return dst_raster; } private Object createArray(ColorModel model) { final int transferType = model.getTransferType(); final int size = model.getNumComponents(); switch (transferType) { case DataBuffer.TYPE_BYTE: return new byte[size]; case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: return new short[size]; case DataBuffer.TYPE_INT: return new int[size]; case DataBuffer.TYPE_FLOAT: return new float[size]; case DataBuffer.TYPE_DOUBLE: return new double[size]; default: throw new IllegalArgumentException("Unknown transfer type : " + transferType); } } }