home *** CD-ROM | disk | FTP | other *** search
/ Apple Developer Connection Student Program / ADC Tools Sampler CD Disk 3 1999.iso / Metrowerks CodeWarrior / Java Support / Java_Source / IFC_112 / netscape / application / Graphics.java < prev    next >
Encoding:
Text File  |  1999-05-28  |  34.6 KB  |  1,086 lines  |  [TEXT/CWIE]

  1. // Graphics.java
  2. // By Ned Etcode
  3. // Copyright 1995, 1996, 1997 Netscape Communications Corp.  All rights reserved.
  4.  
  5. package netscape.application;
  6.  
  7. import netscape.util.*;
  8.  
  9. import java.lang.Thread;
  10.  
  11. /** Object subclass representing a drawing context, either onscreen or
  12.   * offscreen. Onscreen Graphics are created either for the Applet frame
  13.   * or ExternalWindows by passing a RootView to the constructor.
  14.   * Offscreen Graphics are created for Bitmaps, by passing the Bitmap to
  15.   * the constructor. Graphics maintain a stack of graphics states,
  16.   * manipulated by the <b>pushState()</b> and <b>popState()</b> methods. When a
  17.   * state is popped, the graphics state reverts to whatever it was before
  18.   * the most recent push (i.e. color, font, clip rectangle, etc).<p>
  19.   * You typically never construct Graphics instances directly, rather you
  20.   * work with them inside of View <b>drawView()</b> methods to draw to
  21.   * the screen.
  22.   * @note 1.0 minor fix to catch null object during graphic debugging
  23.   * @note 1.1 drawStringInRect() only does a fontMetrics.stringWidth() when necessary now
  24.   */
  25.  
  26. public class Graphics {
  27.     Vector              graphicsStates = new Vector();
  28.     java.awt.Graphics   primaryAwtGraphics;
  29.     java.awt.Graphics   currentAwtGraphics;
  30.     static int          graphicsCount = 0;
  31.     int                 graphicsID = graphicsCount++;
  32.     Rect                primaryClipRect;
  33.     Bitmap              buffer;
  34.  
  35.     /** String display style. */
  36.     public static final int     LEFT_JUSTIFIED = 0;
  37.     /** String display style. */
  38.     public static final int     CENTERED = 1;
  39.     /** String display style. */
  40.     public static final int     RIGHT_JUSTIFIED = 2;
  41.  
  42. /* constructors */
  43.  
  44.     /** Constructs a Graphics object suitable for drawing onscreen. It is
  45.       * untranslated, so the (0, 0) coordinate is in the upper-left
  46.       * corner of the RootView. You should explictly destroy Graphics
  47.       * objects, using the <b>dispose()</b> method, because they consume
  48.       * resources and are limited in number.
  49.       */
  50.     public Graphics(View view) {
  51.         Rect absoluteBounds = view.convertRectToView(null, view.bounds);
  52.  
  53.         absoluteBounds.x -= view.bounds.x;
  54.         absoluteBounds.y -= view.bounds.y;
  55.  
  56.         buffer = null;
  57.         init(absoluteBounds, view.rootView().panel.getGraphics());
  58.     }
  59.  
  60.     /** Constructs a Graphics object suitable for drawing offscreen. It is
  61.       * untranslated, so the (0, 0) coordinate is in the Bitmap's upper-left
  62.       * corner.
  63.       */
  64.     public Graphics(Bitmap aBitmap) {
  65.         buffer = aBitmap;
  66.         init(new Rect(0, 0, aBitmap.width(), aBitmap.height()),
  67.              aBitmap.awtImage.getGraphics());
  68.     }
  69.  
  70.     Graphics(Rect bounds, java.awt.Graphics awtGraphics) {
  71.         init(new Rect(bounds), awtGraphics);
  72.     }
  73.  
  74.     static Graphics newGraphics(View view) {
  75.         return debugViewCount() == 0 ? new Graphics(view)
  76.                                      : new DebugGraphics(view);
  77.     }
  78.  
  79.     static Graphics newGraphics(Bitmap bitmap) {
  80.         return debugViewCount() == 0 ? new Graphics(bitmap)
  81.                                      : new DebugGraphics(bitmap);
  82.     }
  83.  
  84. /* initializers */
  85.  
  86.      void init(Rect clipRect, java.awt.Graphics graphics) {
  87.         GraphicsState state;
  88.  
  89.         state = new GraphicsState();
  90.         graphicsStates.addElement(state);
  91.         state.absoluteClipRect = clipRect;
  92.         primaryAwtGraphics = graphics;
  93.         currentAwtGraphics = primaryAwtGraphics;
  94.         primaryAwtGraphics.clipRect(clipRect.x, clipRect.y, clipRect.width,
  95.                                     clipRect.height);
  96.         primaryClipRect = clipRect;
  97.         state.xOffset = clipRect.x;
  98.         state.yOffset = clipRect.y;
  99.     }
  100.  
  101.     /** Creates an entry in the Graphics state stack. Each call to
  102.       * <b>pushState()</b> should be paired with a call to <b>popState()</b>.
  103.       * Any changes to the Graphics object between the calls will be flushed
  104.       * after the <b>popState()</b>.
  105.       * @see #popState
  106.       */
  107.     public void pushState() {
  108.         GraphicsState lastState = state(), newState;
  109.  
  110.         if (lastState != null) {
  111.             newState = (GraphicsState)lastState.clone();
  112.         } else {
  113.             // THIS SHOULD NEVER HAPPEN ALERT!
  114.             newState = new GraphicsState();
  115.         }
  116.  
  117.         graphicsStates.addElement(newState);
  118.     }
  119.  
  120.     final void restoreAwtGraphics(java.awt.Graphics awtGraphics,
  121.                                           GraphicsState state) {
  122.         if (state.font != null) {
  123.             if (state.font.wasDownloaded())
  124.                 awtGraphics.setFont(null);
  125.             else
  126.                 awtGraphics.setFont(state.font._awtFont);
  127.         }
  128.         if (state.color == null) {
  129.             awtGraphics.setColor(null);
  130.         } else {
  131.             awtGraphics.setColor(state.color._color);
  132.             if (state.xorColor != null)
  133.                 awtGraphics.setXORMode(state.xorColor._color);
  134.             else
  135.                 awtGraphics.setPaintMode();
  136.         }
  137.     }
  138.  
  139.     /** Restores the Graphics object to its condition before the most recent
  140.       * <b>pushState()</b> call. All fonts, colors, clipping rectangles and
  141.       * translations will be restored.
  142.       */
  143.     public void popState() {
  144.         GraphicsState state = state();
  145.  
  146.         graphicsStates.removeLastElement();
  147.  
  148.         if (state.awtGraphics != null) {
  149.             state.awtGraphics.dispose();
  150.             currentAwtGraphics = awtGraphics();
  151.         }
  152.  
  153.         state = state();
  154.         if (state != null)
  155.             restoreAwtGraphics(currentAwtGraphics, state);
  156.     }
  157.  
  158.     final GraphicsState state() {
  159.         return (GraphicsState)graphicsStates.lastElement();
  160.     }
  161.  
  162.     java.awt.Graphics awtGraphics() {
  163.         int iState = graphicsStates.count();
  164.  
  165.         while (iState-- > 0) {
  166.             GraphicsState state;
  167.  
  168.             state = (GraphicsState)graphicsStates.elementAt(iState);
  169.             if (state.awtGraphics != null)
  170.                 return state.awtGraphics;
  171.         }
  172.         return primaryAwtGraphics;
  173.     }
  174.  
  175.     /** Sets the Font used for text drawing operations.
  176.       */
  177.     public void setFont(Font aFont) {
  178.         GraphicsState state = state();
  179.  
  180.         state.font = aFont;
  181.  
  182.         if (aFont == null || aFont.wasDownloaded())
  183.             currentAwtGraphics.setFont(null);
  184.         else
  185.             currentAwtGraphics.setFont(aFont._awtFont);
  186.     }
  187.  
  188.     /** Returns the Font used for drawing operations.
  189.       * @see #setFont
  190.       */
  191.     public Font font() {
  192.         return state().font;
  193.     }
  194.  
  195.     /** Sets the color to be used for drawing and filling lines and shapes.
  196.       */
  197.     public void setColor(Color aColor) {
  198.         GraphicsState state = state();
  199.  
  200.         state.color = aColor;
  201.         currentAwtGraphics.setColor(aColor == null ? null : aColor._color);
  202.     }
  203.  
  204.     /** Returns the color used for drawing lines and filling regions.
  205.       * @see #setColor
  206.       */
  207.     public Color color() {
  208.         return state().color;
  209.     }
  210.  
  211.     /** Alters the coordinate system so that all drawing, filling, and
  212.       * clipping operations implicitly have <b>x</b> and <b>y</b> added to
  213.       * their positions. If there is any current translation, the total
  214.       * translation is the sum of the old and new translations.
  215.       * @see #xTranslation
  216.       * @see #yTranslation
  217.       * @see #translation
  218.       */
  219.     public void translate(int x, int y) {
  220.         GraphicsState state = state();
  221.  
  222.         state.xOffset += x;
  223.         state.yOffset += y;
  224.         state.clipRect = null;
  225.     }
  226.  
  227.     /** Returns the X-coordinate of the Graphics' coordinate system origin.
  228.       * @see #translate
  229.       */
  230.     public int xTranslation() {
  231.         GraphicsState state = state();
  232.  
  233.         return state.xOffset;
  234.     }
  235.  
  236.     /** Returns the Y-coordinate of the Graphics' coordinate system origin.
  237.       * @see #translate
  238.       */
  239.     public int yTranslation() {
  240.         GraphicsState state = state();
  241.  
  242.         return state.yOffset;
  243.     }
  244.  
  245.     /** Returns the amount of translation performed on the current graphics
  246.       * context.  Each <b>pushState()</b> creates a context with no
  247.       * translation.  Subsequent calls to <b>translate</b> offset all
  248.       * drawing operations by the aggregate amount of translation.
  249.       * @see #translate
  250.       */
  251.     public Point translation() {
  252.         GraphicsState state = state();
  253.  
  254.         return new Point(state.xOffset, state.yOffset);
  255.     }
  256.  
  257.     /** Sets the rectangle within which drawing can occur.  Any drawing
  258.       * occurring outside of this rectangle will not appear. If
  259.       * <b>intersect</b> is <b>true</b>, this method intersects
  260.       * <b>rect</b> with the current clipping rectangle to produce the new
  261.       * clipping rectangle.
  262.       */
  263.     public void setClipRect(Rect rect, boolean intersect) {
  264.         GraphicsState state = state();
  265.         Rect clipRect, oldClipRect = absoluteClipRect(), newClipRect;
  266.  
  267.         if (rect == null) {
  268.             clipRect = primaryClipRect;
  269.             newClipRect = clipRect;
  270.             clipRect = new Rect(clipRect);
  271.         } else {
  272.             newClipRect = rect;
  273.             clipRect = new Rect(rect);
  274.             clipRect.moveBy(state.xOffset, state.yOffset);
  275.         }
  276.         if (intersect) {
  277.             clipRect.intersectWith(oldClipRect);
  278.         }
  279.  
  280.         if (!clipRect.equals(oldClipRect)) {
  281.  
  282.             if (!intersect && (state.awtGraphics != null)) {
  283.                 state.awtGraphics.dispose();
  284.                 state.awtGraphics = null;
  285.             }
  286.  
  287.             if (state.awtGraphics == null) {
  288.                 state.awtGraphics = primaryAwtGraphics.create();
  289.                 currentAwtGraphics = state.awtGraphics;
  290.  
  291.                 // hack to get around a bug in the interaction between
  292.                 // .../src/solaris/sun/sun/awt/motif/X11Graphics.java's
  293.                 // create() and setColor()
  294.  
  295.                 if (state.color != null) {
  296.                     currentAwtGraphics.setColor(Color.white._color);
  297.                     currentAwtGraphics.setColor(state.color._color);
  298.                 }
  299.             }
  300.  
  301.             currentAwtGraphics.clipRect(clipRect.x,
  302.                                         clipRect.y,
  303.                                         clipRect.width,
  304.                                         clipRect.height);
  305.             state.absoluteClipRect = clipRect;
  306.             state.clipRect = null;
  307.             restoreAwtGraphics(currentAwtGraphics, state);
  308.         }
  309.     }
  310.  
  311.     /** Sets the rectangle within which drawing can occur.  Any drawing
  312.       * occurring outside of this rectangle will not appear.  Intersects
  313.       * <b>rect</b> with the current clipping rectangle to produce the new
  314.       * clipping rectangle.
  315.       * @see #setClipRect(Rect, boolean)
  316.       */
  317.     public void setClipRect(Rect rect) {
  318.         setClipRect(rect, true);
  319.     }
  320.  
  321.     /** Returns the Graphics object's current clipping rectangle.  All drawing
  322.       * operations are clipped to this Rect.
  323.       * @see #setClipRect
  324.       */
  325.     public Rect clipRect() {
  326.         GraphicsState state = state();
  327.  
  328.         if (state.clipRect != null)
  329.             return state.clipRect;
  330.         else {
  331.             int iState = graphicsStates.count();
  332.  
  333.             while (iState-- > 0) {
  334.                 GraphicsState clipState
  335.                             = (GraphicsState)graphicsStates.elementAt(iState);
  336.                 if (clipState.absoluteClipRect != null) {
  337.                     state.clipRect = new Rect(clipState.absoluteClipRect);
  338.  
  339.                     state.clipRect.moveBy(-state.xOffset, -state.yOffset);
  340.                     return state.clipRect;
  341.                 }
  342.             }
  343.             return null;
  344.         }
  345.     }
  346.  
  347.     /** Returns the Graphics object's current clipping rectangle, in
  348.       * absolute coordinates, that is, those of the RootView or Bitmap the
  349.       * Graphics is drawing into.
  350.       * @see #clipRect
  351.       */
  352.     Rect absoluteClipRect() {
  353.         int iState = graphicsStates.count();
  354.  
  355.         while (iState-- > 0) {
  356.             GraphicsState state;
  357.  
  358.             state = (GraphicsState)graphicsStates.elementAt(iState);
  359.             if (state.absoluteClipRect != null) {
  360.                 return state.absoluteClipRect;
  361.             }
  362.         }
  363.         return null;
  364.     }
  365.  
  366.     /** Clears the clipping rectangle so that drawing can occur anywhere
  367.       * within the Graphics' drawing region.
  368.       * @see #setClipRect
  369.       * @see #clipRect
  370.       */
  371.     public void clearClipRect() {
  372.         setClipRect(null, false);
  373.     }
  374.  
  375.  
  376. /* attributes */
  377.  
  378.     /** Returns the Bitmap associated with the Graphics.  Returns
  379.       * <b>null</b> if the Graphics is not drawing into a Bitmap.
  380.       */
  381.     public Bitmap buffer() {
  382.         return buffer;
  383.     }
  384.  
  385.     /** Returns <b>true</b> if the Graphics was created from a Bitmap.
  386.       */
  387.     public boolean isDrawingBuffer() {
  388.         return buffer != null;
  389.     }
  390.  
  391.  
  392. /* actions */
  393.  
  394.     /** Destroys the Graphics object and any resources associated with it.
  395.       */
  396.     public void dispose() {
  397.         int iState = graphicsStates.count();
  398.  
  399. /* THIS SHOULD NEVER HAPPEN ALERT!
  400.         if (iState > 1) {
  401.             System.err.println("Disposing of graphics with states: " + this);
  402.             Thread.dumpStack();
  403.         }
  404. */
  405.         while (iState-- > 0) {
  406.             GraphicsState state;
  407.  
  408.             state = (GraphicsState) graphicsStates.elementAt(iState);
  409.             if (state.awtGraphics != null)
  410.                 state.awtGraphics.dispose();
  411.         }
  412.         graphicsStates.removeAllElements();
  413.         primaryAwtGraphics.dispose();
  414.         primaryAwtGraphics = null;
  415.         currentAwtGraphics = null;
  416.     }
  417.  
  418.     /** Forces any pending drawing operations to be sent to the native
  419.       * graphics system for onscreen drawing.
  420.       */
  421.     public void sync() {
  422.         Application.application().syncGraphics();
  423.     }
  424.  
  425.  
  426. /* color, paint mode */
  427.  
  428.     /** Sets the paint mode to overwrite the destination with the current
  429.       * color.
  430.       * @see #setXORMode
  431.       */
  432.     public void setPaintMode() {
  433.         GraphicsState state = state();
  434.  
  435.         state.xorColor = null;
  436.         currentAwtGraphics.setPaintMode();
  437.     }
  438.  
  439.     /** Sets the paint mode to alternate between the current color and
  440.       * <b>aColor</b>.
  441.       * @see #setPaintMode
  442.       */
  443.     public void setXORMode(Color aColor) {
  444.         if (aColor == null)
  445.             setPaintMode();
  446.         else {
  447.             GraphicsState state = state();
  448.  
  449.             state.xorColor = aColor;
  450.             currentAwtGraphics.setXORMode(aColor._color);
  451.         }
  452.     }
  453.  
  454.  
  455. /* rects */
  456.  
  457.     /** Draws <b>aRect</b> using the current color.
  458.       */
  459.     public void drawRect(Rect aRect) {
  460.         drawRect(aRect.x, aRect.y, aRect.width, aRect.height);
  461.     }
  462.  
  463.     /** Draws the rectangle
  464.       * (<b>x</b>, <b>y</b>, <b>width</b>, <b>height</b>) using
  465.       * the current color.
  466.       */
  467.     public void drawRect(int x, int y, int width, int height) {
  468.         GraphicsState state = state();
  469.  
  470.         x += state.xOffset;
  471.         y += state.yOffset;
  472.  
  473.        currentAwtGraphics.drawRect(x, y, width - 1, height - 1);
  474.     }
  475.  
  476.     /** Fills <b>aRect</b> using the current color.
  477.       */
  478.     public void fillRect(Rect aRect) {
  479.         fillRect(aRect.x, aRect.y, aRect.width, aRect.height);
  480.     }
  481.  
  482.     /** Fills the rectangle
  483.       * (<b>x</b>, <b>y</b>, <b>width</b>, <b>height</b>) using the current
  484.       * color.
  485.       */
  486.     public void fillRect(int x, int y, int width, int height) {
  487.         GraphicsState state = state();
  488.  
  489.         x += state.xOffset;
  490.         y += state.yOffset;
  491.         currentAwtGraphics.fillRect(x, y, width, height);
  492.     }
  493.  
  494. /* rounded rects */
  495.  
  496.     /** Draws a rectangle with rounded corners in <b>aRect</b>,
  497.       * as determined by <b>arcWidth</b> and <b>arcHeight</b>.
  498.       */
  499.     public void drawRoundedRect(Rect aRect, int arcWidth, int arcHeight) {
  500.         drawRoundedRect(aRect.x, aRect.y, aRect.width, aRect.height,
  501.                         arcWidth, arcHeight);
  502.     }
  503.  
  504.     /** Draws a rectangle with rounded corners in the rectangle
  505.       * (<b>x</b>, <b>y</b>, <b>width</b>, <b>height</b>), as determined by
  506.       * <b>arcWidth</b> and <b>arcHeight</b>.
  507.       */
  508.     public void drawRoundedRect(int x, int y, int width, int height,
  509.                                 int arcWidth, int arcHeight) {
  510.         GraphicsState state = state();
  511.  
  512.         x += state.xOffset;
  513.         y += state.yOffset;
  514.         currentAwtGraphics.drawRoundRect(x, y, width - 1, height - 1,
  515.                                          arcWidth, arcHeight);
  516.     }
  517.  
  518.     /** Fills a rectangle with rounded corners in <b>aRect</b>,
  519.       * as determined by <b>arcWidth</b> and <b>arcHeight</b>.
  520.       */
  521.     public void fillRoundedRect(Rect aRect, int arcWidth, int arcHeight) {
  522.         fillRoundedRect(aRect.x, aRect.y, aRect.width, aRect.height,
  523.                          arcWidth, arcHeight);
  524.     }
  525.  
  526.     /** Fills a rectangle with rounded corners in the rectangle
  527.       * (<b>x</b>, <b>y</b>, <b>width</b>, <b>height</b>), as determined by
  528.       * <b>arcWidth</b> and <b>arcHeight</b>.
  529.       */
  530.     public void fillRoundedRect(int x, int y, int width, int height,
  531.                                 int arcWidth, int arcHeight) {
  532.         GraphicsState state = state();
  533.  
  534.         x += state.xOffset;
  535.         y += state.yOffset;
  536.         currentAwtGraphics.fillRoundRect(x, y, width, height, arcWidth,
  537.                                          arcHeight);
  538.     }
  539.  
  540.  
  541.  
  542. /* lines */
  543.  
  544.     /** Draws a line from the point (<b>x1</b>, <b>y1</b>) to the
  545.       * point (<b>x2</b>, <b>y2</b>) in the current color.
  546.       */
  547.     public void drawLine(int x1, int y1, int x2, int y2) {
  548.         GraphicsState state = state();
  549.  
  550.         x1 += state.xOffset;
  551.         y1 += state.yOffset;
  552.         x2 += state.xOffset;
  553.         y2 += state.yOffset;
  554.         currentAwtGraphics.drawLine(x1, y1, x2, y2);
  555.     }
  556.  
  557.  
  558.  
  559. /* points */
  560.  
  561.     /** Draws the point (<b>x</b>, <b>y</b>) in the current color.
  562.       */
  563.     public void drawPoint(int x, int y) {
  564.         GraphicsState state = state();
  565.  
  566.         x += state.xOffset;
  567.         y += state.yOffset;
  568.         currentAwtGraphics.drawLine(x, y, x, y);
  569.     }
  570.  
  571. /* ovals */
  572.  
  573.     /** Draws an oval inside <b>aRect</b> in the current color.
  574.       */
  575.     public void drawOval(Rect aRect) {
  576.         drawOval(aRect.x, aRect.y, aRect.width - 1, aRect.height - 1);
  577.     }
  578.  
  579.     /** Draws an oval inside the rect
  580.       * (<b>x</b>, <b>y</b>, <b>width</b>, <b>height</b>) in the
  581.       * current color.
  582.       */
  583.     public void drawOval(int x, int y, int width, int height) {
  584.         GraphicsState state = state();
  585.  
  586.         x += state.xOffset;
  587.         y += state.yOffset;
  588.         currentAwtGraphics.drawOval(x, y, width, height);
  589.     }
  590.  
  591.     /** Fills an oval inside <b>aRect</b> in the current color.
  592.       */
  593.     public void fillOval(Rect aRect) {
  594.         fillOval(aRect.x, aRect.y, aRect.width, aRect.height);
  595.     }
  596.  
  597.     /** Fills an oval inside the rect
  598.       * (<b>x</b>, <b>y</b>, <b>width</b>, <b>height</b>) in the
  599.       * current color.
  600.       */
  601.     public void fillOval(int x, int y, int width, int height) {
  602.         GraphicsState state = state();
  603.  
  604.         x += state.xOffset;
  605.         y += state.yOffset;
  606.         currentAwtGraphics.fillOval(x, y, width, height);
  607.     }
  608.  
  609.  
  610. /* arcs */
  611.  
  612.     /** Draws an arc in <b>aRect</b> from <b>startAngle</b> for
  613.       * <b>arcAngle</b> degrees.
  614.       */
  615.     public void drawArc(Rect aRect, int startAngle, int arcAngle) {
  616.         drawArc(aRect.x, aRect.y, aRect.width, aRect.height,
  617.                 startAngle, arcAngle);
  618.     }
  619.  
  620.     /** Draws an arc in <b>aRect</b> from <b>startAngle</b> for
  621.       * <b>arcAngle</b> degrees.
  622.       */
  623.     public void drawArc(int x, int y, int width, int height, int startAngle,
  624.                         int arcAngle) {
  625.         GraphicsState state = state();
  626.  
  627.         x += state.xOffset;
  628.         y += state.yOffset;
  629.         currentAwtGraphics.drawArc(x, y, width, height, startAngle, arcAngle);
  630.     }
  631.  
  632.     /** Fills an arc in <b>aRect</b> from <b>startAngle</b> for
  633.       * <b>arcAngle</b> degrees.
  634.       */
  635.     public void fillArc(Rect aRect, int startAngle, int arcAngle) {
  636.        fillArc(aRect.x, aRect.y, aRect.width, aRect.height,
  637.                     startAngle, arcAngle);
  638.     }
  639.  
  640.     /** Fills an arc in <b>aRect</b> from <b>startAngle</b> for
  641.       * <b>arcAngle</b> degrees.
  642.       */
  643.     public void fillArc(int x, int y, int width, int height, int startAngle,
  644.                         int arcAngle) {
  645.         GraphicsState state = state();
  646.  
  647.         x += state.xOffset;
  648.         y += state.yOffset;
  649.         currentAwtGraphics.fillArc(x, y, width, height, startAngle, arcAngle);
  650.     }
  651.  
  652.     /** Draws the polygon described by <b>xPoints</b> and <b>yPoints</b>
  653.       * using the current color. Both arrays must have at least
  654.       * <b>nPoints</b> integers.
  655.       */
  656.     public void drawPolygon(int xPoints[], int yPoints[], int nPoints) {
  657.         GraphicsState state = state();
  658.  
  659.         // This avoids allocating translated arrays of points
  660.         if (state.xOffset != 0 || state.yOffset != 0) {
  661.             currentAwtGraphics.translate(state.xOffset, state.yOffset);
  662.             currentAwtGraphics.drawPolygon(xPoints, yPoints, nPoints);
  663.             currentAwtGraphics.translate(-state.xOffset, -state.yOffset);
  664.         } else
  665.             currentAwtGraphics.drawPolygon(xPoints, yPoints, nPoints);
  666.     }
  667.  
  668.     /** Draws <b>polygon</b> using the current color.
  669.       */
  670.     public void drawPolygon(Polygon polygon) {
  671.         drawPolygon(polygon.xPoints, polygon.yPoints, polygon.numPoints);
  672.     }
  673.  
  674.     /** Fills the polygon described by <b>xPoints</b> and <b>yPoints</b>
  675.       * using the current color. Both arrays must have at least
  676.       * <b>nPoints</b> integers.
  677.       */
  678.     public void fillPolygon(int xPoints[], int yPoints[], int nPoints) {
  679.         GraphicsState state = state();
  680.  
  681.         // This avoids allocating translated arrays of points
  682.         if (state.xOffset != 0 || state.yOffset != 0) {
  683.             currentAwtGraphics.translate(state.xOffset, state.yOffset);
  684.             currentAwtGraphics.fillPolygon(xPoints, yPoints, nPoints);
  685.             currentAwtGraphics.translate(-state.xOffset, -state.yOffset);
  686.         } else
  687.             currentAwtGraphics.fillPolygon(xPoints, yPoints, nPoints);
  688.     }
  689.  
  690.     /** Fills <b>polygon</b> using the current color.
  691.       */
  692.     public void fillPolygon(Polygon polygon) {
  693.         fillPolygon(polygon.xPoints, polygon.yPoints, polygon.numPoints);
  694.     }
  695.  
  696.     /** Displays <b>bitmap</b> at the point (<b>x</b>, <b>y</b>).
  697.       */
  698.     public void drawBitmapAt(Bitmap bitmap, int x, int y) {
  699.         GraphicsState state = state();
  700.  
  701.         x += state.xOffset;
  702.         y += state.yOffset;
  703.  
  704.         if (bitmap == null) {
  705.             return;
  706.         }
  707.  
  708.         if (!bitmap.loadsIncrementally()) {
  709.             bitmap.loadData();
  710.         }
  711.         if (!bitmap.isValid()) {
  712.             System.err.println("Graphics.drawBitmapAt() - Invalid bitmap: " +
  713.                                bitmap.name());
  714.             return;
  715.         }
  716.  
  717.         currentAwtGraphics.drawImage(bitmap.awtImage, x, y,
  718.                                      bitmap.bitmapObserver());
  719.     }
  720.  
  721.     /** Draws <b>bitmap</b> at the point (<b>x</b>, <b>y</b>), scaled in the
  722.       * X-dimension to have width <b>width</b> and the Y-dimension to
  723.       * have height <b>height</b>.
  724.       */
  725.     public void drawBitmapScaled(Bitmap bitmap, int x, int y, int width,
  726.         int height) {
  727.         GraphicsState state = state();
  728.  
  729.         if (bitmap == null) {
  730.             return;
  731.         }
  732.  
  733.         x += state.xOffset;
  734.         y += state.yOffset;
  735.  
  736.         bitmap.createScaledVersion(width, height);
  737.  
  738.         if (!bitmap.isValid()) {
  739.             System.err.println(
  740.                         "Graphics.drawBitmapScaled() - Invalid bitmap: " +
  741.                         bitmap.name());
  742.             return;
  743.         }
  744.  
  745.         currentAwtGraphics.drawImage(bitmap.awtImage, x, y, width, height,
  746.                                      bitmap.bitmapObserver());
  747.     }
  748.  
  749.     static int computeStringWidth(FontMetrics fm, String str) {
  750.        return fm.stringWidth(str);
  751.  
  752.         // This is faster, but may be unreliable under the
  753.         // crushing blow of i18n
  754. //        int i,c;
  755. //        int result = 0;
  756. //        char ch;
  757. //        for(i=0, c=str.length() ; i < c ; i++) {
  758. //            ch = str.charAt(i);
  759. //            if(ch > 255)
  760. //                return fm.stringWidth(str);
  761. //            else
  762. //                result += fm.charWidth((int)ch);
  763. //        }
  764. //        return result;
  765.     }
  766.  
  767.     /** Draws <b>aString</b> in the rectangle defined by
  768.       * (<b>x</b>, <b>y</b>, <b>width</b>, <b>height</b>).
  769.       * <b>justification</b> specifies the text's justification, one of
  770.       * LEFT_JUSTIFIED, CENTERED, or RIGHT_JUSTIFIED.
  771.       * <b>drawStringInRect()</b> does not clip to the rectangle, but instead
  772.       * uses this rectangle and the desired justification to compute the point
  773.       * at which to begin drawing the text.
  774.       * @see #drawString
  775.       */
  776.     public void drawStringInRect(String aString, int x, int y,
  777.                                  int width, int height, int justification) {
  778.         FontMetrics  fontMetrics;
  779.         int          drawWidth, startX, startY, delta;
  780.  
  781.         if (font() == null) {
  782.             throw new InconsistencyException("No font set");
  783.         }
  784.         fontMetrics = font().fontMetrics();
  785.         if (fontMetrics == null) {
  786.             throw new InconsistencyException("No metrics for Font " + font());
  787.         }
  788.  
  789. //        drawWidth = fontMetrics.stringWidth(aString);
  790. //        if (drawWidth > width) {
  791. //            drawWidth = width;
  792. //        }
  793.  
  794.         if (justification == CENTERED) {
  795.             drawWidth = computeStringWidth(fontMetrics, aString);
  796.             if (drawWidth > width) {
  797.                 drawWidth = width;
  798.             }
  799.             startX = x + (width - drawWidth) / 2;
  800.         } else if (justification == RIGHT_JUSTIFIED) {
  801.             drawWidth = computeStringWidth(fontMetrics, aString);
  802.             if (drawWidth > width) {
  803.                 drawWidth = width;
  804.             }
  805.             startX = x + width - drawWidth;
  806.         } else {
  807.             startX = x;
  808.         }
  809.  
  810.         delta = (height - fontMetrics.ascent() - fontMetrics.descent()) / 2;
  811.         if (delta < 0) {
  812.             delta = 0;
  813.         }
  814.  
  815.         startY = y + height - delta - fontMetrics.descent();
  816.  
  817.         drawString(aString, startX, startY);
  818.     }
  819.  
  820.     /** Draws <b>aString</b> in <b>aRect</b>, according to
  821.       * the justification <b>justification</b>.
  822.       * @see #drawStringInRect(String, Font, int, int, int, int, int)
  823.       */
  824.     public void drawStringInRect(String aString,
  825.                                  Rect aRect,
  826.                                  int justification) {
  827.         if (aRect == null) {
  828.             throw new InconsistencyException(
  829.                                     "Null Rect passed to drawStringInRect.");
  830.         }
  831.  
  832.         drawStringInRect(aString, aRect.x, aRect.y, aRect.width,
  833.                          aRect.height, justification);
  834.     }
  835.  
  836.     /** Draws <b>aString</b> at the point (<b>x</b>, <b>y</b>) using the
  837.       * current font and color.
  838.       * @see #drawStringInRect
  839.       */
  840.     public void drawString(String aString, int x, int y) {
  841.         Font                 font;
  842.         FontMetrics          fontMetrics;
  843.         Vector               glyphVector;
  844.         int                  widthsArray[], i, widthsArrayBase, index;
  845.  
  846.         if (aString == null) {
  847.             throw new InconsistencyException(
  848.                                     "Null String passed to drawString.");
  849.         }
  850.  
  851.         font = font();
  852.  
  853.         if (font == null || !font.wasDownloaded()) {
  854.             GraphicsState state = state();
  855.  
  856.             x += state.xOffset;
  857.             y += state.yOffset;
  858.             currentAwtGraphics.drawString(aString, x, y);
  859.         } else {
  860.             fontMetrics = font.fontMetrics();
  861.             widthsArray = fontMetrics.widthsArray();
  862.             widthsArrayBase = fontMetrics.widthsArrayBase();
  863.             glyphVector = font.glyphVector();
  864.  
  865.             y -= fontMetrics.ascent();
  866.  
  867.             for (i = 0; i < aString.length(); i++) {
  868.                 index = (int)aString.charAt(i) - widthsArrayBase;
  869.                 if (index < 0 ||
  870.                     index >= widthsArray.length - widthsArrayBase) {
  871.                     if (aString.charAt(i) == ' ') {
  872.                         x += widthsArray[' '];
  873.                     }
  874.                     continue;
  875.                 }
  876.  
  877.                 drawBitmapAt((Bitmap)glyphVector.elementAt(index), x, y);
  878.                 x += widthsArray[(int)aString.charAt(i)];
  879.             }
  880.         }
  881.     }
  882.  
  883.     /** Draws <b>length</b> bytes of <b>data</b>, beginning <b>offset</b> bytes
  884.       * into the array, using the current font and color, at the point
  885.       * (<b>x</b>, <b>y</b>).
  886.       */
  887.     public void drawBytes(byte data[], int offset, int length, int x, int y) {
  888.         Font font = font();
  889.         FontMetrics          fontMetrics;
  890.         Vector               glyphVector;
  891.         int                     widthsArray[], i, widthsArrayBase, index;
  892.  
  893.         if (font == null || !font.wasDownloaded()) {
  894.             GraphicsState state = state();
  895.  
  896.             x += state.xOffset;
  897.             y += state.yOffset;
  898.             currentAwtGraphics.drawBytes(data, offset, length, x, y);
  899.         } else {
  900.             fontMetrics = font.fontMetrics();
  901.             widthsArray = fontMetrics.widthsArray();
  902.             widthsArrayBase = fontMetrics.widthsArrayBase();
  903.             glyphVector = font.glyphVector();
  904.  
  905.             y -= fontMetrics.ascent();
  906.  
  907.             for (i = 0; i < length; i++) {
  908.                 index = (int)data[i] - widthsArrayBase;
  909.                 if (index < 0 ||
  910.                     index >= widthsArray.length - widthsArrayBase) {
  911.                     if ((char)data[i] == ' ') {
  912.                         x += widthsArray[' '];
  913.                     }
  914.                     continue;
  915.                 }
  916.  
  917.                 drawBitmapAt((Bitmap)glyphVector.elementAt(index), x, y);
  918.                 x += widthsArray[(int)data[i]];
  919.             }
  920.         }
  921.     }
  922.  
  923.     /** Draws <b>length</b> characters of <b>data</b>, beginning <b>offset</b>
  924.       * characters into the array, using the current font and color, at the
  925.       * point (<b>x</b>, <b>y</b>).
  926.       */
  927.     public void drawChars(char data[], int offset, int length, int x, int y) {
  928.         Font font = font();
  929.         FontMetrics          fontMetrics;
  930.         Vector               glyphVector;
  931.         int                     widthsArray[], i, widthsArrayBase, index;
  932.  
  933.         if (font == null) {
  934.             return;
  935.         } else if (!font.wasDownloaded()) {
  936.             GraphicsState state = state();
  937.  
  938.             x += state.xOffset;
  939.             y += state.yOffset;
  940.             currentAwtGraphics.drawChars(data, offset, length, x, y);
  941.         } else {
  942.             fontMetrics = font.fontMetrics();
  943.             widthsArray = fontMetrics.widthsArray();
  944.             widthsArrayBase = fontMetrics.widthsArrayBase();
  945.             glyphVector = font.glyphVector();
  946.  
  947.             y -= fontMetrics.ascent();
  948.  
  949.             for (i = 0; i < length; i++) {
  950.                 index = (int)data[i] - widthsArrayBase;
  951.                 if (index < 0 ||
  952.                     index >= widthsArray.length - widthsArrayBase) {
  953.                     if ((char)data[i] == ' ') {
  954.                         x += widthsArray[' '];
  955.                     }
  956.                     continue;
  957.                 }
  958.  
  959.                 drawBitmapAt((Bitmap)glyphVector.elementAt(index), x, y);
  960.                 x += widthsArray[(int)data[i]];
  961.             }
  962.         }
  963.     }
  964.  
  965.     void copyArea(int x, int y, int width, int height, int destX, int destY) {
  966.         GraphicsState state = state();
  967.  
  968.         x += state.xOffset;
  969.         y += state.yOffset;
  970.         destX += state.xOffset;
  971.         destY += state.yOffset;
  972.         currentAwtGraphics.copyArea(x, y, width, height, destX - x, destY - y);
  973.     }
  974.  
  975.     /** Returns the Graphic's String representation.
  976.       */
  977.     public String toString() {
  978.         StringBuffer stringBuffer;
  979.         String          tmpString;
  980.         int             iState = graphicsStates.count();
  981.  
  982.         tmpString = (buffer != null) ? " for Bitmap " : " ";
  983.         stringBuffer = new StringBuffer("Graphics" + tmpString + iState +
  984.                                   " states:\n");
  985.         while (iState-- > 0) {
  986.             GraphicsState state;
  987.  
  988.             state = (GraphicsState)graphicsStates.elementAt(iState);
  989.             stringBuffer.append(state.toString());
  990.             stringBuffer.append("\n");
  991.         }
  992.         return stringBuffer.toString();
  993.     }
  994.  
  995.     String toShortString() {
  996.         StringBuffer buffer = new StringBuffer("Graphics" + (isDrawingBuffer() ? "<B>" : "") + "(" + graphicsID + ")");
  997.         int stateDepth = graphicsStates.count();
  998.         while (stateDepth-- > 0)
  999.             buffer.append("-");
  1000.         buffer.append(">");
  1001.         return buffer.toString();
  1002.     }
  1003.  
  1004.     /** Activates Graphics debugging options.  Graphics instances only support
  1005.       * a <b>debugOption</b> value of <b>0</b>.
  1006.       * The DebugGraphics subclass supports other values to enable diagnostic
  1007.       * output on graphics operations.
  1008.       * @see DebugGraphics
  1009.       */
  1010.     public void setDebugOptions(int debugOptions) {
  1011.         if (debugOptions != 0) {
  1012.             throw new InconsistencyException("Can't set non zero debugOptions on a Graphics.  Use DebugGraphics instead.");
  1013.         }
  1014.     }
  1015.  
  1016.     /** Returns the current debug option setting.  Graphics instances only
  1017.       * a debug option value of <b>0</b>.
  1018.       * The DebugGraphics subclass supports other values to enable diagnostic
  1019.       * output on graphics operations.
  1020.       * @see #setDebugOptions
  1021.       * @see DebugGraphics
  1022.       */
  1023.     public int debugOptions() {
  1024.         return 0;
  1025.     }
  1026.  
  1027.     static DebugGraphicsInfo info() {
  1028.         Application app = Application.application();
  1029.  
  1030.         if (app.debugGraphicsInfo == null) {
  1031.             app.debugGraphicsInfo = new DebugGraphicsInfo();
  1032.         }
  1033.         return app.debugGraphicsInfo;
  1034.     }
  1035.  
  1036.     void setDebug(View view) {
  1037.     }
  1038.  
  1039.     static void setViewDebug(View view, int debugOptions) {
  1040.         DebugGraphicsInfo info = info();
  1041.  
  1042.         info.setViewDebug(view, debugOptions);
  1043.     }
  1044.  
  1045.     static int shouldViewDebug(View view) {
  1046.         Application app = Application.application();
  1047.  
  1048.         if (app.debugGraphicsInfo == null) {
  1049.             return 0;
  1050.         } else {
  1051.             DebugGraphicsInfo info = app.debugGraphicsInfo;
  1052.             int debugOptions = 0;
  1053.  
  1054.             while (view != null) {
  1055.                 debugOptions |= info.viewDebug(view);
  1056.                 view = view._superview;
  1057.             }
  1058.  
  1059.             return debugOptions;
  1060.         }
  1061.     }
  1062.  
  1063.     static int viewDebug(View view) {
  1064.         Application app = Application.application();
  1065.  
  1066.         if (app.debugGraphicsInfo == null) {
  1067.             return 0;
  1068.         } else {
  1069.             DebugGraphicsInfo info = app.debugGraphicsInfo;
  1070.  
  1071.             return info.viewDebug(view);
  1072.         }
  1073.     }
  1074.  
  1075.     static int debugViewCount() {
  1076.         Application app = Application.application();
  1077.  
  1078.         if (app != null && app.debugGraphicsInfo != null &&
  1079.             app.debugGraphicsInfo.viewToDebug != null) {
  1080.             return app.debugGraphicsInfo.viewToDebug.count();
  1081.         } else {
  1082.             return 0;
  1083.         }
  1084.     }
  1085. }
  1086.