-
Notifications
You must be signed in to change notification settings - Fork 89
Expand file tree
/
Copy pathCanvas.java
More file actions
264 lines (222 loc) · 7.02 KB
/
Canvas.java
File metadata and controls
264 lines (222 loc) · 7.02 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
/*
* This file is part of NodeBox.
*
* Copyright (C) 2008 Frederik De Bleser ([email protected])
*
* NodeBox is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* NodeBox 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with NodeBox. If not, see <http://www.gnu.org/licenses/>.
*/
package nodebox.graphics;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Locale;
public class Canvas extends AbstractTransformable {
public static final double DEFAULT_WIDTH = 1000;
public static final double DEFAULT_HEIGHT = 1000;
private Color background = new Color(1, 1, 1);
private double offsetX, offsetY;
private double width, height;
private ArrayList<Grob> items = new ArrayList<Grob>();
public Canvas() {
this(DEFAULT_WIDTH, DEFAULT_HEIGHT);
}
public Canvas(double width, double height) {
setSize(width, height);
}
public Canvas(Canvas other) {
this.offsetX = other.offsetX;
this.offsetY = other.offsetY;
this.width = other.width;
this.height = other.height;
this.background = other.background == null ? null : other.background.clone();
for (Grob g : other.items) {
add(g.clone());
}
}
/**
* Convert the current canvas into a geometry object.
* Only objects of a geometric nature can be present in the output.
*
* @return a Geometry object
*/
public Geometry asGeometry() {
return asGeometry(true);
}
/**
* Convert the current canvas into a geometry object.
* Only objects of a geometric nature can be present in the output.
*
* @param clone if the items on the canvas need to be cloned.
* @return a Geometry object
*/
public Geometry asGeometry(boolean clone) {
Geometry g = new Geometry();
for (Grob item : items) {
if (item instanceof Path)
g.add((Path) (clone ? item.clone() : item));
else if (item instanceof Text)
g.add(((Text) item).getPath());
else if (item instanceof Geometry)
g.extend((Geometry) (clone ? item.clone() : item));
}
return g;
}
public Color getBackground() {
return background;
}
public Color setBackground(Color background) {
return this.background = background;
}
public double getOffsetX() {
return offsetX;
}
public void setOffsetX(double offsetX) {
this.offsetX = offsetX;
}
public double getOffsetY() {
return offsetY;
}
public void setOffsetY(double offsetY) {
this.offsetY = offsetY;
}
public double getWidth() {
return width;
}
public void setWidth(double width) {
this.width = width;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
public void setSize(double width, double height) {
this.width = width;
this.height = height;
}
//// Container operations ////
public void add(Grob g) {
items.add(g);
}
public int size() {
return items.size();
}
public void clear() {
items.clear();
}
public java.util.List<Grob> getItems() {
return items;
}
public Grob get(int index) {
try {
return items.get(index);
} catch (IndexOutOfBoundsException e) {
return null;
}
}
/**
* Create copies of all grobs of the given group and append them to myself.
*
* @param c the canvas whose elements are appended.
*/
public void extend(Canvas c) {
for (Grob grob : c.getItems()) {
add(grob.clone());
}
}
public void transform(Transform t) {
for (Grob g : items) {
g.transform(t);
}
}
//// Geometry ////
public boolean isEmpty() {
return items.isEmpty();
}
/**
* Returns the bounding box of the canvas.
* <p/>
* This does not compute the bounding boxes of the children, but always returns the requested canvas bounds.
*
* @return a bounding box with x/y at the center and width/height of the canvas.
*/
public Rect getBounds() {
return new Rect(-width / 2 + offsetX, -height / 2 + offsetY, width, height);
}
public Canvas clone() {
return new Canvas(this);
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof Canvas)) return false;
Canvas other = (Canvas) obj;
return width == other.width
&& height == other.height
&& background.equals(other.background)
&& super.equals(other);
}
//// Drawing ////
public void inheritFromContext(GraphicsContext ctx) {
// TODO: Implement
}
public void draw(Graphics2D g) {
if (background != null) {
g.setColor(background.getAwtColor());
g.fill(getBounds().getRectangle2D());
}
g.clip(getBounds().getRectangle2D());
for (Grob grob : items) {
grob.draw(g);
}
}
public BufferedImage asImage() {
Rect bounds = getBounds();
BufferedImage img = new BufferedImage((int) Math.round(bounds.getWidth()), (int) Math.round(bounds.getHeight()), BufferedImage.TYPE_INT_ARGB);
Graphics2D g = img.createGraphics();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.translate(-bounds.getX(), -bounds.getY());
draw(g);
img.flush();
return img;
}
public void save(File file) {
if (file.getName().endsWith(".pdf")) {
PDFRenderer.render(this, getBounds(), file);
} else {
try {
ImageIO.write(asImage(), getFileExtension(file), file);
} catch (IOException e) {
throw new RuntimeException("Could not write image file " + file, e);
}
}
}
private String getFileExtension(File file) {
String fileName = file.getName();
String ext = null;
int i = fileName.lastIndexOf('.');
if (i > 0 && i < fileName.length() - 1) {
ext = fileName.substring(i + 1).toLowerCase(Locale.US);
}
return ext;
}
@Override
public String toString() {
return "<" + getClass().getSimpleName() + ": " + width + ", " + height + ">";
}
}