/******************************************************************************* * Copyright (c) 2001, 2010 Mathew A. Nelson and Robocode contributors * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://robocode.sourceforge.net/license/epl-v10.html * * Contributors: * Flemming N. Larsen, Pavel Savara * - Initial implementation *******************************************************************************/ package net.sf.robocode.robotpaint; import net.sf.robocode.io.Logger; import net.sf.robocode.serialization.RbSerializer; import java.awt.*; import java.awt.font.FontRenderContext; import java.awt.font.GlyphVector; import java.awt.geom.*; import java.awt.image.BufferedImage; import java.awt.image.BufferedImageOp; import java.awt.image.ImageObserver; import java.awt.image.RenderedImage; import java.awt.image.renderable.RenderableImage; import java.nio.BufferOverflowException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.text.AttributedCharacterIterator; import java.text.CharacterIterator; import java.util.Map; import javax.swing.UIManager; /** * @author Flemming N. Larsen (original) * @author Pavel Savara (original) */ public class Graphics2DSerialized extends Graphics2D implements IGraphicsProxy { private static final int INITIAL_BUFFER_SIZE = 2 * 1024; private static final int MAX_BUFFER_SIZE = 64 * 1024; private final Method[] methods = Method.class.getEnumConstants(); private enum Method { TRANSLATE_INT, // translate(int, int) SET_COLOR, // setColor(Color) SET_PAINT_MODE, // setPaintMode() SET_XOR_MODE, // setXORMode(Color) SET_FONT, // setFont(Font) CLIP_RECT, // clipRect(int, int, int, int) SET_CLIP, // setClip(int, int, int, int) SET_CLIP_SHAPE, // setClip(Shape) COPY_AREA, // copyArea(int, int, int, int, int, int) DRAW_LINE, // drawLine(int, int, int, int) FILL_RECT, // fillRect(int, int, int, int) DRAW_RECT, // drawRect(int, int, int, int) CLEAR_RECT, // clearRect(int, int, int, int) DRAW_ROUND_RECT, // drawRoundRect(int, int, int, int, int, int) FILL_ROUND_RECT, // fillRoundRect(int, int, int, int, int, int) DRAW_3D_RECT, // draw3DRect(int, int, int, int, boolean) FILL_3D_RECT, // draw3DRect(int, int, int, int, boolean) DRAW_OVAL, // drawOval(int, int, int, int) FILL_OVAL, // fillOval(int, int, int, int) DRAW_ARC, // drawArc(int, int, int, int, int, int) FILL_ARC, // fillArc(int, int, int, int, int, int) DRAW_POLYLINE, // drawPolyline(int[], int[], int) DRAW_POLYGON, // drawPolygon(int[], int[], int) FILL_POLYGON, // fillPolygon(int[], int[], int) DRAW_STRING_INT, // drawString(String, int, int) DRAW_STRING_ACI_INT, // drawString(AttributedCharacterIterator, int, int) DRAW_CHARS, // drawChars(char[], int, int, int, int) DRAW_BYTES, // drawBytes(byte[], int, int, int, int) DRAW_IMAGE_1, // drawImage(Image, int, int, ImageObserver) DRAW_IMAGE_2, // drawImage(Image, int, int, int, int, ImageObserver) DRAW_IMAGE_3, // drawImage(Image, int, int, Color, ImageObserver) DRAW_IMAGE_4, // drawImage(Image, int, int, int, int, Color, ImageObserver) DRAW_IMAGE_5, // drawImage(Image, int, int, int, int, int, int, int, int, ImageObserver) DRAW_IMAGE_6, // drawImage(Image, int, int, int, int, int, int, int, int, Color, ImageObserver) DRAW_SHAPE, // draw(Shape) DRAW_IMAGE_7, // drawImage(Image, AffineTransform, ImageObserver) DRAW_IMAGE_8, // drawImage(BufferedImage, BufferedImageOp, int, int) DRAW_RENDERED_IMAGE, // drawRenderedImage(RenderedImage, AffineTransform) DRAW_RENDERABLE_IMGAGE, // drawRenderableImage(RenderableImage, AffineTransform) DRAW_STRING_FLOAT, // drawString(String, float, float) DRAW_STRING_ACI_FLOAT, // drawString(AttributedCharacterIterator, float, float) DRAW_GLYPH_VECTOR, // drawGlyphVector(GlyphVector gv, float x, float y) FILL_SHAPE, // fill(Shape) SET_COMPOSITE, // setComposite(Composite) SET_PAINT, // setPaint(Paint) SET_STROKE, // setStroke(Stroke) SET_RENDERING_HINT, // setRenderingHint(Key, Object) SET_RENDERING_HINTS, // setRenderingHints(Map<?, ?>) ADD_RENDERING_HINTS, // addRenderingHints(Map<?, ?>) TRANSLATE_DOUBLE, // translate(double, double) ROTATE, // rotate(double) ROTATE_XY, // rotate(double, double, double) SCALE, // scale(double, double) SHEAR, // shear(double, double) TRANSFORM, // transform(AffineTransform) SET_TRANSFORM, // setTransform(AffineTransform Tx) SET_BACKGROUND, // setBackground(Color) CLIP, // clip(Shape) } // Needed for getTransform() private transient AffineTransform transform = new AffineTransform(); // Needed for getComposite() private transient Composite composite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER); // Needed for getPaint() private transient Paint paint = Color.BLACK; // Needed for getStroke() private transient Stroke stroke = new BasicStroke(); // Needed for getRenderingHint() and getRenderingHints() private transient RenderingHints renderingHints; // Needed for getBackground() private transient Color background = UIManager.getColor("Button.background"); // Needed for getClip() private transient Shape clip; // is null initially // Needed for getColor() private transient Color color = Color.BLACK; // Needed for getFont() private transient Font font = new Font("Dialog", Font.PLAIN, 11); // used for robot labels // Flag indicating if this proxy has been initialized private transient boolean isInitialized; // Flag indicating if painting is enabled private transient boolean isPaintingEnabled; // Flag indicating if Robocode is in debugging mode. If so, an unlimited buffer is allowed private final boolean isDebugging; // Byte buffer that works as a stack of method calls to this proxy private ByteBuffer calls; // Serializer for this proxy private final RbSerializer serializer = new RbSerializer(); // FOR-DEBUG private Method lastRead; // FOR-DEBUG private int lastPos; // The one and only constructor public Graphics2DSerialized() { isDebugging = System.getProperty("debug", "false").equals("true"); // Create a default RenderingHints object renderingHints = new RenderingHints(null); renderingHints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_DEFAULT); renderingHints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); renderingHints.put(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_OFF); renderingHints.put(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT); renderingHints.put(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_DEFAULT); } // -------------------------------------------------------------------------- // Overriding all methods from the extended Graphics class // -------------------------------------------------------------------------- // Methods that should not be overridden or implemented: // - finalize() // - toString() @Override public Graphics create() { Graphics2DSerialized gfxProxyCopy = new Graphics2DSerialized(); gfxProxyCopy.calls = ByteBuffer.allocate(INITIAL_BUFFER_SIZE); gfxProxyCopy.transform = transform; gfxProxyCopy.composite = copyOf(composite); gfxProxyCopy.paint = paint; gfxProxyCopy.stroke = copyOf(stroke); gfxProxyCopy.renderingHints = renderingHints; gfxProxyCopy.background = copyOf(background); gfxProxyCopy.clip = copyOf(clip); gfxProxyCopy.color = copyOf(color); gfxProxyCopy.font = font; gfxProxyCopy.isInitialized = isInitialized; calls.put(calls); return gfxProxyCopy; } @Override public Graphics create(int x, int y, int width, int height) { Graphics g = create(); g.translate(x, y); g.setClip(0, 0, width, height); return g; } @Override public void translate(int x, int y) { if (isPaintingEnabled) { calls.mark(); // Mark for rollback try { put(Method.TRANSLATE_INT); put(x); put(y); } catch (BufferOverflowException e) { if (recoverFromBufferOverflow()) { translate(x, y); // Retry this method after reallocation return; // Make sure we leave } } } // for getTransform() this.transform.translate(x, y); } @Override public Color getColor() { return color; } @Override public void setColor(Color c) { if (isPaintingEnabled) { calls.mark(); // Mark for rollback try { put(Method.SET_COLOR); put(c); } catch (BufferOverflowException e) { if (recoverFromBufferOverflow()) { setColor(c); // Retry this method after reallocation return; // Make sure we leave } } } // for getColor() this.color = c; } @Override public void setPaintMode() { if (isPaintingEnabled) { calls.mark(); // Mark for rollback try { put(Method.SET_PAINT_MODE); } catch (BufferOverflowException e) { if (recoverFromBufferOverflow()) { setPaintMode(); // Retry this method after reallocation return; // Make sure we leave } } } } @Override public void setXORMode(Color c1) { if (isPaintingEnabled) { calls.mark(); // Mark for rollback try { put(Method.SET_XOR_MODE); put(c1); } catch (BufferOverflowException e) { if (recoverFromBufferOverflow()) { setXORMode(c1); // Retry this method after reallocation return; // Make sure we leave } } } } @Override public Font getFont() { return font; } @Override public void setFont(Font font) { if (isPaintingEnabled) { calls.mark(); // Mark for rollback try { put(Method.SET_FONT); put(font); } catch (BufferOverflowException e) { if (recoverFromBufferOverflow()) { setFont(font); // Retry this method after reallocation return; // Make sure we leave } } } // for getFont() this.font = font; } @Override public FontMetrics getFontMetrics(Font f) { return new FontMetricsByFont(f, getFontRenderContext()); } @Override public Rectangle getClipBounds() { return clip.getBounds(); } @Override public void clipRect(int x, int y, int width, int height) { if (isPaintingEnabled) { calls.mark(); // Mark for rollback try { put(Method.CLIP_RECT); put(x); put(y); put(width); put(height); } catch (BufferOverflowException e) { if (recoverFromBufferOverflow()) { clipRect(x, y, width, height); // Retry this method after reallocation return; // Make sure we leave } } } // for getClip() Area clipArea = new Area(clip); Area clipRectArea = new Area(new Rectangle(x, y, width, height)); clipArea.intersect(clipRectArea); this.clip = clipArea; } @Override public void setClip(int x, int y, int width, int height) { if (isPaintingEnabled) { calls.mark(); // Mark for rollback try { put(Method.SET_CLIP); put(x); put(y); put(width); put(height); } catch (BufferOverflowException e) { if (recoverFromBufferOverflow()) { setClip(x, y, width, height); // Retry this method after reallocation return; // Make sure we leave } } } // for getClip() this.clip = new Rectangle(x, y, width, height); } @Override public Shape getClip() { return clip; } @Override public void setClip(Shape clip) { if (isPaintingEnabled) { calls.mark(); // Mark for rollback try { put(Method.SET_CLIP_SHAPE); put(clip); } catch (BufferOverflowException e) { if (recoverFromBufferOverflow()) { setClip(clip); // Retry this method after reallocation return; // Make sure we leave } } } // for getClip() this.clip = clip; } @Override public void copyArea(int x, int y, int width, int height, int dx, int dy) { if (isPaintingEnabled) { calls.mark(); // Mark for rollback try { put(Method.COPY_AREA); put(x); put(y); put(width); put(height); put(dx); put(dy); } catch (BufferOverflowException e) { if (recoverFromBufferOverflow()) { copyArea(x, y, width, height, dx, dy); // Retry this method after reallocation return; // Make sure we leave } } } } @Override public void drawLine(int x1, int y1, int x2, int y2) { if (isPaintingEnabled) { calls.mark(); // Mark for rollback try { put(Method.DRAW_LINE); put(x1); put(y1); put(x2); put(y2); } catch (BufferOverflowException e) { if (recoverFromBufferOverflow()) { drawLine(x1, y1, x2, y2); // Retry this method after reallocation return; // Make sure we leave } } } } @Override public void fillRect(int x, int y, int width, int height) { if (isPaintingEnabled) { calls.mark(); // Mark for rollback try { put(Method.FILL_RECT); put(x); put(y); put(width); put(height); } catch (BufferOverflowException e) { if (recoverFromBufferOverflow()) { fillRect(x, y, width, height); // Retry this method after reallocation return; // Make sure we leave } } } } @Override public void drawRect(int x, int y, int width, int height) { if (isPaintingEnabled) { calls.mark(); // Mark for rollback try { put(Method.DRAW_RECT); put(x); put(y); put(width); put(height); } catch (BufferOverflowException e) { if (recoverFromBufferOverflow()) { drawRect(x, y, width, height); // Retry this method after reallocation return; // Make sure we leave } } } } @Override public void clearRect(int x, int y, int width, int height) { if (isPaintingEnabled) { calls.mark(); // Mark for rollback try { put(Method.CLEAR_RECT); put(x); put(y); put(width); put(height); } catch (BufferOverflowException e) { if (recoverFromBufferOverflow()) { clearRect(x, y, width, height); // Retry this method after reallocation return; // Make sure we leave } } } } @Override public void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) { if (isPaintingEnabled) { calls.mark(); // Mark for rollback try { put(Method.DRAW_ROUND_RECT); put(x); put(y); put(width); put(height); put(arcWidth); put(arcHeight); } catch (BufferOverflowException e) { if (recoverFromBufferOverflow()) { drawRoundRect(x, y, width, height, arcWidth, arcHeight); // Retry this method after reallocation return; // Make sure we leave } } } } @Override public void fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) { if (isPaintingEnabled) { calls.mark(); // Mark for rollback try { put(Method.FILL_ROUND_RECT); put(x); put(y); put(width); put(height); put(arcWidth); put(arcHeight); } catch (BufferOverflowException e) { if (recoverFromBufferOverflow()) { fillRoundRect(x, y, width, height, arcWidth, arcHeight); // Retry this method after reallocation return; // Make sure we leave } } } } @Override public void draw3DRect(int x, int y, int width, int height, boolean raised) { if (isPaintingEnabled) { calls.mark(); // Mark for rollback try { put(Method.DRAW_3D_RECT); put(x); put(y); put(width); put(height); put(raised); } catch (BufferOverflowException e) { if (recoverFromBufferOverflow()) { draw3DRect(x, y, width, height, raised); // Retry this method after reallocation return; // Make sure we leave } } } } @Override public void fill3DRect(int x, int y, int width, int height, boolean raised) { if (isPaintingEnabled) { calls.mark(); // Mark for rollback try { put(Method.FILL_3D_RECT); put(x); put(y); put(width); put(height); put(raised); } catch (BufferOverflowException e) { if (recoverFromBufferOverflow()) { fill3DRect(x, y, width, height, raised); // Retry this method after reallocation return; // Make sure we leave } } } } @Override public void drawOval(int x, int y, int width, int height) { if (isPaintingEnabled) { calls.mark(); // Mark for rollback try { put(Method.DRAW_OVAL); put(x); put(y); put(width); put(height); } catch (BufferOverflowException e) { if (recoverFromBufferOverflow()) { drawOval(x, y, width, height); // Retry this method after reallocation return; // Make sure we leave } } } } @Override public void fillOval(int x, int y, int width, int height) { if (isPaintingEnabled) { calls.mark(); // Mark for rollback try { put(Method.FILL_OVAL); put(x); put(y); put(width); put(height); } catch (BufferOverflowException e) { if (recoverFromBufferOverflow()) { fillOval(x, y, width, height); // Retry this method after reallocation return; // Make sure we leave } } } } @Override public void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle) { if (isPaintingEnabled) { calls.mark(); // Mark for rollback try { put(Method.DRAW_ARC); put(x); put(y); put(width); put(height); put(startAngle); put(arcAngle); } catch (BufferOverflowException e) { if (recoverFromBufferOverflow()) { drawArc(x, y, width, height, startAngle, arcAngle); // Retry this method after reallocation return; // Make sure we leave } } } } @Override public void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle) { if (isPaintingEnabled) { calls.mark(); // Mark for rollback try { put(Method.FILL_ARC); put(x); put(y); put(width); put(height); put(startAngle); put(arcAngle); } catch (BufferOverflowException e) { if (recoverFromBufferOverflow()) { fillArc(x, y, width, height, startAngle, arcAngle); // Retry this method after reallocation return; // Make sure we leave } } } } @Override public void drawPolyline(int[] xPoints, int[] yPoints, int npoints) { if (isPaintingEnabled) { calls.mark(); // Mark for rollback try { put(Method.DRAW_POLYLINE); put(xPoints); put(yPoints); put(npoints); } catch (BufferOverflowException e) { if (recoverFromBufferOverflow()) { drawPolyline(xPoints, yPoints, npoints); // Retry this method after reallocation return; // Make sure we leave } } } } @Override public void drawPolygon(int[] xPoints, int[] yPoints, int npoints) { if (isPaintingEnabled) { calls.mark(); // Mark for rollback try { put(Method.DRAW_POLYGON); put(xPoints); put(yPoints); put(npoints); } catch (BufferOverflowException e) { if (recoverFromBufferOverflow()) { drawPolygon(xPoints, yPoints, npoints); // Retry this method after reallocation return; // Make sure we leave } } } } @Override public void drawPolygon(Polygon p) { if (isPaintingEnabled) { drawPolygon(p.xpoints, p.ypoints, p.npoints); // Reuse sister method } } @Override public void fillPolygon(int[] xPoints, int[] yPoints, int npoints) { if (isPaintingEnabled) { calls.mark(); // Mark for rollback try { put(Method.FILL_POLYGON); put(xPoints); put(yPoints); put(npoints); } catch (BufferOverflowException e) { if (recoverFromBufferOverflow()) { fillPolygon(xPoints, yPoints, npoints); // Retry this method after reallocation return; // Make sure we leave } } } } @Override public void fillPolygon(Polygon p) { if (isPaintingEnabled) { fillPolygon(p.xpoints, p.ypoints, p.npoints); // Reuse sister method } } @Override public void drawString(String str, int x, int y) { if (str == null) { throw new NullPointerException("str is null"); // According to the specification! } if (isPaintingEnabled) { calls.mark(); // Mark for rollback try { put(Method.DRAW_STRING_INT); put(str); put(x); put(y); } catch (BufferOverflowException e) { if (recoverFromBufferOverflow()) { drawString(str, x, y); // Retry this method after reallocation return; // Make sure we leave } } } } @Override public void drawString(AttributedCharacterIterator iterator, int x, int y) { if (isPaintingEnabled) { calls.mark(); // Mark for rollback try { put(Method.DRAW_STRING_ACI_INT); put(iterator); put(x); put(y); } catch (BufferOverflowException e) { if (recoverFromBufferOverflow()) { drawString(iterator, x, y); // Retry this method after reallocation return; // Make sure we leave } } } } @Override public void drawChars(char[] data, int offset, int length, int x, int y) { if (isPaintingEnabled) { calls.mark(); // Mark for rollback try { put(Method.DRAW_CHARS); put(data); put(offset); put(length); put(x); put(y); } catch (BufferOverflowException e) { if (recoverFromBufferOverflow()) { drawChars(data, offset, length, x, y); // Retry this method after reallocation return; // Make sure we leave } } } } @Override public void drawBytes(byte[] data, int offset, int length, int x, int y) { if (isPaintingEnabled) { calls.mark(); // Mark for rollback try { put(Method.DRAW_BYTES); put(data); put(offset); put(length); put(x); put(y); } catch (BufferOverflowException e) { if (recoverFromBufferOverflow()) { drawBytes(data, offset, length, x, y); // Retry this method after reallocation return; // Make sure we leave } } } } @Override public boolean drawImage(Image img, int x, int y, ImageObserver observer) { if (isPaintingEnabled) { notSupported(); } return false; // as if the image pixels are still changing (as the call is queued) } @Override public boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer) { if (isPaintingEnabled) { notSupported(); } return false; // as if the image pixels are still changing (as the call is queued) } @Override public boolean drawImage(Image img, int x, int y, Color bgcolor, ImageObserver observer) { if (isPaintingEnabled) { notSupported(); } return false; // as if the image pixels are still changing (as the call is queued) } @Override public boolean drawImage(Image img, int x, int y, int width, int height, Color bgcolor, ImageObserver observer) { if (isPaintingEnabled) { notSupported(); } return false; // as if the image pixels are still changing (as the call is queued) } @Override public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer) { if (isPaintingEnabled) { notSupported(); } return false; // as if the image pixels are still changing (as the call is queued) } @Override 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 (isPaintingEnabled) { notSupported(); } return false; // as if the image pixels are still changing (as the call is queued) } @Override public void dispose() {// Ignored here } @Override @Deprecated public Rectangle getClipRect() { return getClipBounds(); // Must use getClipBounds() instead of this deprecated method } @Override public boolean hitClip(int x, int y, int width, int height) { return (clip != null) && clip.intersects(x, y, width, height); } @Override public Rectangle getClipBounds(Rectangle r) { Rectangle bounds = clip.getBounds(); r.setBounds(bounds); return bounds; } // -------------------------------------------------------------------------- // Overriding all methods from the extended Graphics2D class // -------------------------------------------------------------------------- @Override public void draw(Shape s) { if (isPaintingEnabled) { calls.mark(); // Mark for rollback try { put(Method.DRAW_SHAPE); put(s); } catch (BufferOverflowException e) { if (recoverFromBufferOverflow()) { draw(s); // Retry this method after reallocation return; // Make sure we leave } } } } @Override public boolean drawImage(Image img, AffineTransform xform, ImageObserver obs) { if (isPaintingEnabled) { notSupported(); } return false; // as if the image is still being rendered (as the call is queued) } @Override public void drawImage(BufferedImage img, BufferedImageOp op, int x, int y) { if (isPaintingEnabled) { notSupported(); } } @Override public void drawRenderedImage(RenderedImage img, AffineTransform xform) {} @Override public void drawRenderableImage(RenderableImage img, AffineTransform xform) { if (isPaintingEnabled) { notSupported(); } } @Override public void drawString(String str, float x, float y) { if (str == null) { throw new NullPointerException("str is null"); // According to the specification! } if (isPaintingEnabled) { calls.mark(); // Mark for rollback try { put(Method.DRAW_STRING_FLOAT); put(str); put(x); put(y); } catch (BufferOverflowException e) { if (recoverFromBufferOverflow()) { drawString(str, x, y); // Retry this method after reallocation return; // Make sure we leave } } } } @Override public void drawString(AttributedCharacterIterator iterator, float x, float y) { if (isPaintingEnabled) { calls.mark(); // Mark for rollback try { put(Method.DRAW_STRING_ACI_FLOAT); put(iterator); put(x); put(y); } catch (BufferOverflowException e) { if (recoverFromBufferOverflow()) { drawString(iterator, x, y); // Retry this method after reallocation return; // Make sure we leave } } } } @Override public void drawGlyphVector(GlyphVector gv, float x, float y) { if (isPaintingEnabled) { notSupported(); } } @Override public void fill(Shape s) { if (isPaintingEnabled) { calls.mark(); // Mark for rollback try { put(Method.FILL_SHAPE); put(s); } catch (BufferOverflowException e) { if (recoverFromBufferOverflow()) { fill(s); // Retry this method after reallocation return; // Make sure we leave } } } } @Override public boolean hit(Rectangle rect, Shape s, boolean onStroke) { if (onStroke && getStroke() != null) { s = getStroke().createStrokedShape(s); } if (getTransform() != null) { s = getTransform().createTransformedShape(s); } Area area = new Area(s); if (getClip() != null) { area.intersect(new Area(getClip())); } return area.intersects(rect); } @Override public GraphicsConfiguration getDeviceConfiguration() { return GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration(); } @Override public void setComposite(Composite comp) { if (isPaintingEnabled) { calls.mark(); // Mark for rollback try { put(Method.SET_COMPOSITE); put(comp); } catch (BufferOverflowException e) { if (recoverFromBufferOverflow()) { setComposite(comp); // Retry this method after reallocation return; // Make sure we leave } } } // for getComposite() this.composite = comp; } @Override public void setPaint(Paint paint) { if (isPaintingEnabled) { calls.mark(); // Mark for rollback try { put(Method.SET_PAINT); put(paint); } catch (BufferOverflowException e) { if (recoverFromBufferOverflow()) { setPaint(paint); // Retry this method after reallocation return; // Make sure we leave } } } // for getPaint() this.paint = paint; } @Override public void setStroke(Stroke s) { if (isPaintingEnabled) { calls.mark(); // Mark for rollback try { put(Method.SET_STROKE); put(s); } catch (BufferOverflowException e) { if (recoverFromBufferOverflow()) { setStroke(s); // Retry this method after reallocation return; // Make sure we leave } } } // for getStroke() this.stroke = s; } @Override public void setRenderingHint(RenderingHints.Key hintKey, Object hintValue) { // for getRenderingHint() and getRenderingHints() this.renderingHints.put(hintKey, hintValue); if (isPaintingEnabled) { notSupportedWarn(); } } @Override public Object getRenderingHint(RenderingHints.Key hintKey) { return renderingHints.get(hintKey); } @Override public void setRenderingHints(Map<?, ?> hints) { // for getRenderingHint() and getRenderingHints() this.renderingHints.clear(); // Needs to clear first this.renderingHints.putAll(hints); // Only overrides existing keys if (isPaintingEnabled) { notSupportedWarn(); } } @Override public void addRenderingHints(Map<?, ?> hints) { // for getRenderingHint() and getRenderingHints() this.renderingHints.putAll(hints); if (isPaintingEnabled) { notSupportedWarn(); } } @Override public RenderingHints getRenderingHints() { return renderingHints; } @Override public void translate(double tx, double ty) { if (isPaintingEnabled) { calls.mark(); // Mark for rollback try { put(Method.TRANSLATE_DOUBLE); put(tx); put(ty); } catch (BufferOverflowException e) { if (recoverFromBufferOverflow()) { translate(tx, ty); // Retry this method after reallocation return; // Make sure we leave } } } // for getTransform() transform.translate(tx, ty); } @Override public void rotate(double theta) { if (isPaintingEnabled) { calls.mark(); // Mark for rollback try { put(Method.ROTATE); put(theta); } catch (BufferOverflowException e) { if (recoverFromBufferOverflow()) { rotate(theta); // Retry this method after reallocation return; // Make sure we leave } } } // for getTransform() transform.rotate(theta); } @Override public void rotate(double theta, double x, double y) { if (isPaintingEnabled) { calls.mark(); // Mark for rollback try { put(Method.ROTATE_XY); put(theta); put(x); put(y); } catch (BufferOverflowException e) { if (recoverFromBufferOverflow()) { rotate(theta, x, y); // Retry this method after reallocation return; // Make sure we leave } } } // for getTransform() transform.rotate(theta, x, y); } @Override public void scale(double sx, double sy) { if (isPaintingEnabled) { calls.mark(); // Mark for rollback try { put(Method.SCALE); put(sx); put(sy); } catch (BufferOverflowException e) { if (recoverFromBufferOverflow()) { scale(sx, sy); // Retry this method after reallocation return; // Make sure we leave } } } // for getTransform() transform.scale(sx, sy); } @Override public void shear(double shx, double shy) { if (isPaintingEnabled) { calls.mark(); // Mark for rollback try { put(Method.SHEAR); put(shx); put(shy); } catch (BufferOverflowException e) { if (recoverFromBufferOverflow()) { shear(shx, shy); // Retry this method after reallocation return; // Make sure we leave } } } // for getTransform() transform.shear(shx, shy); } @Override public void transform(AffineTransform Tx) { if (isPaintingEnabled) { calls.mark(); // Mark for rollback try { put(Method.TRANSFORM); put(Tx); } catch (BufferOverflowException e) { if (recoverFromBufferOverflow()) { transform(Tx); // Retry this method after reallocation return; // Make sure we leave } } } // for getTransform() transform.concatenate(Tx); } @Override public void setTransform(AffineTransform Tx) { if (isPaintingEnabled) { calls.mark(); // Mark for rollback try { put(Method.SET_TRANSFORM); put(Tx); } catch (BufferOverflowException e) { if (recoverFromBufferOverflow()) { setTransform(Tx); // Retry this method after reallocation return; // Make sure we leave } } } // for getTransform() this.transform = Tx; } @Override public AffineTransform getTransform() { return (AffineTransform) transform.clone(); } @Override public Paint getPaint() { return paint; } @Override public Composite getComposite() { return composite; } @Override public void setBackground(Color color) { if (isPaintingEnabled) { calls.mark(); // Mark for rollback try { put(Method.SET_BACKGROUND); put(color); } catch (BufferOverflowException e) { if (recoverFromBufferOverflow()) { setBackground(color); // Retry this method after reallocation return; // Make sure we leave } } } // for getBackground() background = color; } @Override public Color getBackground() { return background; } @Override public Stroke getStroke() { return stroke; } @Override public void clip(Shape s) { if (isPaintingEnabled) { calls.mark(); // Mark for rollback try { put(Method.CLIP); put(s); } catch (BufferOverflowException e) { if (recoverFromBufferOverflow()) { clip(s); // Retry this method after reallocation return; // Make sure we leave } } } // for getClip() if (s == null) { this.clip = null; } else { Area shapeArea = new Area(s); Area clipArea = new Area(clip); shapeArea.transform(transform); // transform by the current transform clipArea.intersect(shapeArea); // intersect current clip by the transformed shape this.clip = clipArea; } } @Override public FontRenderContext getFontRenderContext() { RenderingHints hints = getRenderingHints(); if (hints == null) { return new FontRenderContext(null, false, false); } else { boolean isAntiAliased = RenderingHints.VALUE_FRACTIONALMETRICS_ON.equals( hints.get(RenderingHints.KEY_TEXT_ANTIALIASING)); boolean usesFractionalMetrics = RenderingHints.VALUE_FRACTIONALMETRICS_ON.equals( hints.get(RenderingHints.KEY_FRACTIONALMETRICS)); return new FontRenderContext(null, isAntiAliased, usesFractionalMetrics); } } // -------------------------------------------------------------------------- // Processing of queued method calls to a Graphics2D object // -------------------------------------------------------------------------- public void setPaintingEnabled(boolean enabled) { if (enabled && !isPaintingEnabled) { calls = ByteBuffer.allocate(INITIAL_BUFFER_SIZE); calls.put(calls.order() == ByteOrder.BIG_ENDIAN ? (byte) 1 : (byte) 0); } isPaintingEnabled = enabled; } public void processTo(Graphics2D g) { if (!isInitialized) { // Make sure the transform is not null transform = g.getTransform(); transform = transform == null ? new AffineTransform() : new AffineTransform(transform); color = copyOf(g.getColor()); font = g.getFont(); clip = copyOf(g.getClip()); composite = copyOf(g.getComposite()); paint = g.getPaint(); stroke = copyOf(g.getStroke()); renderingHints = (RenderingHints) g.getRenderingHints().clone(); background = copyOf(g.getBackground()); isInitialized = true; } calls.flip(); while (calls.remaining() > 0) { processQueuedCall(g); } } public void clearQueue() { calls.clear(); calls.put(calls.order() == ByteOrder.BIG_ENDIAN ? (byte) 1 : (byte) 0); } public void processTo(Graphics2D g, Object graphicsCalls) { calls.clear(); calls.mark(); // Mark for rollback try { calls.put((byte[]) graphicsCalls); } catch (BufferOverflowException e) { calls.reset(); // Rollback buffer if (reallocBuffer()) { processTo(g, graphicsCalls); return; // must exit here } calls.clear(); } calls.flip(); if (calls.get() == 1) { calls.order(ByteOrder.BIG_ENDIAN); } else { calls.order(ByteOrder.LITTLE_ENDIAN); } while (calls.remaining() > 0) { try { processQueuedCall(g); } catch (Exception e) { e.printStackTrace(); // FOR-DEBUG } catch (Error e) { // FOR-DEBUG calls.position(lastPos - 4); } } } public byte[] readoutQueuedCalls() { if (calls == null || calls.position() == 0) { return null; } byte[] res = new byte[calls.position()]; calls.flip(); calls.get(res); calls.clear(); calls.put(calls.order() == ByteOrder.BIG_ENDIAN ? (byte) 1 : (byte) 0); return res; } private void processQueuedCall(Graphics2D g) { Method m = readMethod(); switch (m) { case TRANSLATE_INT: processTranslate_int(g); break; case SET_COLOR: processSetColor(g); break; case SET_PAINT_MODE: processSetPaintMode(g); break; case SET_XOR_MODE: processSetXORMode(g); break; case SET_FONT: processSetFont(g); break; case CLIP_RECT: processClipRect(g); break; case SET_CLIP: processSetClip(g); break; case SET_CLIP_SHAPE: processSetClip_Shape(g); break; case COPY_AREA: processCopyArea(g); break; case DRAW_LINE: processDrawLine(g); break; case FILL_RECT: processFillRect(g); break; case DRAW_RECT: processDrawRect(g); break; case CLEAR_RECT: processClearRect(g); break; case DRAW_ROUND_RECT: processDrawRoundRect(g); break; case FILL_ROUND_RECT: processFillRoundRect(g); break; case DRAW_3D_RECT: processDraw3DRect(g); break; case FILL_3D_RECT: processFill3DRect(g); break; case DRAW_OVAL: processDrawOval(g); break; case FILL_OVAL: processFillOval(g); break; case DRAW_ARC: processDrawArc(g); break; case FILL_ARC: processFillArc(g); break; case DRAW_POLYLINE: processDrawPolyline(g); break; case DRAW_POLYGON: processDrawPolygon(g); break; case FILL_POLYGON: processFillPolygon(g); break; case DRAW_STRING_INT: processDrawString_int(g); break; case DRAW_STRING_ACI_INT: processDrawString_ACIterator_int(g); break; case DRAW_CHARS: processDrawChars(g); break; case DRAW_BYTES: processDrawBytes(g); break; case DRAW_SHAPE: processDrawShape(g); break; case DRAW_STRING_FLOAT: processDrawString_float(g); break; case DRAW_STRING_ACI_FLOAT: processDrawString_ACIterator_float(g); break; case FILL_SHAPE: processFillShape(g); break; case SET_COMPOSITE: processSetComposite(g); break; case SET_PAINT: processSetPaint(g); break; case SET_STROKE: processSetStroke(g); break; case TRANSLATE_DOUBLE: processTranslate_double(g); break; case ROTATE: processRotate(g); break; case ROTATE_XY: processRotate_xy(g); break; case SCALE: processScale(g); break; case SHEAR: processShear(g); break; case TRANSFORM: processTransform(g); break; case SET_TRANSFORM: processSetTransform(g); break; case SET_BACKGROUND: processSetBackground(g); break; case CLIP: processClip(g); break; case DRAW_GLYPH_VECTOR: case DRAW_IMAGE_1: case DRAW_IMAGE_2: case DRAW_IMAGE_3: case DRAW_IMAGE_4: case DRAW_IMAGE_5: case DRAW_IMAGE_6: case DRAW_IMAGE_7: case DRAW_IMAGE_8: case DRAW_RENDERED_IMAGE: case DRAW_RENDERABLE_IMGAGE: case SET_RENDERING_HINT: case SET_RENDERING_HINTS: case ADD_RENDERING_HINTS: default: notSupported(); break; } } private void processTranslate_int(Graphics2D g) { // translate(int, int) g.translate(calls.getInt(), calls.getInt()); } private void processSetColor(Graphics2D g) { // setColor(Color) g.setColor(new Color(calls.getInt(), true)); } private void processSetPaintMode(Graphics2D g) { // setPaintMode() g.setPaintMode(); } private void processSetXORMode(Graphics2D g) { // setXORMode(Color) g.setXORMode(new Color(calls.getInt(), true)); } private void processSetFont(Graphics2D g) { // setFont(Font) g.setFont(new Font(serializer.deserializeString(calls), calls.getInt(), calls.getInt())); } private void processClipRect(Graphics2D g) { // clipRect(int, int, int, int) g.clipRect(calls.getInt(), calls.getInt(), calls.getInt(), calls.getInt()); } private void processSetClip(Graphics2D g) { // setClip(int, int, int, int) g.setClip(calls.getInt(), calls.getInt(), calls.getInt(), calls.getInt()); } private void processSetClip_Shape(Graphics2D g) { // setClip(Shape) g.setClip(readShape()); } private void processCopyArea(Graphics2D g) { // copyArea(int, int, int, int, int, int) g.copyArea(calls.getInt(), calls.getInt(), calls.getInt(), calls.getInt(), calls.getInt(), calls.getInt()); } private void processDrawLine(Graphics2D g) { // drawLine(int, int, int, int) g.drawLine(calls.getInt(), calls.getInt(), calls.getInt(), calls.getInt()); } private void processFillRect(Graphics2D g) { // fillRect(int, int, int, int) g.fillRect(calls.getInt(), calls.getInt(), calls.getInt(), calls.getInt()); } private void processDrawRect(Graphics2D g) { // drawRect(int, int, int, int) g.drawRect(calls.getInt(), calls.getInt(), calls.getInt(), calls.getInt()); } private void processClearRect(Graphics2D g) { // clearRect(int, int, int, int) g.clearRect(calls.getInt(), calls.getInt(), calls.getInt(), calls.getInt()); } private void processDrawRoundRect(Graphics2D g) { // drawRoundRect(int, int, int, int, int, int) g.drawRoundRect(calls.getInt(), calls.getInt(), calls.getInt(), calls.getInt(), calls.getInt(), calls.getInt()); } private void processFillRoundRect(Graphics2D g) { // fillRoundRect(int, int, int, int, int, int) g.fillRoundRect(calls.getInt(), calls.getInt(), calls.getInt(), calls.getInt(), calls.getInt(), calls.getInt()); } private void processDraw3DRect(Graphics2D g) { // draw3DRect(int, int, int, int, boolean) g.draw3DRect(calls.getInt(), calls.getInt(), calls.getInt(), calls.getInt(), serializer.deserializeBoolean(calls)); } private void processFill3DRect(Graphics2D g) { // fill3DRect(int, int, int, int, boolean) g.fill3DRect(calls.getInt(), calls.getInt(), calls.getInt(), calls.getInt(), serializer.deserializeBoolean(calls)); } private void processDrawOval(Graphics2D g) { // drawOval(int, int, int, int) g.drawOval(calls.getInt(), calls.getInt(), calls.getInt(), calls.getInt()); } private void processFillOval(Graphics2D g) { // fillOval(int, int, int, int) g.fillOval(calls.getInt(), calls.getInt(), calls.getInt(), calls.getInt()); } private void processDrawArc(Graphics2D g) { // drawArc(int, int, int, int, int, int) g.drawArc(calls.getInt(), calls.getInt(), calls.getInt(), calls.getInt(), calls.getInt(), calls.getInt()); } private void processFillArc(Graphics2D g) { // fillArc(int, int, int, int, int, int) g.fillArc(calls.getInt(), calls.getInt(), calls.getInt(), calls.getInt(), calls.getInt(), calls.getInt()); } private void processDrawPolyline(Graphics2D g) { // drawPolyline(int[], int[], int) g.drawPolyline(serializer.deserializeIntegers(calls), serializer.deserializeIntegers(calls), calls.getInt()); } private void processDrawPolygon(Graphics2D g) { // drawPolygon(int[], int[], int) g.drawPolygon(serializer.deserializeIntegers(calls), serializer.deserializeIntegers(calls), calls.getInt()); } private void processFillPolygon(Graphics2D g) { // fillPolygon(int[], int[], int) g.fillPolygon(serializer.deserializeIntegers(calls), serializer.deserializeIntegers(calls), calls.getInt()); } private void processDrawString_int(Graphics2D g) { // drawString(String, int, int) g.drawString(serializer.deserializeString(calls), calls.getInt(), calls.getInt()); } private void processDrawString_ACIterator_int(Graphics2D g) { // drawString(String, int, int) g.drawString(serializer.deserializeString(calls), calls.getInt(), calls.getInt()); } private void processDrawChars(Graphics2D g) { // drawBytes(char[], int, int, int, int) g.drawChars(serializer.deserializeChars(calls), calls.getInt(), calls.getInt(), calls.getInt(), calls.getInt()); } private void processDrawBytes(Graphics2D g) { // drawBytes(byte[], int, int, int, int) g.drawBytes(serializer.deserializeBytes(calls), calls.getInt(), calls.getInt(), calls.getInt(), calls.getInt()); } private void processDrawShape(Graphics2D g) { // draw(Shape) g.draw(readShape()); } private void processDrawString_float(Graphics2D g) { // drawString(String, float, float) g.drawString(serializer.deserializeString(calls), calls.getFloat(), calls.getFloat()); } private void processDrawString_ACIterator_float(Graphics2D g) { // drawString(String, float, float) g.drawString(serializer.deserializeString(calls), calls.getFloat(), calls.getFloat()); } private void processFillShape(Graphics2D g) { // fill(Shape) g.fill(readShape()); } private void processSetComposite(Graphics2D g) { // setComposite(Composite) g.setComposite(readComposite()); } private void processSetPaint(Graphics2D g) { // setPaint(Paint) g.setPaint(readPaint()); } private void processSetStroke(Graphics2D g) { // setStroke(Stroke) g.setStroke(readStroke()); } private void processTranslate_double(Graphics2D g) { // translate(double, double) g.translate(calls.getDouble(), calls.getDouble()); } private void processRotate(Graphics2D g) { // rotate(double) g.rotate(calls.getDouble()); } private void processRotate_xy(Graphics2D g) { // rotate(double) g.rotate(calls.getDouble(), calls.getDouble(), calls.getDouble()); } private void processScale(Graphics2D g) { // scale(double, double) g.scale(calls.getDouble(), calls.getDouble()); } private void processShear(Graphics2D g) { // shear(double, double) g.shear(calls.getDouble(), calls.getDouble()); } private void processTransform(Graphics2D g) { // transform(AffineTransform) final AffineTransform transform = getAffineTransform(); g.transform(transform); } private void processSetTransform(Graphics2D g) { // setTransform(AffineTransform) g.setTransform(getAffineTransform()); } private void processSetBackground(Graphics2D g) { // setBackground(Color) g.setBackground(new Color(calls.getInt(), true)); } private void processClip(Graphics2D g) { // clip(Shape) g.clip(readShape()); } private Shape readShape() { switch (calls.get()) { case 1: return new Arc2D.Double(calls.getDouble(), // x calls.getDouble(), // y calls.getDouble(), // width calls.getDouble(), // height calls.getDouble(), // angle start calls.getDouble(), // angle extended calls.getInt()); // arc type case 2: return new Line2D.Double(calls.getDouble(), // x1 calls.getDouble(), // y1 calls.getDouble(), // x2 calls.getDouble()); // y2 case 3: return new Rectangle2D.Double(calls.getDouble(), // x calls.getDouble(), // y calls.getDouble(), // width calls.getDouble()); // height case 4: return new Ellipse2D.Double(calls.getDouble(), // x calls.getDouble(), // y calls.getDouble(), // width calls.getDouble()); // height case 0: DeserializePathIterator pai = new DeserializePathIterator(); GeneralPath path = new GeneralPath(); path.append(pai, false); return path; default: break; } notSupported(); return null; } /** * Reallocates the calls buffer by replacing it with a new one with doubled capacity * and copying the old one. * * @return {@code true} if the buffer was reallocated; * {@code false} if the max. capacity has been reached meaning that the reallocation * was not performed. */ private boolean reallocBuffer() { int bufferSize; if (calls == null) { // No buffer -> Use initial buffer size bufferSize = INITIAL_BUFFER_SIZE; } else { // Otherwise, double up capacity bufferSize = 2 * calls.capacity(); } // Check if the max. buffer size has been reached if (!isDebugging && bufferSize > MAX_BUFFER_SIZE) { return false; // not reallocated! } // Allocate new buffer ByteBuffer newBuffer = ByteBuffer.allocate(bufferSize); if (calls != null) { // Copy all bytes contained in the current buffer to the new buffer byte[] copiedBytes = new byte[calls.position()]; calls.clear(); calls.get(copiedBytes); newBuffer.put(copiedBytes); } // Switch to the new buffer calls = newBuffer; return true; // buffer was reallocated } private int unrecoveredBufferOverflowCount; private boolean recoverFromBufferOverflow() { calls.reset(); // Rollback buffer boolean recovered = reallocBuffer(); if (!recovered) { if (unrecoveredBufferOverflowCount++ == 1) { // Prevent spamming System.out.println( "SYSTEM: This robot is painting too much between actions.\n" + "SYSTEM: Max. buffer capacity (" + MAX_BUFFER_SIZE + " bytes per turn) has been reached.\n" + "SYSTEM: Last painting operations are being dropped.\n"); } } return recovered; } private class DeserializePathIterator implements PathIterator { final int count; int pos; final int windingRule; int[] type; double[][] coords; public DeserializePathIterator() { count = calls.getInt(); pos = 0; windingRule = calls.getInt(); if (count > 0) { type = new int[count]; coords = new double[count][]; for (int i = 0; i < count; i++) { type[i] = calls.getInt(); coords[i] = serializer.deserializeDoubles(calls); } } } public int getWindingRule() { return windingRule; } public boolean isDone() { return pos == count; } public void next() { pos++; } public int currentSegment(float[] coords) { for (int i = 0; i < coords.length; i++) { coords[i] = (float) this.coords[pos][i]; } return type[pos]; } public int currentSegment(double[] coords) { System.arraycopy(this.coords[pos], 0, coords, 0, coords.length); return type[pos]; } } private void put(Shape shape) { if (shape instanceof Arc2D) { put((byte) 1); Arc2D arc = (Arc2D) shape; put(arc.getX()); put(arc.getY()); put(arc.getWidth()); put(arc.getHeight()); put(arc.getAngleStart()); put(arc.getAngleExtent()); put(arc.getArcType()); } else if (shape instanceof Line2D) { put((byte) 2); Line2D line = (Line2D) shape; put(line.getX1()); put(line.getY1()); put(line.getX2()); put(line.getY2()); } else if (shape instanceof Rectangle2D) { put((byte) 3); Rectangle2D rect = (Rectangle2D) shape; put(rect.getX()); put(rect.getY()); put(rect.getWidth()); put(rect.getHeight()); } else if (shape instanceof Ellipse2D) { put((byte) 4); Ellipse2D elipse = (Ellipse2D) shape; put(elipse.getX()); put(elipse.getY()); put(elipse.getWidth()); put(elipse.getHeight()); } else { put((byte) 0); double coords[] = new double[6]; int count = 0; // count them first PathIterator pi = shape.getPathIterator(null); while (!pi.isDone()) { count++; pi.next(); } put(count); // write them pi = shape.getPathIterator(null); put(pi.getWindingRule()); while (!pi.isDone()) { int type = pi.currentSegment(coords); put(type); put(coords); pi.next(); } } } private Composite readComposite() { switch (calls.get()) { case 1: return AlphaComposite.getInstance(calls.getInt()); default: break; } notSupported(); return null; } private void put(Composite comp) { if (comp instanceof AlphaComposite) { AlphaComposite composite = (AlphaComposite) comp; put((byte) 1); put(composite.getRule()); } else { notSupported(); } } private Paint readPaint() { switch (calls.get()) { case 1: return new Color(calls.getInt(), true); default: break; } notSupported(); return null; } private void put(Paint paint) { if (paint instanceof Color) { Color color = (Color) paint; put((byte) 1); put(color.getRGB()); } else { notSupported(); } } private Stroke readStroke() { switch (calls.get()) { case 1: return new BasicStroke(calls.getFloat(), calls.getInt(), calls.getInt(), calls.getFloat(), serializer.deserializeFloats(calls), calls.getFloat()); default: break; } notSupported(); return null; } private void put(Stroke stroke) { if (stroke instanceof BasicStroke) { BasicStroke bs = (BasicStroke) stroke; put((byte) 1); put(bs.getLineWidth()); put(bs.getEndCap()); put(bs.getLineJoin()); put(bs.getMiterLimit()); put(bs.getDashArray()); put(bs.getDashPhase()); } else { notSupported(); } } private AffineTransform getAffineTransform() { return new AffineTransform(serializer.deserializeDoubles(calls)); } private void put(AffineTransform tx) { double[] m = new double[6]; tx.getMatrix(m); put(m); put(tx.getType()); } private Method readMethod() { // FOR-DEBUG if (calls.getInt() != 0xBADF00D) { // FOR-DEBUG calls.position(lastPos); // FOR-DEBUG // throw new Error(); // FOR-DEBUG } // FOR-DEBUG lastPos = calls.position(); Method m = methods[calls.get()]; // FOR-DEBUG if (calls.getInt() != 0xBADF00D) { // FOR-DEBUG throw new Error(); // FOR-DEBUG } // FOR-DEBUG lastRead = m; return m; } private void put(Method m) { // FOR-DEBUG calls.putInt(0xBADF00D); calls.put((byte) m.ordinal()); // FOR-DEBUG calls.putInt(0xBADF00D); } private void put(AttributedCharacterIterator iterator) { StringBuilder sb = new StringBuilder(); for (char c = iterator.first(); c != CharacterIterator.DONE; c = iterator.next()) { sb.append(c); } put(sb.toString()); } private void put(String value) { serializer.serialize(calls, value); } private void put(boolean value) { serializer.serialize(calls, value); } private void put(byte value) { calls.put(value); } private void put(int value) { calls.putInt(value); } private void put(int[] values) { serializer.serialize(calls, values); } private void put(byte[] values) { serializer.serialize(calls, values); } private void put(char[] values) { serializer.serialize(calls, values); } private void put(double[] values) { serializer.serialize(calls, values); } private void put(float[] values) { serializer.serialize(calls, values); } private void put(double value) { calls.putDouble(value); } private void put(float value) { calls.putFloat(value); } private void put(Color value) { calls.putInt(value.getRGB()); } private void put(Font font) { serializer.serialize(calls, font.getFontName()); calls.putInt(font.getStyle()); calls.putInt(font.getSize()); } // -------------------------------------------------------------------------- // Copy // -------------------------------------------------------------------------- public static Color copyOf(Color c) { return (c != null) ? new Color(c.getRGB(), true) : null; } private Shape copyOf(Shape s) { return (s != null) ? new GeneralPath(s) : null; } private Stroke copyOf(Stroke s) { if (s == null) { return null; } if (s instanceof BasicStroke) { BasicStroke bs = (BasicStroke) s; return new BasicStroke(bs.getLineWidth(), bs.getEndCap(), bs.getLineJoin(), bs.getMiterLimit(), bs.getDashArray(), bs.getDashPhase()); } throw new UnsupportedOperationException("The Stroke type '" + s.getClass().getName() + "' is not supported"); } private Composite copyOf(Composite c) { if (c == null) { return null; } if (c instanceof AlphaComposite) { AlphaComposite ac = (AlphaComposite) c; return AlphaComposite.getInstance(ac.getRule(), ac.getAlpha()); } throw new UnsupportedOperationException("The Composite type '" + c.getClass().getName() + "' is not supported"); } private void notSupported() { throw new UnsupportedOperationException("We are sorry. Operation is not supported in Robocode."); } private void notSupportedWarn() { Logger.printlnToRobotsConsole("SYSTEM: We are sorry. Operation is not supported in Robocode."); } // -------------------------------------------------------------------------- // Worker classes // -------------------------------------------------------------------------- /** * Extended FontMetrics class which only purpose is to let us access its * protected contructor taking a Font as input parameter. * * @author Flemming N. Larsen */ private class FontMetricsByFont extends FontMetrics { static final long serialVersionUID = 1L; final FontRenderContext fontRenderContext; FontMetricsByFont(Font font, FontRenderContext frc) { super(font); fontRenderContext = frc; } /** * Bugfix [2791007] - FontMetrics StackOverflowError. * More info here: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4257064 */ @Override public int charsWidth(char[] data, int off, int len) { if (font == null) { return 0; } Rectangle2D bounds = font.getStringBounds(data, off, off + len, fontRenderContext); return (bounds != null) ? (int) (bounds.getWidth() + 0.5) : 0; } } }