View Javadoc
1   package nl.tudelft.simulation.dsol.web.animation;
2   
3   import java.awt.AlphaComposite;
4   import java.awt.BasicStroke;
5   import java.awt.Canvas;
6   import java.awt.Color;
7   import java.awt.Composite;
8   import java.awt.Font;
9   import java.awt.FontMetrics;
10  import java.awt.Graphics;
11  import java.awt.Graphics2D;
12  import java.awt.GraphicsConfiguration;
13  import java.awt.Image;
14  import java.awt.Paint;
15  import java.awt.Rectangle;
16  import java.awt.RenderingHints;
17  import java.awt.RenderingHints.Key;
18  import java.awt.Shape;
19  import java.awt.Stroke;
20  import java.awt.font.FontRenderContext;
21  import java.awt.font.GlyphVector;
22  import java.awt.geom.AffineTransform;
23  import java.awt.geom.Ellipse2D;
24  import java.awt.geom.Line2D;
25  import java.awt.geom.Path2D;
26  import java.awt.geom.PathIterator;
27  import java.awt.geom.Rectangle2D;
28  import java.awt.image.BufferedImage;
29  import java.awt.image.BufferedImageOp;
30  import java.awt.image.ImageObserver;
31  import java.awt.image.RenderedImage;
32  import java.awt.image.renderable.RenderableImage;
33  import java.text.AttributedCharacterIterator;
34  import java.util.LinkedHashMap;
35  import java.util.Map;
36  
37  import org.djutils.logger.CategoryLogger;
38  
39  import nl.tudelft.simulation.dsol.logger.Cat;
40  
41  /**
42   * HtmlGraphics.java. <br>
43   * <br>
44   * Copyright (c) 2003-2025 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
45   * for project information <a href="https://www.simulation.tudelft.nl/" target="_blank">www.simulation.tudelft.nl</a>. The
46   * source code and binary code of this software is proprietary information of Delft University of Technology.
47   * @author <a href="https://github.com/averbraeck" target="_blank">Alexander Verbraeck</a>
48   */
49  public class HtmlGraphics2D extends Graphics2D
50  {
51      /** the current color of the background for drawing. */
52      Color background = Color.WHITE;
53  
54      /** the current drawing color. */
55      Color color = Color.BLACK;
56  
57      /** the current font. */
58      Font font = new Font(Font.SANS_SERIF, Font.PLAIN, 10);
59  
60      /** the drawing canvas. */
61      Canvas canvas = new Canvas();
62  
63      /** the cached current font properties. */
64      FontMetrics fontMetrics = this.canvas.getFontMetrics(this.font);
65  
66      /** the current paint. */
67      Paint paint = Color.BLACK;
68  
69      /** the current stroke. */
70      Stroke stroke = new BasicStroke();
71  
72      /** TODO: the current rendering hints. */
73      RenderingHints renderingHints = new RenderingHints(new LinkedHashMap<Key, Object>());
74  
75      /** the current affine transform. */
76      AffineTransform affineTransform = new AffineTransform();
77  
78      /** TODO: the current composite. What is that? */
79      Composite composite = AlphaComposite.Clear;
80  
81      /** the commands to send over the channel to the HTML5 code. */
82      StringBuffer commands = new StringBuffer();
83  
84      /**
85       * Clear the commands and put the start tag in.
86       */
87      public void clearCommand()
88      {
89          this.commands = new StringBuffer();
90          this.commands.append("<animate>\n");
91      }
92  
93      /**
94       * Close the commands and put the end tag in.
95       * @return the current set of commands
96       */
97      public String closeAndGetCommands()
98      {
99          this.commands.append("</animate>\n");
100         return this.commands.toString();
101     }
102 
103     /**
104      * Add a draw command
105      * @param drawCommand String; the tag for the draw command
106      * @param params Object...; the params for the draw command
107      */
108     protected void addDraw(String drawCommand, Object... params)
109     {
110         this.commands.append("<draw>" + drawCommand);
111         for (Object param : params)
112         {
113             this.commands.append("," + param.toString());
114         }
115         this.commands.append("</draw>\n");
116     }
117 
118     /**
119      * add AffineTransform to the command.
120      */
121     protected void addAffineTransform()
122     {
123         this.commands.append(",");
124         this.commands.append(this.affineTransform.getScaleX());
125         this.commands.append(",");
126         this.commands.append(this.affineTransform.getShearY());
127         this.commands.append(",");
128         this.commands.append(this.affineTransform.getShearX());
129         this.commands.append(",");
130         this.commands.append(this.affineTransform.getScaleY());
131         this.commands.append(",");
132         this.commands.append(this.affineTransform.getTranslateX());
133         this.commands.append(",");
134         this.commands.append(this.affineTransform.getTranslateY());
135     }
136 
137     /**
138      * add Color to the command.
139      * @param c Color; the color
140      */
141     protected void addColor(Color c)
142     {
143         this.commands.append(",");
144         this.commands.append(c.getRed());
145         this.commands.append(",");
146         this.commands.append(c.getGreen());
147         this.commands.append(",");
148         this.commands.append(c.getBlue());
149         this.commands.append(",");
150         this.commands.append(c.getAlpha());
151         this.commands.append(",");
152         this.commands.append(c.getTransparency());
153     }
154 
155     /**
156      * add font data to the command, font-name, font-size, bold/italic/plain
157      */
158     protected void addFontData()
159     {
160         this.commands.append(",");
161         String javaFontName = this.font.getFontName().toLowerCase();
162         String htmlFontName;
163         if (javaFontName.contains("arial") || javaFontName.contains("helvetica") || javaFontName.contains("verdana")
164                 || javaFontName.contains("tahoma") || javaFontName.contains("segoe") || javaFontName.contains("sans"))
165             htmlFontName = "sans-serif";
166         else if (javaFontName.contains("times") || javaFontName.contains("cambria") || javaFontName.contains("georgia")
167                 || javaFontName.contains("serif"))
168             htmlFontName = "serif";
169         else if (javaFontName.contains("courier") || javaFontName.contains("consol") || javaFontName.contains("mono"))
170             htmlFontName = "monospace";
171         else
172             htmlFontName = "sans-serif";
173         this.commands.append(htmlFontName);
174         this.commands.append(",");
175         this.commands.append(this.font.getSize2D());
176         this.commands.append(",");
177         if (this.font.isBold())
178             this.commands.append("bold");
179         else if (this.font.isItalic())
180             this.commands.append("italic");
181         else
182             this.commands.append("plain");
183     }
184 
185     /**
186      * Add fill command, transform.m11(h-scale), transform.m12(h-skew), transform.m21(v-skew), transform.m22(v-scale),
187      * transform.dx(h-translate), transform.dy(v-translate), color.r, color.g, color.b, color.alpha, color.transparency,
188      * params...
189      * @param fillCommand String; the tag to use
190      * @param params Object...; the params to send
191      */
192     protected void addTransformFill(String fillCommand, Object... params)
193     {
194         this.commands.append("<transformFill>" + fillCommand);
195         addAffineTransform();
196         if (this.paint instanceof Color)
197             addColor((Color) this.paint);
198         else
199             addColor(this.color);
200         for (Object param : params)
201         {
202             this.commands.append("," + param.toString());
203         }
204         this.commands.append("</transformFill>\n");
205     }
206 
207     /**
208      * Add command, transform.m11(h-scale), transform.m12(h-skew), transform.m21(v-skew), transform.m22(v-scale),
209      * transform.dx(h-translate), transform.dy(v-translate), linecolor.r, linecolor.g, linecolor.b, linecolor.alpha,
210      * linecolor.transparency, line-width, params...
211      * @param drawCommand String; the tag to use
212      * @param params Object...; the params
213      */
214     protected void addTransformDraw(String drawCommand, Object... params)
215     {
216         this.commands.append("<transformDraw>" + drawCommand);
217         addAffineTransform();
218         if (this.paint instanceof Color)
219             addColor((Color) this.paint);
220         else
221             addColor(this.color);
222         if (this.stroke instanceof BasicStroke)
223             this.commands.append("," + ((BasicStroke) this.stroke).getLineWidth());
224         else
225             this.commands.append(", 0.1");
226         for (Object param : params)
227         {
228             this.commands.append("," + param.toString());
229         }
230         this.commands.append("</transformDraw>\n");
231     }
232 
233     /**
234      * adds a float array to the command
235      * @param array float[]; the array
236      * @param length int; the number of points from the array to write
237      */
238     private void addFloatArray(final float[] array, final int length)
239     {
240         for (int i = 0; i < length; i++)
241         {
242             this.commands.append(", " + array[i]);
243         }
244     }
245 
246     /**
247      * adds a double array to the command
248      * @param array double[]; the array
249      * @param length int; the number of points from the array to write
250      */
251     private void addDoubleArray(final double[] array, final int length)
252     {
253         for (int i = 0; i < length; i++)
254         {
255             this.commands.append(", " + array[i]);
256         }
257     }
258 
259     /**
260      * Add a path2D to the command. In case of fill:<br>
261      * FILL, transform.m11(h-scale), transform.m12(h-skew), transform.m21(v-skew), transform.m22(v-scale),
262      * transform.dx(h-translate), transform.dy(v-translate), fillcolor.r, fillcolor.g, fillcolor.b, fillcolor.alpha,
263      * fillcolor.transparency, winding_rule[WIND_EVEN_ODD/WIND_NON_ZERO], COMMAND, coords, COMMAND, coords, ... <br>
264      * In case of draw:<br>
265      * DRAW, transform.m11(h-scale), transform.m12(h-skew), transform.m21(v-skew), transform.m22(v-scale),
266      * transform.dx(h-translate), transform.dy(v-translate), strokecolor.r, strokecolor.g, strokecolor.b, strokecolor.alpha,
267      * strokecolor.transparency, line_width, COMMAND, coords, COMMAND, coords, ... <br>
268      * where command can be one of the following:<br>
269      * - CLOSE, followed by no coordinates<br>
270      * - CUBICTO, followed by 3 coordinates (6 numbers)<br>
271      * - LINETO, followed by 1 coordinate (2 numbers)<br>
272      * - MOVETO, followed by 1 coordinate (2 numbers)<br>
273      * - QUADTO, followed by 2 coordinates (4 numbers)<br>
274      * @param path Path2D.Float; the path to draw
275      * @param fill boolean;
276      */
277     protected void addTransformPathFloat(Path2D.Float path, boolean fill)
278     {
279         if (fill)
280             this.commands.append("<transformPath>FILL");
281         else
282             this.commands.append("<transformPath>DRAW");
283         addAffineTransform();
284         addColor(this.color);
285         if (fill)
286         {
287             if (path.getWindingRule() == Path2D.WIND_EVEN_ODD)
288                 this.commands.append(",WIND_EVEN_ODD");
289             else
290                 this.commands.append(",WIND_NON_ZERO");
291         }
292         else
293         {
294             if (this.stroke instanceof BasicStroke)
295                 this.commands.append("," + ((BasicStroke) this.stroke).getLineWidth());
296             else
297                 this.commands.append(", 0.1");
298         }
299         float[] coords = new float[6];
300         PathIterator i = path.getPathIterator(null);
301         while (!i.isDone())
302         {
303             int segment = i.currentSegment(coords);
304             switch (segment)
305             {
306                 case PathIterator.SEG_CLOSE:
307                     this.commands.append(",CLOSE");
308                     break;
309                 case PathIterator.SEG_CUBICTO:
310                     this.commands.append(",CUBICTO");
311                     addFloatArray(coords, 6);
312                     break;
313                 case PathIterator.SEG_LINETO:
314                     this.commands.append(",LINETO");
315                     addFloatArray(coords, 2);
316                     break;
317                 case PathIterator.SEG_MOVETO:
318                     this.commands.append(",MOVETO");
319                     addFloatArray(coords, 2);
320                     break;
321                 case PathIterator.SEG_QUADTO:
322                     this.commands.append(",QUADTO");
323                     addFloatArray(coords, 4);
324                     break;
325                 default:
326                     throw new RuntimeException("unkown segment");
327             }
328             i.next();
329         }
330             this.commands.append("</transformPath>\n");
331     }
332 
333     /**
334      * Add a path2D to the command. In case of fill:<br>
335      * FILL, transform.m11(h-scale), transform.m12(h-skew), transform.m21(v-skew), transform.m22(v-scale),
336      * transform.dx(h-translate), transform.dy(v-translate), fillcolor.r, fillcolor.g, fillcolor.b, fillcolor.alpha,
337      * fillcolor.transparency, winding_rule[WIND_EVEN_ODD/WIND_NON_ZERO], COMMAND, coords, COMMAND, coords, ... <br>
338      * In case of draw:<br>
339      * DRAW, transform.m11(h-scale), transform.m12(h-skew), transform.m21(v-skew), transform.m22(v-scale),
340      * transform.dx(h-translate), transform.dy(v-translate), strokecolor.r, strokecolor.g, strokecolor.b, strokecolor.alpha,
341      * strokecolor.transparency, line_width, COMMAND, coords, COMMAND, coords, ... <br>
342      * where command can be one of the following:<br>
343      * - CLOSE, followed by no coordinates<br>
344      * - CUBICTO, followed by 3 coordinates (6 numbers)<br>
345      * - LINETO, followed by 1 coordinate (2 numbers)<br>
346      * - MOVETO, followed by 1 coordinate (2 numbers)<br>
347      * - QUADTO, followed by 2 coordinates (4 numbers)<br>
348      * @param path Path2D.Double; the path to draw
349      * @param fill boolean;
350      */
351     protected void addTransformPathDouble(Path2D.Double path, boolean fill)
352     {
353         if (fill)
354             this.commands.append("<transformPath>FILL");
355         else
356             this.commands.append("<transformPath>DRAW");
357         addAffineTransform();
358         addColor(this.color);
359         if (fill)
360         {
361             if (path.getWindingRule() == Path2D.WIND_EVEN_ODD)
362                 this.commands.append(",WIND_EVEN_ODD");
363             else
364                 this.commands.append(",WIND_NON_ZERO");
365         }
366         else
367         {
368             if (this.stroke instanceof BasicStroke)
369                 this.commands.append("," + ((BasicStroke) this.stroke).getLineWidth());
370             else
371                 this.commands.append(", 0.1");
372         }
373         double[] coords = new double[6];
374         PathIterator i = path.getPathIterator(null);
375         while (!i.isDone())
376         {
377             int segment = i.currentSegment(coords);
378             switch (segment)
379             {
380                 case PathIterator.SEG_CLOSE:
381                     this.commands.append(",CLOSE");
382                     break;
383                 case PathIterator.SEG_CUBICTO:
384                     this.commands.append(",CUBICTO");
385                     addDoubleArray(coords, 6);
386                     break;
387                 case PathIterator.SEG_LINETO:
388                     this.commands.append(",LINETO");
389                     addDoubleArray(coords, 2);
390                     break;
391                 case PathIterator.SEG_MOVETO:
392                     this.commands.append(",MOVETO");
393                     addDoubleArray(coords, 2);
394                     break;
395                 case PathIterator.SEG_QUADTO:
396                     this.commands.append(",QUADTO");
397                     addDoubleArray(coords, 4);
398                     break;
399                 default:
400                     throw new RuntimeException("unkown segment");
401             }
402             i.next();
403         }
404         this.commands.append("</transformPath>\n");
405     }
406 
407     /**
408      * Add string, 0=command, 1=transform.m11(h-scale), 2=transform.m12(h-skew), 3=transform.m21(v-skew),
409      * 4=transform.m22(v-scale), 5=transform.dx(h-translate), 6=transform.dy(v-translate), 7=color.r, 8=color.g, 9=color.b,
410      * 10=color.alpha, 11=color.transparency, 12=fontname, 13=fontsize, 14=fontstyle(normal/italic/bold), 15=x, 16=y, 17=text.
411      * @param drawCommand String; the tag to use
412      * @param params Object...; the params
413      */
414     protected void addTransformText(String drawCommand, Object... params)
415     {
416         this.commands.append("<transformText>" + drawCommand);
417         addAffineTransform();
418         addColor(this.color);
419         addFontData();
420         for (Object param : params)
421         {
422             this.commands.append("," + param.toString());
423         }
424         this.commands.append("</transformText>\n");
425     }
426 
427     @Override
428     public void draw(Shape shape)
429     {
430         drawFillShape(shape, false);
431     }
432 
433     /**
434      * Draw or fill a shape.
435      * @param shape Shape; the shape
436      * @param fill boolean; filled or not
437      */
438     protected void drawFillShape(Shape shape, boolean fill)
439     {
440         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.draw(shape: {})", shape.getClass().getSimpleName());
441         if (shape instanceof Ellipse2D.Double)
442         {
443             Ellipse2D.Double ellipse = (Ellipse2D.Double) shape;
444             if (fill)
445                 addTransformFill("fillOval", ellipse.getCenterX(), ellipse.getCenterY(), ellipse.width / 2.0,
446                         ellipse.height / 2.0);
447             else
448                 addTransformDraw("drawOval", ellipse.getCenterX(), ellipse.getCenterY(), ellipse.width / 2.0,
449                         ellipse.height / 2.0);
450         }
451         else if (shape instanceof Ellipse2D.Float)
452         {
453             Ellipse2D.Float ellipse = (Ellipse2D.Float) shape;
454             if (fill)
455                 addTransformFill("fillOval", ellipse.getCenterX(), ellipse.getCenterY(), ellipse.width / 2.0,
456                         ellipse.height / 2.0);
457             else
458                 addTransformDraw("drawOval", ellipse.getCenterX(), ellipse.getCenterY(), ellipse.width / 2.0,
459                         ellipse.height / 2.0);
460         }
461         else if (shape instanceof Line2D.Double)
462         {
463             Line2D.Double line = (Line2D.Double) shape;
464             addTransformDraw("drawLine", line.x1, line.y1, line.x2, line.y2);
465         }
466         else if (shape instanceof Line2D.Float)
467         {
468             Line2D.Float line = (Line2D.Float) shape;
469             addTransformDraw("drawLine", line.x1, line.y1, line.x2, line.y2);
470         }
471         else if (shape instanceof Rectangle2D.Double)
472         {
473             Rectangle2D.Double rect = (Rectangle2D.Double) shape;
474             if (fill)
475                 addTransformFill("fillRect", rect.x, rect.y, rect.width, rect.height);
476             else
477                 addTransformDraw("drawRect", rect.x, rect.y, rect.width, rect.height);
478         }
479         else if (shape instanceof Rectangle2D.Float)
480         {
481             Rectangle2D.Float rect = (Rectangle2D.Float) shape;
482             if (fill)
483                 addTransformFill("fillRect", rect.x, rect.y, rect.width, rect.height);
484             else
485                 addTransformDraw("drawRect", rect.x, rect.y, rect.width, rect.height);
486         }
487         else if (shape instanceof Path2D.Float)
488         {
489             Path2D.Float path = (Path2D.Float) shape;
490             addTransformPathFloat(path, fill);
491         }
492         else if (shape instanceof Path2D.Double)
493         {
494             Path2D.Double path = (Path2D.Double) shape;
495             addTransformPathDouble(path, fill);
496         }
497 
498     }
499 
500     @Override
501     public boolean drawImage(Image img, AffineTransform xform, ImageObserver obs)
502     {
503         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.drawImage()");
504         return true;
505     }
506 
507     @Override
508     public void drawImage(BufferedImage img, BufferedImageOp op, int x, int y)
509     {
510         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.drawImage()");
511     }
512 
513     @Override
514     public void drawRenderedImage(RenderedImage img, AffineTransform xform)
515     {
516         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.drawRenderedImage()");
517     }
518 
519     @Override
520     public void drawRenderableImage(RenderableImage img, AffineTransform xform)
521     {
522         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.drawRenderableImage()");
523     }
524 
525     @Override
526     public void drawString(String str, int x, int y)
527     {
528         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.drawString()");
529         addTransformText("drawString", x, y, str);
530     }
531 
532     @Override
533     public void drawString(String str, float x, float y)
534     {
535         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.drawString()");
536         addTransformText("drawString", x, y, str);
537     }
538 
539     @Override
540     public void drawString(AttributedCharacterIterator iterator, int x, int y)
541     {
542         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.drawString()");
543     }
544 
545     @Override
546     public void drawString(AttributedCharacterIterator iterator, float x, float y)
547     {
548         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.drawString()");
549     }
550 
551     @Override
552     public void drawGlyphVector(GlyphVector g, float x, float y)
553     {
554         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.drawGlyphVector()");
555     }
556 
557     @Override
558     public void fill(Shape shape)
559     {
560         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.fill()");
561         drawFillShape(shape, true);
562     }
563 
564     @Override
565     public boolean hit(Rectangle rect, Shape s, boolean onStroke)
566     {
567         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.hit()");
568         return false;
569     }
570 
571     @Override
572     public GraphicsConfiguration getDeviceConfiguration()
573     {
574         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.getDeviceConfiguration()");
575         return null;
576     }
577 
578     @Override
579     public void setComposite(Composite comp)
580     {
581         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.setComposite()");
582     }
583 
584     @Override
585     public void setPaint(Paint paint)
586     {
587         this.paint = paint;
588         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.setPaint()");
589     }
590 
591     @Override
592     public void setStroke(Stroke s)
593     {
594         this.stroke = s;
595         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.setStroke()");
596     }
597 
598     @Override
599     public void setRenderingHint(Key hintKey, Object hintValue)
600     {
601         this.renderingHints.put(hintKey, hintValue);
602         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.setRenderingHint()");
603     }
604 
605     @Override
606     public Object getRenderingHint(Key hintKey)
607     {
608         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.getRenderingHint()");
609         return this.renderingHints.get(hintKey);
610     }
611 
612     @Override
613     public void setRenderingHints(Map<?, ?> hints)
614     {
615         this.renderingHints.clear();
616         this.renderingHints.putAll(hints);
617         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.setRenderingHints()");
618     }
619 
620     @Override
621     public void addRenderingHints(Map<?, ?> hints)
622     {
623         this.renderingHints.putAll(hints);
624         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.addRenderingHints()");
625     }
626 
627     @Override
628     public RenderingHints getRenderingHints()
629     {
630         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.getRenderingHints()");
631         return this.renderingHints;
632     }
633 
634     @Override
635     public void translate(int x, int y)
636     {
637         this.affineTransform.translate(x, y);
638         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.translate()");
639     }
640 
641     @Override
642     public void translate(double tx, double ty)
643     {
644         this.affineTransform.translate(tx, ty);
645         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.translate()");
646     }
647 
648     @Override
649     public void rotate(double theta)
650     {
651         this.affineTransform.rotate(theta);
652         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.rotate()");
653     }
654 
655     @Override
656     public void rotate(double theta, double x, double y)
657     {
658         this.affineTransform.rotate(theta, x, y);
659         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.rotate()");
660     }
661 
662     @Override
663     public void scale(double sx, double sy)
664     {
665         this.affineTransform.scale(sx, sy);
666         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.scale()");
667     }
668 
669     @Override
670     public void shear(double shx, double shy)
671     {
672         this.affineTransform.shear(shx, shy);
673         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.shear()");
674     }
675 
676     @Override
677     public void transform(AffineTransform Tx)
678     {
679         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.transform()");
680     }
681 
682     @Override
683     public void setTransform(AffineTransform Tx)
684     {
685         this.affineTransform = (AffineTransform) Tx.clone();
686         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.setTransform()");
687     }
688 
689     @Override
690     public AffineTransform getTransform()
691     {
692         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.getTransform()");
693         return this.affineTransform;
694     }
695 
696     @Override
697     public Paint getPaint()
698     {
699         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.getPaint()");
700         return this.paint;
701     }
702 
703     @Override
704     public Composite getComposite()
705     {
706         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.getComposite()");
707         return this.composite;
708     }
709 
710     @Override
711     public void setBackground(Color color)
712     {
713         this.background = color;
714         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.setBackground()");
715     }
716 
717     @Override
718     public Color getBackground()
719     {
720         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.getBackground()");
721         return this.background;
722     }
723 
724     @Override
725     public Stroke getStroke()
726     {
727         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.getStroke()");
728         return this.stroke;
729     }
730 
731     @Override
732     public void clip(Shape s)
733     {
734         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.clip()");
735     }
736 
737     @Override
738     public FontRenderContext getFontRenderContext()
739     {
740         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.getFontRenderContext()");
741         return new FontRenderContext(this.affineTransform, true, true);
742     }
743 
744     @Override
745     public Graphics create()
746     {
747         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.create()");
748         return new HtmlGraphics2D(); // TODO: clone
749     }
750 
751     @Override
752     public Color getColor()
753     {
754         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.getColor()");
755         return this.color;
756     }
757 
758     @Override
759     public void setColor(Color c)
760     {
761         this.color = c;
762         this.paint = c; // TODO see how difference between paint and color should be handled
763         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.setColor()");
764     }
765 
766     @Override
767     public void setPaintMode()
768     {
769         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.setPaintMode()");
770     }
771 
772     @Override
773     public void setXORMode(Color c1)
774     {
775         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.setXORMode()");
776     }
777 
778     @Override
779     public Font getFont()
780     {
781         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.getFont()");
782         return this.font;
783     }
784 
785     @Override
786     public void setFont(Font font)
787     {
788         this.font = font;
789         this.fontMetrics = this.canvas.getFontMetrics(this.font);
790         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.setFont()");
791     }
792 
793     @Override
794     public FontMetrics getFontMetrics(Font f)
795     {
796         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.getFontMetrics()");
797         return this.fontMetrics;
798     }
799 
800     @Override
801     public Rectangle getClipBounds()
802     {
803         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.getClipBounds()");
804         return null;
805     }
806 
807     @Override
808     public void clipRect(int x, int y, int width, int height)
809     {
810         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.clipRect()");
811     }
812 
813     @Override
814     public void setClip(int x, int y, int width, int height)
815     {
816         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.setClip()");
817     }
818 
819     @Override
820     public Shape getClip()
821     {
822         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.getClip()");
823         return null;
824     }
825 
826     @Override
827     public void setClip(Shape clip)
828     {
829         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.setClip()");
830     }
831 
832     @Override
833     public void copyArea(int x, int y, int width, int height, int dx, int dy)
834     {
835         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.copyArea()");
836     }
837 
838     @Override
839     public void drawLine(int x1, int y1, int x2, int y2)
840     {
841         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.drawLine()");
842         addTransformDraw("drawLine", x1, y1, x2, y2);
843     }
844 
845     @Override
846     public void fillRect(int x, int y, int width, int height)
847     {
848         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.fillRect()");
849         addTransformFill("fillRect", x, y, width, height);
850     }
851 
852     @Override
853     public void clearRect(int x, int y, int width, int height)
854     {
855         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.clearRect()");
856         addTransformDraw("clearRect", x, y, width, height);
857     }
858 
859     @Override
860     public void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight)
861     {
862         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.drawRoundRect()");
863     }
864 
865     @Override
866     public void fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight)
867     {
868         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.fillRoundRect()");
869     }
870 
871     @Override
872     public void drawOval(int x, int y, int width, int height)
873     {
874         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.drawOval()");
875         addTransformDraw("drawOval", x, y, width, height);
876     }
877 
878     @Override
879     public void fillOval(int x, int y, int width, int height)
880     {
881         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.fillOval()");
882         addTransformFill("fillOval", x, y, width, height);
883     }
884 
885     @Override
886     public void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle)
887     {
888         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.drawArc()");
889     }
890 
891     @Override
892     public void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle)
893     {
894         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.fillArc()");
895     }
896 
897     @Override
898     public void drawPolyline(int[] xPoints, int[] yPoints, int nPoints)
899     {
900         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.fillPolyline()");
901     }
902 
903     @Override
904     public void drawPolygon(int[] xPoints, int[] yPoints, int nPoints)
905     {
906         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.drawPolygon()");
907     }
908 
909     @Override
910     public void fillPolygon(int[] xPoints, int[] yPoints, int nPoints)
911     {
912         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.fillPolygon()");
913     }
914 
915     @Override
916     public boolean drawImage(Image img, int x, int y, ImageObserver observer)
917     {
918         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.drawImage()");
919         return false;
920     }
921 
922     @Override
923     public boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer)
924     {
925         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.drawImage()");
926         return false;
927     }
928 
929     @Override
930     public boolean drawImage(Image img, int x, int y, Color bgcolor, ImageObserver observer)
931     {
932         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.drawImage()");
933         return false;
934     }
935 
936     @Override
937     public boolean drawImage(Image img, int x, int y, int width, int height, Color bgcolor, ImageObserver observer)
938     {
939         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.drawImage()");
940         return false;
941     }
942 
943     @Override
944     public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2,
945             ImageObserver observer)
946     {
947         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.drawImage()");
948         return false;
949     }
950 
951     @Override
952     public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, Color bgcolor,
953             ImageObserver observer)
954     {
955         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.drawImage()");
956         return false;
957     }
958 
959     @Override
960     public void dispose()
961     {
962         CategoryLogger.filter(Cat.WEB).trace("HtmlGraphics2D.dispose()");
963     }
964 
965 }