Package org.newdawn.slick

Source Code of org.newdawn.slick.Graphics

package org.newdawn.slick;

import java.nio.ByteBuffer;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;

import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.newdawn.slick.geom.Rectangle;
import org.newdawn.slick.geom.Shape;
import org.newdawn.slick.geom.ShapeRenderer;
import org.newdawn.slick.opengl.TextureImpl;
import org.newdawn.slick.opengl.renderer.LineStripRenderer;
import org.newdawn.slick.opengl.renderer.Renderer;
import org.newdawn.slick.opengl.renderer.SGL;
import org.newdawn.slick.util.FastTrig;
import org.newdawn.slick.util.Log;

/**
* A graphics context that can be used to render primatives to the accelerated
* canvas provided by LWJGL.
*
* @author kevin
*/
public class Graphics {
  /** The renderer to use for all GL operations */
  protected static SGL GL = Renderer.get();
  /** The renderer to use line strips */
  private static LineStripRenderer LSR = Renderer.getLineStripRenderer();

  /** The normal drawing mode */
  public static int MODE_NORMAL = 1;

  /** Draw to the alpha map */
  public static int MODE_ALPHA_MAP = 2;

  /** Draw using the alpha blending */
  public static int MODE_ALPHA_BLEND = 3;

  /** Draw multiplying the source and destination colours */
  public static int MODE_COLOR_MULTIPLY = 4;
 
  /** Draw adding the existing colour to the new colour */
  public static int MODE_ADD = 5;
 
  /** Draw blending the new image into the old one by a factor of it's colour */
  public static int MODE_SCREEN = 6;
 
  /** The default number of segments that will be used when drawing an oval */
  private static final int DEFAULT_SEGMENTS = 50;

  /** The last graphics context in use */
  protected static Graphics currentGraphics = null;
 
  /** The default font to use */
  protected static Font DEFAULT_FONT;

  /** The last set scale */
  private float sx = 1;
  /** The last set scale */
  private float sy = 1;
 
  /**
   * Set the current graphics context in use
   *
   * @param current The graphics context that should be considered current
   */
  public static void setCurrent(Graphics current) {
    if (currentGraphics != current) {
      if (currentGraphics != null) {
        currentGraphics.disable();
      }
      currentGraphics = current;
      currentGraphics.enable();
    }
  }
 
  /** The font in use */
  private Font font;

  /** The current color */
  private Color currentColor = Color.white;

  /** The width of the screen */
  protected int screenWidth;

  /** The height of the screen */
  protected int screenHeight;

  /** True if the matrix has been pushed to the stack */
  private boolean pushed;

  /** The graphics context clipping */
  private Rectangle clip;

  /** Buffer used for setting the world clip */
  private DoubleBuffer worldClip = BufferUtils.createDoubleBuffer(4);

  /** The buffer used to read a screen pixel */
  private ByteBuffer readBuffer = BufferUtils.createByteBuffer(4);

  /** True if we're antialias */
  private boolean antialias;

  /** The world clip recorded since last set */
  private Rectangle worldClipRecord;

  /** The current drawing mode */
  private int currentDrawingMode = MODE_NORMAL;

  /** The current line width */
  private float lineWidth = 1;

  /** The matrix stack */
  private ArrayList stack = new ArrayList();
  /** The index into the stack we're using */
  private int stackIndex;

  /**
   * Default constructor for sub-classes
   */
  public Graphics() { 
  }
 
  /**
   * Create a new graphics context. Only the container should be doing this
   * really
   *
   * @param width
   *            The width of the screen for this context
   * @param height
   *            The height of the screen for this context
   */
  public Graphics(int width, int height) {
    if (DEFAULT_FONT == null) {
      AccessController.doPrivileged(new PrivilegedAction() {
        public Object run() {
          try {
            DEFAULT_FONT = new AngelCodeFont(
                "org/newdawn/slick/data/defaultfont.fnt",
                "org/newdawn/slick/data/defaultfont.png");
          } catch (SlickException e) {
            Log.error(e);
          }
          return null; // nothing to return
        }
      });
    }
   
    this.font = DEFAULT_FONT;
    screenWidth = width;
    screenHeight = height;
  }

  /**
   * Set the dimensions considered by the graphics context
   *
   * @param width The width of the graphics context
   * @param height The height of the graphics context
   */
  void setDimensions(int width, int height) {
    screenWidth = width;
    screenHeight = height;
  }
 
  /**
   * Set the drawing mode to use. This mode defines how pixels are drawn to
   * the graphics context. It can be used to draw into the alpha map.
   *
   * The mode supplied should be one of {@link Graphics#MODE_NORMAL} or
   * {@link Graphics#MODE_ALPHA_MAP} or {@link Graphics#MODE_ALPHA_BLEND}
   *
   * @param mode
   *            The mode to apply.
   */
  public void setDrawMode(int mode) {
    predraw();
    currentDrawingMode = mode;
    if (currentDrawingMode == MODE_NORMAL) {
      GL.glEnable(SGL.GL_BLEND);
      GL.glColorMask(true, true, true, true);
      GL.glBlendFunc(SGL.GL_SRC_ALPHA, SGL.GL_ONE_MINUS_SRC_ALPHA);
    }
    if (currentDrawingMode == MODE_ALPHA_MAP) {
      GL.glDisable(SGL.GL_BLEND);
      GL.glColorMask(false, false, false, true);
    }
    if (currentDrawingMode == MODE_ALPHA_BLEND) {
      GL.glEnable(SGL.GL_BLEND);
      GL.glColorMask(true, true, true, false);
      GL.glBlendFunc(GL11.GL_DST_ALPHA, GL11.GL_ONE_MINUS_DST_ALPHA);
    }
    if (currentDrawingMode == MODE_COLOR_MULTIPLY) {
      GL.glEnable(SGL.GL_BLEND);
      GL.glColorMask(true, true, true, true);
      GL.glBlendFunc(GL11.GL_ONE_MINUS_SRC_COLOR, GL11.GL_SRC_COLOR);
    }
    if (currentDrawingMode == MODE_ADD) {
      GL.glEnable(SGL.GL_BLEND);
      GL.glColorMask(true, true, true, true);
      GL.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE);
    }
    if (currentDrawingMode == MODE_SCREEN) {
      GL.glEnable(SGL.GL_BLEND);
      GL.glColorMask(true, true, true, true);
      GL.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_COLOR);
    }
    postdraw();
  }

  /**
   * Clear the state of the alpha map across the entire screen. This sets
   * alpha to 0 everywhere, meaning in {@link Graphics#MODE_ALPHA_BLEND}
   * nothing will be drawn.
   */
  public void clearAlphaMap() {
    pushTransform();
    GL.glLoadIdentity();
   
    int originalMode = currentDrawingMode;
    setDrawMode(MODE_ALPHA_MAP);
    setColor(new Color(0,0,0,0));
    fillRect(0, 0, screenWidth, screenHeight);
    setColor(currentColor);
    setDrawMode(originalMode);
   
    popTransform();
  }

  /**
   * Must be called before all OpenGL operations to maintain context for
   * dynamic images
   */
  private void predraw() {
    setCurrent(this);
  }

  /**
   * Must be called after all OpenGL operations to maintain context for
   * dynamic images
   */
  private void postdraw() {
  }

  /**
   * Enable rendering to this graphics context
   */
  protected void enable() {
  }

  /**
   * Flush this graphics context to the underlying rendering context
   */
  public void flush() {
    if (currentGraphics == this) {
      currentGraphics.disable();
      currentGraphics = null;
    }
  }

  /**
   * Disable rendering to this graphics context
   */
  protected void disable() {
  }

  /**
   * Get the current font
   *
   * @return The current font
   */
  public Font getFont() {
    return font;
  }

  /**
   * Set the background colour of the graphics context
   *
   * @param color
   *            The background color of the graphics context
   */
  public void setBackground(Color color) {
    predraw();
    GL.glClearColor(color.r, color.g, color.b, color.a);
    postdraw();
  }

  /**
   * Get the current graphics context background color
   *
   * @return The background color of this graphics context
   */
  public Color getBackground() {
    predraw();
    FloatBuffer buffer = BufferUtils.createFloatBuffer(16);
    GL.glGetFloat(SGL.GL_COLOR_CLEAR_VALUE, buffer);
    postdraw();

    return new Color(buffer);
  }

  /**
   * Clear the graphics context
   */
  public void clear() {
    predraw();
    GL.glClear(SGL.GL_COLOR_BUFFER_BIT);
    postdraw();
  }

  /**
   * Reset the transformation on this graphics context
   */
  public void resetTransform() {
    sx = 1;
    sy = 1;
   
    if (pushed) {
      predraw();
      GL.glPopMatrix();
      pushed = false;
      postdraw();
    }
  }

  /**
   * Check if we've pushed the previous matrix, if not then push it now.
   */
  private void checkPush() {
    if (!pushed) {
      predraw();
      GL.glPushMatrix();
      pushed = true;
      postdraw();
    }
  }

  /**
   * Apply a scaling factor to everything drawn on the graphics context
   *
   * @param sx
   *            The scaling factor to apply to the x axis
   * @param sy
   *            The scaling factor to apply to the y axis
   */
  public void scale(float sx, float sy) {
    this.sx = this.sx * sx;
    this.sy = this.sy * sy;
   
    checkPush();

    predraw();
    GL.glScalef(sx, sy, 1);
    postdraw();
  }

  /**
   * Apply a rotation to everything draw on the graphics context
   *
   * @param rx
   *            The x coordinate of the center of rotation
   * @param ry
   *            The y coordinate of the center of rotation
   * @param ang
   *            The angle (in degrees) to rotate by
   */
  public void rotate(float rx, float ry, float ang) {
    checkPush();

    predraw();
    translate(rx, ry);
    GL.glRotatef(ang, 0, 0, 1);
    translate(-rx, -ry);
    postdraw();
  }

  /**
   * Apply a translation to everything drawn to the context
   *
   * @param x
   *            The amount to translate on the x-axis
   * @param y
   *            The amount of translate on the y-axis
   */
  public void translate(float x, float y) {
    checkPush();

    predraw();
    GL.glTranslatef(x, y, 0);
    postdraw();
  }

  /**
   * Set the font to be used when rendering text
   *
   * @param font
   *            The font to be used when rendering text
   */
  public void setFont(Font font) {
    this.font = font;
  }

  /**
   * Reset to using the default font for this context
   */
  public void resetFont() {
    font = DEFAULT_FONT;
  }

  /**
   * Set the color to use when rendering to this context
   *
   * @param color
   *            The color to use when rendering to this context
   */
  public void setColor(Color color) {
    if (color == null) {
      return;
    }
   
    currentColor = new Color(color);
    predraw();
    currentColor.bind();
    postdraw();
  }

  /**
   * Get the color in use by this graphics context
   *
   * @return The color in use by this graphics context
   */
  public Color getColor() {
    return new Color(currentColor);
  }

  /**
   * Draw a line on the canvas in the current colour
   *
   * @param x1
   *            The x coordinate of the start point
   * @param y1
   *            The y coordinate of the start point
   * @param x2
   *            The x coordinate of the end point
   * @param y2
   *            The y coordinate of the end point
   */
  public void drawLine(float x1, float y1, float x2, float y2) {
    float lineWidth = this.lineWidth - 1;
   
    if (LSR.applyGLLineFixes()) {
      if (x1 == x2) {
        if (y1 > y2) {
          float temp = y2;
          y2 = y1;
          y1 = temp;
        }
        float step = 1 / sy;
        lineWidth = lineWidth / sy;
        fillRect(x1-(lineWidth/2.0f),y1-(lineWidth/2.0f),lineWidth+step,(y2-y1)+lineWidth+step);
        return;
      } else if (y1 == y2) {
        if (x1 > x2) {
          float temp = x2;
          x2 = x1;
          x1 = temp;
        }
        float step = 1 / sx;
        lineWidth = lineWidth / sx;
        fillRect(x1-(lineWidth/2.0f),y1-(lineWidth/2.0f),(x2-x1)+lineWidth+step,lineWidth+step);
        return;
      }
    }
   
    predraw();
    currentColor.bind();
    TextureImpl.bindNone();

    LSR.start();
    LSR.vertex(x1,y1);
    LSR.vertex(x2,y2);
    LSR.end();
   
    postdraw();
  }

  /**
   * Draw the outline of the given shape.
   *
   * @param shape
   *            The shape to draw.
   * @param fill
   *            The fill type to apply
   */
  public void draw(Shape shape, ShapeFill fill) {
    predraw();
    TextureImpl.bindNone();

    ShapeRenderer.draw(shape, fill);

    currentColor.bind();
    postdraw();
  }

  /**
   * Draw the the given shape filled in.
   *
   * @param shape
   *            The shape to fill.
   * @param fill
   *            The fill type to apply
   */
  public void fill(Shape shape, ShapeFill fill) {
    predraw();
    TextureImpl.bindNone();

    ShapeRenderer.fill(shape, fill);

    currentColor.bind();
    postdraw();
  }

  /**
   * Draw the outline of the given shape.
   *
   * @param shape
   *            The shape to draw.
   */
  public void draw(Shape shape) {
    predraw();
    TextureImpl.bindNone();
    currentColor.bind();

    ShapeRenderer.draw(shape);

    postdraw();
  }

  /**
   * Draw the the given shape filled in.
   *
   * @param shape
   *            The shape to fill.
   */
  public void fill(Shape shape) {
    predraw();
    TextureImpl.bindNone();
    currentColor.bind();

    ShapeRenderer.fill(shape);

    postdraw();
  }

  /**
   * Draw the the given shape filled in with a texture
   *
   * @param shape
   *            The shape to texture.
   * @param image
   *            The image to tile across the shape
   */
  public void texture(Shape shape, Image image) {
    texture(shape, image, 0.01f, 0.01f, false);
  }

  /**
   * Draw the the given shape filled in with a texture
   *
   * @param shape
   *            The shape to texture.
   * @param image
   *            The image to tile across the shape
   * @param fill
   *            The shape fill to apply
   */
  public void texture(Shape shape, Image image, ShapeFill fill) {
    texture(shape, image, 0.01f, 0.01f, fill);
  }

  /**
   * Draw the the given shape filled in with a texture
   *
   * @param shape
   *            The shape to texture.
   * @param image
   *            The image to tile across the shape
   * @param fit
   *            True if we want to fit the image on to the shape
   */
  public void texture(Shape shape, Image image, boolean fit) {
    if (fit) {
      texture(shape, image, 1, 1, true);
    } else {
      texture(shape, image, 0.01f, 0.01f, false);
    }
  }

  /**
   * Draw the the given shape filled in with a texture
   *
   * @param shape
   *            The shape to texture.
   * @param image
   *            The image to tile across the shape
   * @param scaleX
   *            The scale to apply on the x axis for texturing
   * @param scaleY
   *            The scale to apply on the y axis for texturing
   */
  public void texture(Shape shape, Image image, float scaleX, float scaleY) {
    texture(shape, image, scaleX, scaleY, false);
  }

  /**
   * Draw the the given shape filled in with a texture
   *
   * @param shape
   *            The shape to texture.
   * @param image
   *            The image to tile across the shape
   * @param scaleX
   *            The scale to apply on the x axis for texturing
   * @param scaleY
   *            The scale to apply on the y axis for texturing
   * @param fit
   *            True if we want to fit the image on to the shape
   */
  public void texture(Shape shape, Image image, float scaleX, float scaleY,
      boolean fit) {
    predraw();
    TextureImpl.bindNone();
    currentColor.bind();

    if (fit) {
      ShapeRenderer.textureFit(shape, image, scaleX, scaleY);
    } else {
      ShapeRenderer.texture(shape, image, scaleX, scaleY);
    }
   
    postdraw();
  }

  /**
   * Draw the the given shape filled in with a texture
   *
   * @param shape
   *            The shape to texture.
   * @param image
   *            The image to tile across the shape
   * @param scaleX
   *            The scale to apply on the x axis for texturing
   * @param scaleY
   *            The scale to apply on the y axis for texturing
   * @param fill
   *            The shape fill to apply
   */
  public void texture(Shape shape, Image image, float scaleX, float scaleY,
      ShapeFill fill) {
    predraw();
    TextureImpl.bindNone();
    currentColor.bind();

    ShapeRenderer.texture(shape, image, scaleX, scaleY, fill);

    postdraw();
  }

  /**
   * Draw a rectangle to the canvas in the current colour
   *
   * @param x1
   *            The x coordinate of the top left corner
   * @param y1
   *            The y coordinate of the top left corner
   * @param width
   *            The width of the rectangle to draw
   * @param height
   *            The height of the rectangle to draw
   */
  public void drawRect(float x1, float y1, float width, float height) {
    float lineWidth = getLineWidth();
   
    drawLine(x1,y1,x1+width,y1);
    drawLine(x1+width,y1,x1+width,y1+height);
    drawLine(x1+width,y1+height,x1,y1+height);
    drawLine(x1,y1+height,x1,y1);
  }

  /**
   * Clear the clipping being applied. This will allow graphics to be drawn
   * anywhere on the screen
   */
  public void clearClip() {
    clip = null;
    predraw();
    GL.glDisable(SGL.GL_SCISSOR_TEST);
    postdraw();
  }

  /**
   * Set clipping that controls which areas of the world will be drawn to.
   * Note that world clip is different from standard screen clip in that it's
   * defined in the space of the current world coordinate - i.e. it's affected
   * by translate, rotate, scale etc.
   *
   * @param x
   *            The x coordinate of the top left corner of the allowed area
   * @param y
   *            The y coordinate of the top left corner of the allowed area
   * @param width
   *            The width of the allowed area
   * @param height
   *            The height of the allowed area
   */
  public void setWorldClip(float x, float y, float width, float height) {
    predraw();
    worldClipRecord = new Rectangle(x, y, width, height);
   
    GL.glEnable(SGL.GL_CLIP_PLANE0);
    worldClip.put(1).put(0).put(0).put(-x).flip();
    GL.glClipPlane(SGL.GL_CLIP_PLANE0, worldClip);
    GL.glEnable(SGL.GL_CLIP_PLANE1);
    worldClip.put(-1).put(0).put(0).put(x + width).flip();
    GL.glClipPlane(SGL.GL_CLIP_PLANE1, worldClip);

    GL.glEnable(SGL.GL_CLIP_PLANE2);
    worldClip.put(0).put(1).put(0).put(-y).flip();
    GL.glClipPlane(SGL.GL_CLIP_PLANE2, worldClip);
    GL.glEnable(SGL.GL_CLIP_PLANE3);
    worldClip.put(0).put(-1).put(0).put(y + height).flip();
    GL.glClipPlane(SGL.GL_CLIP_PLANE3, worldClip);
    postdraw();
  }

  /**
   * Clear world clipping setup. This does not effect screen clipping
   */
  public void clearWorldClip() {
    predraw();
    worldClipRecord = null;
    GL.glDisable(SGL.GL_CLIP_PLANE0);
    GL.glDisable(SGL.GL_CLIP_PLANE1);
    GL.glDisable(SGL.GL_CLIP_PLANE2);
    GL.glDisable(SGL.GL_CLIP_PLANE3);
    postdraw();
  }

  /**
   * Set the world clip to be applied
   *
   * @see #setWorldClip(float, float, float, float)
   * @param clip
   *            The area still visible
   */
  public void setWorldClip(Rectangle clip) {
    if (clip == null) {
      clearWorldClip();
    } else {
      setWorldClip(clip.getX(), clip.getY(), clip.getWidth(), clip
          .getHeight());
    }
  }

  /**
   * Get the last set world clip or null of the world clip isn't set
   *
   * @return The last set world clip rectangle
   */
  public Rectangle getWorldClip() {
    return worldClipRecord;
  }

  /**
   * Set the clipping to apply to the drawing. Note that this clipping takes
   * no note of the transforms that have been applied to the context and is
   * always in absolute screen space coordinates.
   *
   * @param x
   *            The x coordinate of the top left corner of the allowed area
   * @param y
   *            The y coordinate of the top left corner of the allowed area
   * @param width
   *            The width of the allowed area
   * @param height
   *            The height of the allowed area
   */
  public void setClip(int x, int y, int width, int height) {
    predraw();
   
    if (clip == null) {
      GL.glEnable(SGL.GL_SCISSOR_TEST);
      clip = new Rectangle(x, y, width, height);
    } else {
      clip.setBounds(x,y,width,height);
    }
   
    GL.glScissor(x, screenHeight - y - height, width, height);
    postdraw();
  }

  /**
   * Set the clipping to apply to the drawing. Note that this clipping takes
   * no note of the transforms that have been applied to the context and is
   * always in absolute screen space coordinates.
   *
   * @param rect
   *            The rectangle describing the clipped area in screen
   *            coordinates
   */
  public void setClip(Rectangle rect) {
    if (rect == null) {
      clearClip();
      return;
    }

    setClip((int) rect.getX(), (int) rect.getY(), (int) rect.getWidth(),
        (int) rect.getHeight());
  }

  /**
   * Return the currently applied clipping rectangle
   *
   * @return The current applied clipping rectangle or null if no clipping is
   *         applied
   */
  public Rectangle getClip() {
    return clip;
  }

  /**
   * Tile a rectangle with a pattern specifing the offset from the top corner
   * that one tile should match
   *
   * @param x
   *            The x coordinate of the rectangle
   * @param y
   *            The y coordinate of the rectangle
   * @param width
   *            The width of the rectangle
   * @param height
   *            The height of the rectangle
   * @param pattern
   *            The image to tile across the rectangle
   * @param offX
   *            The offset on the x axis from the top left corner
   * @param offY
   *            The offset on the y axis from the top left corner
   */
  public void fillRect(float x, float y, float width, float height,
      Image pattern, float offX, float offY) {
    int cols = ((int) Math.ceil(width / pattern.getWidth())) + 2;
    int rows = ((int) Math.ceil(height / pattern.getHeight())) + 2;

    Rectangle preClip = getWorldClip();
    setWorldClip(x, y, width, height);

    predraw();
    // Draw all the quads we need
    for (int c = 0; c < cols; c++) {
      for (int r = 0; r < rows; r++) {
        pattern.draw(c * pattern.getWidth() + x - offX, r
            * pattern.getHeight() + y - offY);
      }
    }
    postdraw();

    setWorldClip(preClip);
  }

  /**
   * Fill a rectangle on the canvas in the current color
   *
   * @param x1
   *            The x coordinate of the top left corner
   * @param y1
   *            The y coordinate of the top left corner
   * @param width
   *            The width of the rectangle to fill
   * @param height
   *            The height of the rectangle to fill
   */
  public void fillRect(float x1, float y1, float width, float height) {
    predraw();
    TextureImpl.bindNone();
    currentColor.bind();

    GL.glBegin(SGL.GL_QUADS);
    GL.glVertex2f(x1, y1);
    GL.glVertex2f(x1 + width, y1);
    GL.glVertex2f(x1 + width, y1 + height);
    GL.glVertex2f(x1, y1 + height);
    GL.glEnd();
    postdraw();
  }

  /**
   * Draw an oval to the canvas
   *
   * @param x1
   *            The x coordinate of the top left corner of a box containing
   *            the oval
   * @param y1
   *            The y coordinate of the top left corner of a box containing
   *            the oval
   * @param width
   *            The width of the oval
   * @param height
   *            The height of the oval
   */
  public void drawOval(float x1, float y1, float width, float height) {
    drawOval(x1, y1, width, height, DEFAULT_SEGMENTS);
  }

  /**
   * Draw an oval to the canvas
   *
   * @param x1
   *            The x coordinate of the top left corner of a box containing
   *            the oval
   * @param y1
   *            The y coordinate of the top left corner of a box containing
   *            the oval
   * @param width
   *            The width of the oval
   * @param height
   *            The height of the oval
   * @param segments
   *            The number of line segments to use when drawing the oval
   */
  public void drawOval(float x1, float y1, float width, float height,
      int segments) {
    drawArc(x1, y1, width, height, segments, 0, 360);
  }

  /**
   * Draw an oval to the canvas
   *
   * @param x1
   *            The x coordinate of the top left corner of a box containing
   *            the arc
   * @param y1
   *            The y coordinate of the top left corner of a box containing
   *            the arc
   * @param width
   *            The width of the arc
   * @param height
   *            The height of the arc
   * @param start
   *            The angle the arc starts at
   * @param end
   *            The angle the arc ends at
   */
  public void drawArc(float x1, float y1, float width, float height,
      float start, float end) {
    drawArc(x1, y1, width, height, DEFAULT_SEGMENTS, start, end);
  }

  /**
   * Draw an oval to the canvas
   *
   * @param x1
   *            The x coordinate of the top left corner of a box containing
   *            the arc
   * @param y1
   *            The y coordinate of the top left corner of a box containing
   *            the arc
   * @param width
   *            The width of the arc
   * @param height
   *            The height of the arc
   * @param segments
   *            The number of line segments to use when drawing the arc
   * @param start
   *            The angle the arc starts at
   * @param end
   *            The angle the arc ends at
   */
  public void drawArc(float x1, float y1, float width, float height,
      int segments, float start, float end) {
    predraw();
    TextureImpl.bindNone();
    currentColor.bind();

    while (end < start) {
      end += 360;
    }

    float cx = x1 + (width / 2.0f);
    float cy = y1 + (height / 2.0f);

    LSR.start();
    int step = 360 / segments;

    for (int a = (int) start; a < (int) (end + step); a += step) {
      float ang = a;
      if (ang > end) {
        ang = end;
      }
      float x = (float) (cx + (FastTrig.cos(Math.toRadians(ang)) * width / 2.0f));
      float y = (float) (cy + (FastTrig.sin(Math.toRadians(ang)) * height / 2.0f));

      LSR.vertex(x,y);
    }
    LSR.end();
    postdraw();
  }

  /**
   * Fill an oval to the canvas
   *
   * @param x1
   *            The x coordinate of the top left corner of a box containing
   *            the oval
   * @param y1
   *            The y coordinate of the top left corner of a box containing
   *            the oval
   * @param width
   *            The width of the oval
   * @param height
   *            The height of the oval
   */
  public void fillOval(float x1, float y1, float width, float height) {
    fillOval(x1, y1, width, height, DEFAULT_SEGMENTS);
  }

  /**
   * Fill an oval to the canvas
   *
   * @param x1
   *            The x coordinate of the top left corner of a box containing
   *            the oval
   * @param y1
   *            The y coordinate of the top left corner of a box containing
   *            the oval
   * @param width
   *            The width of the oval
   * @param height
   *            The height of the oval
   * @param segments
   *            The number of line segments to use when filling the oval
   */
  public void fillOval(float x1, float y1, float width, float height,
      int segments) {
    fillArc(x1, y1, width, height, segments, 0, 360);
  }

  /**
   * Fill an arc to the canvas (a wedge)
   *
   * @param x1
   *            The x coordinate of the top left corner of a box containing
   *            the arc
   * @param y1
   *            The y coordinate of the top left corner of a box containing
   *            the arc
   * @param width
   *            The width of the arc
   * @param height
   *            The height of the arc
   * @param start
   *            The angle the arc starts at
   * @param end
   *            The angle the arc ends at
   */
  public void fillArc(float x1, float y1, float width, float height,
      float start, float end) {
    fillArc(x1, y1, width, height, DEFAULT_SEGMENTS, start, end);
  }

  /**
   * Fill an arc to the canvas (a wedge)
   *
   * @param x1
   *            The x coordinate of the top left corner of a box containing
   *            the arc
   * @param y1
   *            The y coordinate of the top left corner of a box containing
   *            the arc
   * @param width
   *            The width of the arc
   * @param height
   *            The height of the arc
   * @param segments
   *            The number of line segments to use when filling the arc
   * @param start
   *            The angle the arc starts at
   * @param end
   *            The angle the arc ends at
   */
  public void fillArc(float x1, float y1, float width, float height,
      int segments, float start, float end) {
    predraw();
    TextureImpl.bindNone();
    currentColor.bind();

    while (end < start) {
      end += 360;
    }

    float cx = x1 + (width / 2.0f);
    float cy = y1 + (height / 2.0f);

    GL.glBegin(SGL.GL_TRIANGLE_FAN);
    int step = 360 / segments;

    GL.glVertex2f(cx, cy);

    for (int a = (int) start; a < (int) (end + step); a += step) {
      float ang = a;
      if (ang > end) {
        ang = end;
      }

      float x = (float) (cx + (FastTrig.cos(Math.toRadians(ang)) * width / 2.0f));
      float y = (float) (cy + (FastTrig.sin(Math.toRadians(ang)) * height / 2.0f));

      GL.glVertex2f(x, y);
    }
    GL.glEnd();

    if (antialias) {
      GL.glBegin(SGL.GL_TRIANGLE_FAN);
      GL.glVertex2f(cx, cy);
      if (end != 360) {
        end -= 10;
      }

      for (int a = (int) start; a < (int) (end + step); a += step) {
        float ang = a;
        if (ang > end) {
          ang = end;
        }

        float x = (float) (cx + (FastTrig.cos(Math.toRadians(ang + 10))
            * width / 2.0f));
        float y = (float) (cy + (FastTrig.sin(Math.toRadians(ang + 10))
            * height / 2.0f));

        GL.glVertex2f(x, y);
      }
      GL.glEnd();
    }

    postdraw();
  }

  /**
   * Draw a rounded rectangle
   *
   * @param x
   *            The x coordinate of the top left corner of the rectangle
   * @param y
   *            The y coordinate of the top left corner of the rectangle
   * @param width
   *            The width of the rectangle
   * @param height
   *            The height of the rectangle
   * @param cornerRadius
   *            The radius of the rounded edges on the corners
   */
  public void drawRoundRect(float x, float y, float width, float height,
      int cornerRadius) {
    drawRoundRect(x, y, width, height, cornerRadius, DEFAULT_SEGMENTS);
  }

  /**
   * Draw a rounded rectangle
   *
   * @param x
   *            The x coordinate of the top left corner of the rectangle
   * @param y
   *            The y coordinate of the top left corner of the rectangle
   * @param width
   *            The width of the rectangle
   * @param height
   *            The height of the rectangle
   * @param cornerRadius
   *            The radius of the rounded edges on the corners
   * @param segs
   *            The number of segments to make the corners out of
   */
  public void drawRoundRect(float x, float y, float width, float height,
      int cornerRadius, int segs) {
    if (cornerRadius < 0)
      throw new IllegalArgumentException("corner radius must be > 0");
    if (cornerRadius == 0) {
      drawRect(x, y, width, height);
      return;
    }

    int mr = (int) Math.min(width, height) / 2;
    // make sure that w & h are larger than 2*cornerRadius
    if (cornerRadius > mr) {
      cornerRadius = mr;
    }

    drawLine(x + cornerRadius, y, x + width - cornerRadius, y);
    drawLine(x, y + cornerRadius, x, y + height - cornerRadius);
    drawLine(x + width, y + cornerRadius, x + width, y + height
        - cornerRadius);
    drawLine(x + cornerRadius, y + height, x + width - cornerRadius, y
        + height);

    float d = cornerRadius * 2;
    // bottom right - 0, 90
    drawArc(x + width - d, y + height - d, d, d, segs, 0, 90);
    // bottom left - 90, 180
    drawArc(x, y + height - d, d, d, segs, 90, 180);
    // top right - 270, 360
    drawArc(x + width - d, y, d, d, segs, 270, 360);
    // top left - 180, 270
    drawArc(x, y, d, d, segs, 180, 270);
  }

  /**
   * Fill a rounded rectangle
   *
   * @param x
   *            The x coordinate of the top left corner of the rectangle
   * @param y
   *            The y coordinate of the top left corner of the rectangle
   * @param width
   *            The width of the rectangle
   * @param height
   *            The height of the rectangle
   * @param cornerRadius
   *            The radius of the rounded edges on the corners
   */
  public void fillRoundRect(float x, float y, float width, float height,
      int cornerRadius) {
    fillRoundRect(x, y, width, height, cornerRadius, DEFAULT_SEGMENTS);
  }

  /**
   * Fill a rounded rectangle
   *
   * @param x
   *            The x coordinate of the top left corner of the rectangle
   * @param y
   *            The y coordinate of the top left corner of the rectangle
   * @param width
   *            The width of the rectangle
   * @param height
   *            The height of the rectangle
   * @param cornerRadius
   *            The radius of the rounded edges on the corners
   * @param segs
   *            The number of segments to make the corners out of
   */
  public void fillRoundRect(float x, float y, float width, float height,
      int cornerRadius, int segs) {
    if (cornerRadius < 0)
      throw new IllegalArgumentException("corner radius must be > 0");
    if (cornerRadius == 0) {
      fillRect(x, y, width, height);
      return;
    }

    int mr = (int) Math.min(width, height) / 2;
    // make sure that w & h are larger than 2*cornerRadius
    if (cornerRadius > mr) {
      cornerRadius = mr;
    }

    float d = cornerRadius * 2;

    fillRect(x + cornerRadius, y, width - d, cornerRadius);
    fillRect(x, y + cornerRadius, cornerRadius, height - d);
    fillRect(x + width - cornerRadius, y + cornerRadius, cornerRadius,
        height - d);
    fillRect(x + cornerRadius, y + height - cornerRadius, width - d,
        cornerRadius);
    fillRect(x + cornerRadius, y + cornerRadius, width - d, height - d);

    // bottom right - 0, 90
    fillArc(x + width - d, y + height - d, d, d, segs, 0, 90);
    // bottom left - 90, 180
    fillArc(x, y + height - d, d, d, segs, 90, 180);
    // top right - 270, 360
    fillArc(x + width - d, y, d, d, segs, 270, 360);
    // top left - 180, 270
    fillArc(x, y, d, d, segs, 180, 270);
  }

  /**
   * Set the with of the line to be used when drawing line based primitives
   *
   * @param width
   *            The width of the line to be used when drawing line based
   *            primitives
   */
  public void setLineWidth(float width) {
    predraw();
    this.lineWidth = width;
    LSR.setWidth(width);
    GL.glPointSize(width);
    postdraw();
  }

  /**
   * Get the width of lines being drawn in this context
   *
   * @return The width of lines being draw in this context
   */
  public float getLineWidth() {
    return lineWidth;
  }

  /**
   * Reset the line width in use to the default for this graphics context
   */
  public void resetLineWidth() {
    predraw();
   
    Renderer.getLineStripRenderer().setWidth(1.0f);
    GL.glLineWidth(1.0f);
    GL.glPointSize(1.0f);
   
    postdraw();
  }

  /**
   * Indicate if we should antialias as we draw primitives
   *
   * @param anti
   *            True if we should antialias
   */
  public void setAntiAlias(boolean anti) {
    predraw();
    antialias = anti;
    LSR.setAntiAlias(anti);
    if (anti) {
      GL.glEnable(SGL.GL_POLYGON_SMOOTH);
    } else {
      GL.glDisable(SGL.GL_POLYGON_SMOOTH);
    }
    postdraw();
  }

  /**
   * True if antialiasing has been turned on for this graphics context
   *
   * @return True if antialiasing has been turned on for this graphics context
   */
  public boolean isAntiAlias() {
    return antialias;
  }

  /**
   * Draw a string to the screen using the current font
   *
   * @param str
   *            The string to draw
   * @param x
   *            The x coordinate to draw the string at
   * @param y
   *            The y coordinate to draw the string at
   */
  public void drawString(String str, float x, float y) {
    predraw();
    font.drawString(x, y, str, currentColor);
    postdraw();
  }

  /**
   * Draw an image to the screen
   *
   * @param image
   *            The image to draw to the screen
   * @param x
   *            The x location at which to draw the image
   * @param y
   *            The y location at which to draw the image
   * @param col
   *            The color to apply to the image as a filter
   */
  public void drawImage(Image image, float x, float y, Color col) {
    predraw();
    image.draw(x, y, col);
    currentColor.bind();
    postdraw();
  }

  /**
   * Draw an animation to this graphics context
   *
   * @param anim
   *            The animation to be drawn
   * @param x
   *            The x position to draw the animation at
   * @param y
   *            The y position to draw the animation at
   */
  public void drawAnimation(Animation anim, float x, float y) {
    drawAnimation(anim, x, y, Color.white);
  }

  /**
   * Draw an animation to this graphics context
   *
   * @param anim
   *            The animation to be drawn
   * @param x
   *            The x position to draw the animation at
   * @param y
   *            The y position to draw the animation at
   * @param col
   *            The color to apply to the animation as a filter
   */
  public void drawAnimation(Animation anim, float x, float y, Color col) {
    predraw();
    anim.draw(x, y, col);
    currentColor.bind();
    postdraw();
  }

  /**
   * Draw an image to the screen
   *
   * @param image
   *            The image to draw to the screen
   * @param x
   *            The x location at which to draw the image
   * @param y
   *            The y location at which to draw the image
   */
  public void drawImage(Image image, float x, float y) {
    drawImage(image, x, y, Color.white);
  }

  /**
   * Draw a section of an image at a particular location and scale on the
   * screen
   *
   * @param image
   *            The image to draw a section of
   * @param x
   *            The x position to draw the image
   * @param y
   *            The y position to draw the image
   * @param x2
   *            The x position of the bottom right corner of the drawn image
   * @param y2
   *            The y position of the bottom right corner of the drawn image
   * @param srcx
   *            The x position of the rectangle to draw from this image (i.e.
   *            relative to the image)
   * @param srcy
   *            The y position of the rectangle to draw from this image (i.e.
   *            relative to the image)
   * @param srcx2
   *            The x position of the bottom right cornder of rectangle to
   *            draw from this image (i.e. relative to the image)
   * @param srcy2
   *            The t position of the bottom right cornder of rectangle to
   *            draw from this image (i.e. relative to the image)
   */
  public void drawImage(Image image, float x, float y, float x2, float y2,
      float srcx, float srcy, float srcx2, float srcy2) {
    predraw();
    image.draw(x, y, x2, y2, srcx, srcy, srcx2, srcy2);
    currentColor.bind();
    postdraw();
  }

  /**
   * Draw a section of an image at a particular location and scale on the
   * screen
   *
   * @param image
   *            The image to draw a section of
   * @param x
   *            The x position to draw the image
   * @param y
   *            The y position to draw the image
   * @param srcx
   *            The x position of the rectangle to draw from this image (i.e.
   *            relative to the image)
   * @param srcy
   *            The y position of the rectangle to draw from this image (i.e.
   *            relative to the image)
   * @param srcx2
   *            The x position of the bottom right cornder of rectangle to
   *            draw from this image (i.e. relative to the image)
   * @param srcy2
   *            The t position of the bottom right cornder of rectangle to
   *            draw from this image (i.e. relative to the image)
   */
  public void drawImage(Image image, float x, float y, float srcx,
      float srcy, float srcx2, float srcy2) {
    drawImage(image, x, y, x + image.getWidth(), y + image.getHeight(),
        srcx, srcy, srcx2, srcy2);
  }

  /**
   * Copy an area of the rendered screen into an image. The width and height
   * of the area are assumed to match that of the image
   *
   * @param target
   *            The target image
   * @param x
   *            The x position to copy from
   * @param y
   *            The y position to copy from
   */
  public void copyArea(Image target, int x, int y) {
    int format = target.getTexture().hasAlpha() ? SGL.GL_RGBA : SGL.GL_RGB;
    target.bind();
    GL.glCopyTexImage2D(SGL.GL_TEXTURE_2D, 0, format, x, screenHeight
        - (y + target.getHeight()), target.getTexture()
        .getTextureWidth(), target.getTexture().getTextureHeight(), 0);
    target.ensureInverted();
  }

  /**
   * Translate an unsigned int into a signed integer
   *
   * @param b
   *            The byte to convert
   * @return The integer value represented by the byte
   */
  private int translate(byte b) {
    if (b < 0) {
      return 256 + b;
    }

    return b;
  }

  /**
   * Get the colour of a single pixel in this graphics context
   *
   * @param x
   *            The x coordinate of the pixel to read
   * @param y
   *            The y coordinate of the pixel to read
   * @return The colour of the pixel at the specified location
   */
  public Color getPixel(int x, int y) {
    predraw();
    GL.glReadPixels(x, screenHeight - y, 1, 1, SGL.GL_RGBA,
        SGL.GL_UNSIGNED_BYTE, readBuffer);
    postdraw();

    return new Color(translate(readBuffer.get(0)), translate(readBuffer
        .get(1)), translate(readBuffer.get(2)), translate(readBuffer
        .get(3)));
  }

  /**
   * Get an ara of pixels as RGBA values into a buffer
   *
   * @param x The x position in the context to grab from
   * @param y The y position in the context to grab from
   * @param width The width of the area to grab from
   * @param height The hiehgt of the area to grab from
   * @param target The target buffer to grab into
   */
  public void getArea(int x, int y, int width, int height, ByteBuffer target)
  {
    if (target.capacity() < width * height * 4)
    {
      throw new IllegalArgumentException("Byte buffer provided to get area is not big enough");
    }
   
    predraw()
    GL.glReadPixels(x, screenHeight - y - height, width, height, SGL.GL_RGBA,
        SGL.GL_UNSIGNED_BYTE, target);
    postdraw();
  }
 
  /**
   * Draw a section of an image at a particular location and scale on the
   * screen
   *
   * @param image
   *            The image to draw a section of
   * @param x
   *            The x position to draw the image
   * @param y
   *            The y position to draw the image
   * @param x2
   *            The x position of the bottom right corner of the drawn image
   * @param y2
   *            The y position of the bottom right corner of the drawn image
   * @param srcx
   *            The x position of the rectangle to draw from this image (i.e.
   *            relative to the image)
   * @param srcy
   *            The y position of the rectangle to draw from this image (i.e.
   *            relative to the image)
   * @param srcx2
   *            The x position of the bottom right cornder of rectangle to
   *            draw from this image (i.e. relative to the image)
   * @param srcy2
   *            The t position of the bottom right cornder of rectangle to
   *            draw from this image (i.e. relative to the image)
   * @param col
   *            The color to apply to the image as a filter
   */
  public void drawImage(Image image, float x, float y, float x2, float y2,
      float srcx, float srcy, float srcx2, float srcy2, Color col) {
    predraw();
    image.draw(x, y, x2, y2, srcx, srcy, srcx2, srcy2, col);
    currentColor.bind();
    postdraw();
  }

  /**
   * Draw a section of an image at a particular location and scale on the
   * screen
   *
   * @param image
   *            The image to draw a section of
   * @param x
   *            The x position to draw the image
   * @param y
   *            The y position to draw the image
   * @param srcx
   *            The x position of the rectangle to draw from this image (i.e.
   *            relative to the image)
   * @param srcy
   *            The y position of the rectangle to draw from this image (i.e.
   *            relative to the image)
   * @param srcx2
   *            The x position of the bottom right cornder of rectangle to
   *            draw from this image (i.e. relative to the image)
   * @param srcy2
   *            The t position of the bottom right cornder of rectangle to
   *            draw from this image (i.e. relative to the image)
   * @param col
   *            The color to apply to the image as a filter
   */
  public void drawImage(Image image, float x, float y, float srcx,
      float srcy, float srcx2, float srcy2, Color col) {
    drawImage(image, x, y, x + image.getWidth(), y + image.getHeight(),
        srcx, srcy, srcx2, srcy2, col);
  }

  /**
   * Draw a line with a gradient between the two points.
   *
   * @param x1
   *            The starting x position to draw the line
   * @param y1
   *            The starting y position to draw the line
   * @param red1
   *            The starting position's shade of red
   * @param green1
   *            The starting position's shade of green
   * @param blue1
   *            The starting position's shade of blue
   * @param alpha1
   *            The starting position's alpha value
   * @param x2
   *            The ending x position to draw the line
   * @param y2
   *            The ending y position to draw the line
   * @param red2
   *            The ending position's shade of red
   * @param green2
   *            The ending position's shade of green
   * @param blue2
   *            The ending position's shade of blue
   * @param alpha2
   *            The ending position's alpha value
   */
  public void drawGradientLine(float x1, float y1, float red1, float green1,
                  float blue1, float alpha1, float x2, float y2, float red2,
                  float green2, float blue2, float alpha2) {
    predraw();

    TextureImpl.bindNone();

    GL.glBegin(SGL.GL_LINES);

    GL.glColor4f(red1, green1, blue1, alpha1);
    GL.glVertex2f(x1, y1);

    GL.glColor4f(red2, green2, blue2, alpha2);
    GL.glVertex2f(x2, y2);

    GL.glEnd();

    postdraw();
  }

  /**
   * Draw a line with a gradient between the two points.
   *
   * @param x1
   *            The starting x position to draw the line
   * @param y1
   *            The starting y position to draw the line
   * @param Color1
   *            The starting position's color
   * @param x2
   *            The ending x position to draw the line
   * @param y2
   *            The ending y position to draw the line
   * @param Color2
   *            The ending position's color
   */
  public void drawGradientLine(float x1, float y1, Color Color1, float x2,
                 float y2, Color Color2) {
    predraw();

    TextureImpl.bindNone();

    GL.glBegin(SGL.GL_LINES);

    Color1.bind();
    GL.glVertex2f(x1, y1);

    Color2.bind();
    GL.glVertex2f(x2, y2);

    GL.glEnd();

    postdraw();
  }
 
  /**
   * Push the current state of the transform from this graphics contexts
   * onto the underlying graphics stack's transform stack. An associated
   * popTransform() must be performed to restore the state before the end
   * of the rendering loop.
   */
  public void pushTransform() {
    predraw();
   
    FloatBuffer buffer;
    if (stackIndex >= stack.size()) {
      buffer = BufferUtils.createFloatBuffer(16);
      stack.add(buffer);
    } else {
      buffer = (FloatBuffer) stack.get(stackIndex);
    }
   
    GL.glGetFloat(GL11.GL_MODELVIEW_MATRIX, buffer);
    stackIndex++;
   
    postdraw();
  }
 
  /**
   * Pop a previously pushed transform from the stack to the current. This should
   * only be called if a transform has been previously pushed.
   */
  public void popTransform() {
    if (stackIndex == 0) {
      throw new RuntimeException("Attempt to pop a transform that hasn't be pushed");
    }
   
    predraw();
   
    stackIndex--;
    FloatBuffer oldBuffer = (FloatBuffer) stack.get(stackIndex);
    GL.glLoadMatrix(oldBuffer);
   
    postdraw();
  }
  /**
   * Dispose this graphis context, this will release any underlying resourses. However
   * this will also invalidate it's use
   */
  public void destroy() {
   
  }
}
TOP

Related Classes of org.newdawn.slick.Graphics

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.