1 package javax.microedition.lcdui;
\r
3 import lejos.nxt.LCD;
\r
7 * Preliminary Graphics class for LCD Screen
\r
8 * @author Brian Bagnall
\r
11 public class Graphics {
\r
12 /** drawArc and fillArc accuracy parameter */
\r
13 private static final int ARC_ACC = 5;
\r
15 /* Public color definitions */
\r
16 public static final int BLACK = 1;
\r
17 public static final int WHITE = 0;
\r
19 /* Public line stroke definitions */
\r
20 public static final int SOLID = 0;
\r
21 public static final int DOTTED = 2;
\r
23 private int rgbColor = BLACK;
\r
24 private int strokeStyle = SOLID;
\r
26 public Graphics() {}
\r
28 public int getWidth() {
\r
29 return LCD.DISPLAY_WIDTH;
\r
32 public int getHeight() {
\r
33 return 2 * LCD.DISPLAY_DEPTH;
\r
36 public int getCenteredX(String str) {
\r
37 return (LCD.DISPLAY_CHAR_WIDTH - str.length()) / 2;
\r
41 * Using rgbColor as argument even though global, because when this
\r
42 * setPixel() method is used later it will need color argument
\r
44 public void setPixel(int rgbColor, int x, int y) {
\r
45 LCD.setPixel(rgbColor, x, y);
\r
48 public void drawLine(int x0, int y0, int x1, int y1) {
\r
49 drawLine(x0, y0, x1, y1, strokeStyle);
\r
52 private void drawLine(int x0, int y0, int x1, int y1, int style) {
\r
53 // Uses Bresenham's line algorithm
\r
57 boolean skip = false;
\r
59 if (dy < 0) { dy = -dy; stepy = -1; } else { stepy = 1; }
\r
60 if (dx < 0) { dx = -dx; stepx = -1; } else { stepx = 1; }
\r
61 dy <<= 1; // dy is now 2*dy
\r
62 dx <<= 1; // dx is now 2*dx
\r
64 setPixel(rgbColor,x0, y0);
\r
66 int fraction = dy - (dx >> 1); // same as 2*dy - dx
\r
68 if (fraction >= 0) {
\r
70 fraction -= dx; // same as fraction -= 2*dx
\r
73 fraction += dy; // same as fraction -= 2*dy
\r
74 if ((style == SOLID) || !skip)
\r
75 setPixel(rgbColor, x0, y0);
\r
79 int fraction = dx - (dy >> 1);
\r
81 if (fraction >= 0) {
\r
87 if ((style == SOLID) || !skip)
\r
88 setPixel(rgbColor, x0, y0);
\r
94 public void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle) {
\r
95 drawArc(x, y, width, height, startAngle, arcAngle, strokeStyle, false);
\r
98 public void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle) {
\r
99 // drawArc is for now only SOLID
\r
100 drawArc(x, y, width, height, startAngle, arcAngle, SOLID, true);
\r
103 private void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle,
\r
104 int style, boolean fill) {
\r
105 // Scale up width and height to create more accurate ellipse form
\r
106 int xscale = (width < height) ? ARC_ACC : ((ARC_ACC * width + (width >> 1)) / height);
\r
107 int yscale = (width < height) ? ((ARC_ACC * height + (height >> 1)) / width) : ARC_ACC;
\r
109 // Calculate x, y center and radius from upper left corner
\r
110 int x0 = x + (width >> 1);
\r
111 int y0 = y + (height >> 1);
\r
112 int radius = (width < height) ? (width >> 1) : (height >> 1);
\r
114 // Check and set start and end angle
\r
115 int endAngle = startAngle + arcAngle;
\r
116 while (endAngle < 0) endAngle = endAngle + 360;
\r
117 while (endAngle > 360) endAngle = endAngle - 360;
\r
118 if(arcAngle < 0) { // Switches start and end
\r
119 int temp = startAngle;
\r
120 startAngle = endAngle;
\r
121 endAngle = (temp > 0) ? temp : 360;
\r
124 // Initialize scaled up Bresenham's circle algorithm
\r
125 int f = (1 - ARC_ACC * radius);
\r
127 int ddF_y = -2 * ARC_ACC * radius;
\r
129 int yc = ARC_ACC * radius;
\r
142 // Skip points for dotted version
\r
143 dotskip = (dotskip + 1) % (2 * ARC_ACC);
\r
144 if ((style == DOTTED) && !fill && (dotskip < ((2 * ARC_ACC) - 1))) continue;
\r
146 // Scale down again
\r
147 int xxp = (xc * xscale + (xscale >> 1)) / (ARC_ACC * ARC_ACC);
\r
148 int xyp = (xc * yscale + (yscale >> 1)) / (ARC_ACC * ARC_ACC);
\r
149 int yyp = (yc * yscale + (yscale >> 1)) / (ARC_ACC * ARC_ACC);
\r
150 int yxp = (yc * xscale + (xscale >> 1)) / (ARC_ACC * ARC_ACC);
\r
152 // Calculate angle for partly circles / ellipses
\r
153 // NOTE: Below, (float) should not be needed. Not sure why Math.round() only accepts float.
\r
154 int tp = (int) Math.round((float) Math.toDegrees(Math.atan2(yc, xc)));
\r
156 /* TODO: Optimize more by drawing horizontal lines */
\r
157 if (((90 - tp) >= startAngle) && ((90 - tp) <= endAngle))
\r
158 drawLine(x0, y0, x0 + yxp, y0 - xyp, style); // 0 - 45 degrees
\r
159 if ((tp >= startAngle) && (tp <= endAngle))
\r
160 drawLine(x0, y0, x0 + xxp, y0 - yyp, style); // 45 - 90 degrees
\r
161 if (((180 - tp) >= startAngle) && ((180 - tp) <= endAngle))
\r
162 drawLine(x0, y0, x0 - xxp, y0 - yyp, style); // 90 - 135 degrees
\r
163 if (((180 - (90 - tp)) >= startAngle) && ((180 - (90 - tp)) <= endAngle))
\r
164 drawLine(x0, y0, x0 - yxp, y0 - xyp, style); // 135 - 180 degrees
\r
165 if (((270 - tp) >= startAngle) && ((270 - tp) <= endAngle))
\r
166 drawLine(x0, y0, x0 - yxp, y0 + xyp, style); // 180 - 225 degrees
\r
167 if (((270 - (90 - tp)) >= startAngle) && ((270 - (90 - tp)) <= endAngle))
\r
168 drawLine(x0, y0, x0 - xxp, y0 + yyp, style); // 225 - 270 degrees
\r
169 if (((360 - tp) >= startAngle) && ((360 - tp) <= endAngle))
\r
170 drawLine(x0, y0, x0 + xxp, y0 + yyp, style); // 270 - 315 degrees
\r
171 if (((360 - (90 - tp)) >= startAngle) && ((360 - (90 - tp)) <= endAngle))
\r
172 drawLine(x0, y0, x0 + yxp, y0 + xyp, style); // 315 - 360 degrees
\r
174 if (((90 - tp) >= startAngle) && ((90 - tp) <= endAngle))
\r
175 setPixel(rgbColor, x0 + yxp, y0 - xyp); // 0 - 45 degrees
\r
176 if ((tp >= startAngle) && (tp <= endAngle))
\r
177 setPixel(rgbColor, x0 + xxp, y0 - yyp); // 45 - 90 degrees
\r
178 if (((180 - tp) >= startAngle) && ((180 - tp) <= endAngle))
\r
179 setPixel(rgbColor, x0 - xxp, y0 - yyp); // 90 - 135 degrees
\r
180 if (((180 - (90 - tp)) >= startAngle) && ((180 - (90 - tp)) <= endAngle))
\r
181 setPixel(rgbColor, x0 - yxp, y0 - xyp); // 135 - 180 degrees
\r
182 if (((270 - tp) >= startAngle) && ((270 - tp) <= endAngle))
\r
183 setPixel(rgbColor, x0 - yxp, y0 + xyp); // 180 - 225 degrees
\r
184 if (((270 - (90 - tp)) >= startAngle) && ((270 - (90 - tp)) <= endAngle))
\r
185 setPixel(rgbColor, x0 - xxp, y0 + yyp); // 225 - 270 degrees
\r
186 if (((360 - tp) >= startAngle) && ((360 - tp) <= endAngle))
\r
187 setPixel(rgbColor, x0 + xxp, y0 + yyp); // 270 - 315 degrees
\r
188 if (((360 - (90 - tp)) >= startAngle) && ((360 - (90 - tp)) <= endAngle))
\r
189 setPixel(rgbColor, x0 + yxp, y0 + xyp); // 315 - 360 degrees
\r
194 public void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) {
\r
196 int xc = x + (width/2);
\r
197 int yc = y + (height/2);
\r
198 int a = arcWidth/2;
\r
199 int b = arcHeight/2;
\r
201 int translateX = (width/2) - (arcWidth/2);
\r
202 int translateY = (height/2) - (arcHeight/2);
\r
205 int xDiff = arcWidth/2;
\r
206 int yDiff = arcHeight/2;
\r
207 drawLine(x, y+yDiff, x, height-yDiff);
\r
208 drawLine(width, y+yDiff, width, height-yDiff);
\r
209 drawLine(x+xDiff, y, width-xDiff, y);
\r
210 drawLine(x+xDiff, height, width-xDiff, height);
\r
213 /* e(x,y) = b^2*x^2 + a^2*y^2 - a^2*b^2 */
\r
214 int xxx = 0, yyy = b;
\r
215 int a2 = a*a, b2 = b*b;
\r
216 int crit1 = -(a2/4 + a%2 + b2);
\r
217 int crit2 = -(b2/4 + b%2 + a2);
\r
218 int crit3 = -(b2/4 + b%2);
\r
219 int t = -a2*yyy; /* e(xxx+1/2,y-1/2) - (a^2+b^2)/4 */
\r
220 int dxt = 2*b2*xxx, dyt = -2*a2*yyy;
\r
221 int d2xt = 2*b2, d2yt = 2*a2;
\r
223 while (yyy>=0 && xxx<=a) {
\r
224 setPixel(BLACK, xc+xxx + translateX, yc+yyy + translateY); // Q4
\r
225 if (xxx!=0 || yyy!=0)
\r
226 setPixel(BLACK, xc-xxx - translateX, yc-yyy - translateY); // Q2
\r
227 if (xxx!=0 && yyy!=0) {
\r
228 setPixel(BLACK, xc+xxx + translateX, yc-yyy - translateY); // Q1
\r
229 setPixel(BLACK, xc-xxx - translateX, yc+yyy + translateY); // Q3
\r
231 if (t + b2*xxx <= crit1 || /* e(xxx+1,y-1/2) <= 0 */
\r
232 t + a2*yyy <= crit3) /* e(xxx+1/2,y) <= 0 */
\r
233 {xxx++; dxt += d2xt; t += dxt;} // incx()
\r
234 else if (t - a2*yyy > crit2) /* e(xxx+1/2,y-1) > 0 */
\r
235 {yyy--; dyt += d2yt; t += dyt;}
\r
237 {xxx++; dxt += d2xt; t += dxt;} // incx()
\r
238 {yyy--; dyt += d2yt; t += dyt;}
\r
244 public void drawRect(int x, int y, int width, int height) {
\r
245 if ((width < 0) || (height < 0))
\r
248 if (height == 0 || width == 0) {
\r
249 drawLine(x, y, x + width, y + height);
\r
251 drawLine(x, y, x + width - 1, y);
\r
252 drawLine(x + width, y, x + width, y + height - 1);
\r
253 drawLine(x + width, y + height, x + 1, y + height);
\r
254 drawLine(x, y + height, x, y + 1);
\r
258 public void fillRect(int x, int y, int width, int height) {
\r
259 if ((width < 0) || (height < 0))
\r
262 for(int i=y;i<y + height;i++)
\r
263 drawLine(x, i, x + width, i);
\r
264 //for(int j=x; j<x+width;j++) // Barely faster than using lines.
\r
265 //setPixel(rgbColor, j, i);
\r
268 public void drawString(String str, int x, int y) {
\r
269 drawString(str, x, y, false);
\r
272 public void drawString(String str, int x, int y, boolean invert) {
\r
273 LCD.drawString(str, x, y, invert);
\r
276 public void drawChar(char c, int x, int y, boolean invert) {
\r
277 LCD.drawChar(c, x, y, invert);
\r
280 public void drawImage(Image img, int x, int y, boolean invert) {
\r
285 byte[] imgData = img.getData();
\r
286 for (int iy = y; iy < (y + img.getHeight()); iy += 8) {
\r
287 int yOffset = ((iy - y) / 8) * img.getWidth();
\r
288 for (int ix = x; ix < (x + img.getWidth()); ix++) {
\r
289 LCD.drawPixels(imgData[yOffset + (ix - x)], ix, iy, invert);
\r
294 public int getStrokeStyle() {
\r
295 return strokeStyle;
\r
298 public void setStrokeStyle(int style) {
\r
299 if (style != SOLID && style != DOTTED) {
\r
300 throw new IllegalArgumentException();
\r
302 strokeStyle = style;
\r
305 // Temp for testing purposes until Canvas made.
\r
306 public void refresh() {
\r
311 // Temp method for testing. Clears out graphics buffer
\r
312 // and refreshes screen.
\r
313 public void clear() {
\r
314 LCD.clearDisplay();
\r
319 class LCD extends JPanel {
\r
320 public static final int SCREEN_WIDTH = 100;
\r
321 public static final int SCREEN_HEIGHT = 64;
\r
322 public static final int SCREEN_SCALE = 4;
\r
324 public static int [] screenBuf;
\r
325 public Graphics nxjGraphics;
\r
327 // drawArc and fillArc parameters
\r
336 setBackground(Color.WHITE);
\r
337 setPreferredSize(new Dimension(SCREEN_SCALE * SCREEN_WIDTH,
\r
338 SCREEN_SCALE * SCREEN_HEIGHT));
\r
340 nxjGraphics = new Graphics();
\r
341 nxjGraphics.setStrokeStyle(Graphics.DOTTED);
\r
342 nxjGraphics.drawLine(0, 0, 100, 64);
\r
343 nxjGraphics.fillArc(x, y, width, height, start, angle);
\r
344 nxjGraphics.drawArc(10, 10, 20, 40, 0, 360);
\r
345 // nxjGraphics.drawRoundRect(75, 5, 20, 10, 45, 45);
\r
346 nxjGraphics.refresh();
\r
350 public static void drawString(String str, int x, int y) {}
\r
351 public static void setDisplay(int [] buff) {
\r
354 public static void refresh() {}
\r
356 public synchronized void paint(java.awt.Graphics g) {
\r
357 int w = getSize().width;
\r
358 int h = getSize().height;
\r
360 java.awt.Graphics2D g2 = (java.awt.Graphics2D) g;
\r
361 g2.setBackground(getBackground());
\r
362 g2.clearRect(0, 0, w, h);
\r
364 // Draw example image for verification
\r
365 g2.setColor(Color.RED);
\r
366 g2.fillArc(SCREEN_SCALE * x, SCREEN_SCALE * y, SCREEN_SCALE * width,
\r
367 SCREEN_SCALE * height, start, angle);
\r
368 g2.drawRect(300, 20, 80, 40);
\r
369 g2.drawRoundRect(300, 20, 80, 40, 45, 30);
\r
372 g2.setColor(Color.BLACK);
\r
373 for (int xp = 0; xp < 100; xp++) {
\r
374 for (int yp = 0; yp < 64; yp++) {
\r
375 if (xp < 0 || xp >= 100 || yp < 0 || yp >= 64) continue;
\r
376 int xChar = xp / 4;
\r
377 int yChar = yp / 8;
\r
378 int index = yChar * 25 + xChar;
\r
379 int specificBit = (yp % 8) + ((xp % 4) * 8);
\r
380 if ((screenBuf[index] & (1 << specificBit)) != 0) {
\r
381 g2.drawRect(SCREEN_SCALE * xp, SCREEN_SCALE * yp,
\r
382 SCREEN_SCALE, SCREEN_SCALE);
\r
389 class TestApp extends JFrame {
\r
391 // End application when window is closed
\r
392 addWindowListener(new WindowAdapter() {
\r
393 public void windowClosing(WindowEvent e) {
\r
398 setTitle("NXJ Grapics test app");
\r
399 getContentPane().add(new LCD());
\r
404 public static void main(String[] args) {
\r