/** * Copyright 2010 The ForPlay Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package forplay.java; import forplay.core.Asserts; import forplay.core.Canvas; import forplay.core.Gradient; import forplay.core.Image; import forplay.core.Path; import forplay.core.Pattern; import java.awt.Color; import java.awt.Graphics2D; import java.awt.geom.AffineTransform; import java.awt.geom.Ellipse2D; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import java.util.Deque; import java.util.LinkedList; class JavaCanvas implements Canvas { final Graphics2D gfx; private final int width, height; private Deque<JavaCanvasState> stateStack = new LinkedList<JavaCanvasState>(); private Ellipse2D.Float ellipse = new Ellipse2D.Float(); private Line2D.Float line = new Line2D.Float(); private Rectangle2D.Float rect = new Rectangle2D.Float(); JavaCanvas(Graphics2D graphics, int width, int height) { this.gfx = graphics; this.width = width; this.height = height; // push default state stateStack.push(new JavaCanvasState()); // All clears go to rgba(0,0,0,0). gfx.setBackground(new Color(0, true)); } public float alpha() { return currentState().alpha; } public void clear() { gfx.clearRect(0, 0, width, height); } @Override public void clip(Path path) { Asserts.checkArgument(path instanceof JavaPath); currentState().clip = (JavaPath) path; } public void drawImage(Image img, float x, float y) { Asserts.checkArgument(img instanceof JavaImage); JavaImage jimg = (JavaImage) img; currentState().prepareFill(gfx); int dx = (int) x, dy = (int) y, w = jimg.width(), h = jimg.height(); gfx.drawImage(jimg.img, dx, dy, dx + w, dy + h, 0, 0, w, h, null); } public void drawImageCentered(Image img, float x, float y) { drawImage(img, x - img.width()/2, y - img.height()/2); } public void drawImage(Image img, float x, float y, float w, float h) { Asserts.checkArgument(img instanceof JavaImage); JavaImage jimg = (JavaImage) img; // For non-integer scaling, we have to use AffineTransform. AffineTransform tx = new AffineTransform(w / jimg.width(), 0f, 0f, h / jimg.height(), x, y); currentState().prepareFill(gfx); gfx.drawImage(jimg.img, tx, null); } @Override public void drawImage(Image img, float dx, float dy, float dw, float dh, float sx, float sy, float sw, float sh) { Asserts.checkArgument(img instanceof JavaImage); JavaImage jimg = (JavaImage) img; // TODO: use AffineTransform here as well? currentState().prepareFill(gfx); gfx.drawImage(jimg.img, (int)dx, (int)dy, (int)(dx + dw), (int)(dy + dh), (int)sx, (int)sy, (int)(sx + sw), (int)(sy + sh), null); } @Override public void drawLine(float x0, float y0, float x1, float y1) { currentState().prepareStroke(gfx); line.setLine(x0, y0, x1, y1); gfx.draw(line); } @Override public void drawPoint(float x, float y) { currentState().prepareStroke(gfx); gfx.drawLine((int) x, (int) y, (int) x, (int) y); } @Override public void drawText(String text, float x, float y) { currentState().prepareFill(gfx); gfx.drawString(text, x, y); } @Override public void fillCircle(float x, float y, float radius) { currentState().prepareFill(gfx); ellipse.setFrame(x - radius, y - radius, 2 * radius, 2 * radius); gfx.fill(ellipse); } @Override public void fillPath(Path path) { Asserts.checkArgument(path instanceof JavaPath); currentState().prepareFill(gfx); gfx.fill(((JavaPath) path).path); } @Override public void fillRect(float x, float y, float width, float height) { currentState().prepareFill(gfx); rect.setRect(x, y, width, height); gfx.fill(rect); } @Override public int height() { return height; } @Override public void restore() { stateStack.pop(); gfx.setTransform(currentState().transform); } @Override public void rotate(float angle) { gfx.rotate(angle); } @Override public void save() { // update saved transform currentState().transform = gfx.getTransform(); // clone to maintain current state stateStack.push(new JavaCanvasState(currentState())); } @Override public void scale(float x, float y) { gfx.scale(x, y); } public void setAlpha(float alpha) { currentState().alpha = alpha; } @Override public void setCompositeOperation(Composite composite) { currentState().composite = composite; } @Override public void setFillColor(int color) { currentState().fillColor = color; currentState().fillGradient = null; currentState().fillPattern = null; } @Override public void setFillGradient(Gradient gradient) { Asserts.checkArgument(gradient instanceof JavaGradient); currentState().fillGradient = (JavaGradient) gradient; currentState().fillPattern = null; currentState().fillColor = 0; } @Override public void setFillPattern(Pattern pattern) { Asserts.checkArgument(pattern instanceof JavaPattern); currentState().fillPattern = (JavaPattern) pattern; currentState().fillGradient = null; currentState().fillColor = 0; } @Override public void setLineCap(LineCap cap) { currentState().lineCap = cap; } @Override public void setLineJoin(LineJoin join) { currentState().lineJoin = join; } @Override public void setMiterLimit(float miter) { currentState().miterLimit = miter; } @Override public void setStrokeColor(int color) { currentState().strokeColor = color; } @Override public void setStrokeWidth(float w) { currentState().strokeWidth = w; } @Override public void setTransform(float m11, float m12, float m21, float m22, float dx, float dy) { gfx.setTransform(new AffineTransform(m11, m12, m21, m22, dx, dy)); } @Override public void strokeCircle(float x, float y, float radius) { currentState().prepareStroke(gfx); ellipse.setFrame(x - radius, y - radius, 2 * radius, 2 * radius); gfx.draw(ellipse); } @Override public void strokePath(Path path) { currentState().prepareStroke(gfx); gfx.setColor(new Color(currentState().strokeColor, false)); gfx.draw(((JavaPath) path).path); } @Override public void strokeRect(float x, float y, float width, float height) { currentState().prepareStroke(gfx); rect.setRect(x, y, width, height); gfx.draw(rect); } @Override public void transform(float m11, float m12, float m21, float m22, float dx, float dy) { gfx.transform(new AffineTransform(m11, m12, m21, m22, dx, dy)); } @Override public void translate(float x, float y) { gfx.translate(x, y); } @Override public int width() { return width; } private JavaCanvasState currentState() { return stateStack.getFirst(); } }