View Javadoc
1   package org.newdawn.slick;
2   
3   import java.nio.ByteBuffer;
4   import java.nio.DoubleBuffer;
5   import java.nio.FloatBuffer;
6   import java.security.AccessController;
7   import java.security.PrivilegedAction;
8   import java.util.ArrayList;
9   import java.util.List;
10  
11  import org.lwjgl.BufferUtils;
12  import org.lwjgl.opengl.GL11;
13  import org.newdawn.slick.geom.Rectangle;
14  import org.newdawn.slick.geom.Shape;
15  import org.newdawn.slick.geom.ShapeRenderer;
16  import org.newdawn.slick.opengl.TextureImpl;
17  import org.newdawn.slick.opengl.renderer.LineStripRenderer;
18  import org.newdawn.slick.opengl.renderer.Renderer;
19  import org.newdawn.slick.opengl.renderer.SGL;
20  import org.newdawn.slick.util.FastTrig;
21  import org.newdawn.slick.util.Log;
22  
23  import javax.annotation.Nonnull;
24  import javax.annotation.Nullable;
25  
26  /**
27   * A graphics context that can be used to render primatives to the accelerated canvas provided by LWJGL.
28   * 
29   * @author kevin
30   */
31  public class Graphics {
32      /** The renderer to use for all GL operations */
33      protected static final SGL GL = Renderer.get();
34      /** The renderer to use line strips */
35      private static final LineStripRenderer LSR = Renderer.getLineStripRenderer();
36  
37      /** The normal drawing mode */
38      private static final int MODE_NORMAL = 1;
39  
40      /** Draw to the alpha map */
41      private static final int MODE_ALPHA_MAP = 2;
42  
43      /** Draw using the alpha blending */
44      private static final int MODE_ALPHA_BLEND = 3;
45  
46      /** Draw multiplying the source and destination colours */
47      private static final int MODE_COLOR_MULTIPLY = 4;
48  
49      /** Draw adding the existing colour to the new colour */
50      private static final int MODE_ADD = 5;
51  
52      /** Draw blending the new image into the old one by a factor of it's colour */
53      private static final int MODE_SCREEN = 6;
54  
55      /** Draw adding the existing colour to the new colour including alpha */
56      private static final int MODE_ADD_ALPHA = 7;
57  
58      /** Draw multiplying the source and destination colours */
59      private static final int MODE_COLOR_MULTIPLY_ALPHA = 8;
60  
61      /** The default number of segments that will be used when drawing an oval */
62      private static final int DEFAULT_SEGMENTS = 50;
63  
64      /** The last graphics context in use */
65      @Nullable
66      private static Graphics currentGraphics = null;
67  
68      /** The default font to use */
69      private static Font DEFAULT_FONT;
70  
71      /** The last set scale */
72      private float sx = 1;
73      /** The last set scale */
74      private float sy = 1;
75  
76      /**
77       * Set the current graphics context in use
78       *
79       * @param current
80       *            The graphics context that should be considered current
81       */
82      protected static void setCurrent(Graphics current) {
83          if (currentGraphics != current) {
84              if (currentGraphics != null) {
85                  currentGraphics.disable();
86              }
87              currentGraphics = current;
88              currentGraphics.enable();
89          }
90      }
91  
92      /** The font in use */
93      private Font font;
94  
95      /** The current color */
96      @Nonnull
97      private Color currentColor = Color.white;
98  
99      /** The width of the screen */
100     protected int screenWidth;
101 
102     /** The height of the screen */
103     protected int screenHeight;
104 
105     /** True if the matrix has been pushed to the stack */
106     private boolean pushed;
107 
108     /** The graphics context clipping */
109     @Nullable
110     private Rectangle clip;
111 
112     /** Buffer used for setting the world clip */
113     private final DoubleBuffer worldClip = BufferUtils.createDoubleBuffer(4);
114 
115     /** The buffer used to read a screen pixel */
116     private final ByteBuffer readBuffer = BufferUtils.createByteBuffer(4);
117 
118     /** True if we're antialias */
119     private boolean antialias;
120 
121     /** The world clip recorded since last set */
122     @Nullable
123     private Rectangle worldClipRecord;
124 
125     /** The current drawing mode */
126     private int currentDrawingMode = MODE_NORMAL;
127 
128     /** The current line width */
129     private float lineWidth = 1;
130 
131     /** The matrix stack */
132     private final List<FloatBuffer> stack = new ArrayList<>();
133     /** The index into the stack we're using */
134     private int stackIndex;
135 
136     /**
137      * Default constructor for sub-classes
138      */
139     protected Graphics() {
140     }
141 
142     /**
143      * Create a new graphics context. Only the container should be doing this really
144      *
145      * @param width
146      *            The width of the screen for this context
147      * @param height
148      *            The height of the screen for this context
149      */
150     protected Graphics(int width, int height) {
151         if (DEFAULT_FONT == null) {
152             AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
153                 try {
154                     DEFAULT_FONT = new AngelCodeFont("org/newdawn/slick/data/defaultfont.fnt", "org/newdawn/slick/data/defaultfont.png");
155                 } catch (SlickException e) {
156                     Log.error(e);
157                 }
158                 return null; // nothing to return
159             });
160         }
161 
162         this.font = DEFAULT_FONT;
163         screenWidth = width;
164         screenHeight = height;
165     }
166 
167     /**
168      * Set the dimensions considered by the graphics context
169      *
170      * @param width
171      *            The width of the graphics context
172      * @param height
173      *            The height of the graphics context
174      */
175     void setDimensions(int width, int height) {
176         screenWidth = width;
177         screenHeight = height;
178     }
179 
180     /**
181      * Set the drawing mode to use. This mode defines how pixels are drawn to the graphics context. It can be used to
182      * draw into the alpha map.
183      *
184      * The mode supplied should be one of {@link Graphics#MODE_NORMAL} or {@link Graphics#MODE_ALPHA_MAP} or
185      * {@link Graphics#MODE_ALPHA_BLEND}
186      *
187      * @param mode
188      *            The mode to apply.
189      */
190     void setDrawMode(int mode) {
191         predraw();
192         currentDrawingMode = mode;
193         if (currentDrawingMode == MODE_NORMAL) {
194             GL.glEnable(SGL.GL_BLEND);
195             GL.glColorMask(true, true, true, true);
196             GL.glBlendFunc(SGL.GL_SRC_ALPHA, SGL.GL_ONE_MINUS_SRC_ALPHA);
197         }
198         if (currentDrawingMode == MODE_ALPHA_MAP) {
199             GL.glDisable(SGL.GL_BLEND);
200             GL.glColorMask(false, false, false, true);
201         }
202         if (currentDrawingMode == MODE_ALPHA_BLEND) {
203             GL.glEnable(SGL.GL_BLEND);
204             GL.glColorMask(true, true, true, false);
205             GL.glBlendFunc(SGL.GL_DST_ALPHA, SGL.GL_ONE_MINUS_DST_ALPHA);
206         }
207         if (currentDrawingMode == MODE_COLOR_MULTIPLY) {
208             GL.glEnable(SGL.GL_BLEND);
209             GL.glColorMask(true, true, true, true);
210             GL.glBlendFunc(SGL.GL_ONE_MINUS_SRC_COLOR, SGL.GL_SRC_COLOR);
211         }
212         if (currentDrawingMode == MODE_ADD) {
213             GL.glEnable(SGL.GL_BLEND);
214             GL.glColorMask(true, true, true, true);
215             GL.glBlendFunc(SGL.GL_ONE, SGL.GL_ONE);
216         }
217         if (currentDrawingMode == MODE_SCREEN) {
218             GL.glEnable(SGL.GL_BLEND);
219             GL.glColorMask(true, true, true, true);
220             GL.glBlendFunc(SGL.GL_ONE, SGL.GL_ONE_MINUS_SRC_COLOR);
221         }
222         if (currentDrawingMode == MODE_ADD_ALPHA) {
223             GL.glEnable(SGL.GL_BLEND);
224             GL.glColorMask(true, true, true, true);
225             GL.glBlendFunc(SGL.GL_SRC_ALPHA, SGL.GL_ONE);
226         }
227         if (currentDrawingMode == MODE_COLOR_MULTIPLY_ALPHA) {
228             GL.glEnable(SGL.GL_BLEND);
229             GL.glColorMask(true, true, true, true);
230             GL.glBlendFunc(SGL.GL_ONE_MINUS_SRC_COLOR, SGL.GL_ONE_MINUS_SRC_ALPHA);
231         }
232         postdraw();
233     }
234 
235     /**
236      * Clear the state of the alpha map across the entire screen. This sets alpha to 0 everywhere, meaning in
237      * {@link Graphics#MODE_ALPHA_BLEND} nothing will be drawn.
238      */
239     public void clearAlphaMap() {
240         pushTransform();
241         GL.glLoadIdentity();
242 
243         int originalMode = currentDrawingMode;
244         setDrawMode(MODE_ALPHA_MAP);
245         setColor(new Color(0, 0, 0, 0));
246         fillRect(0, 0, screenWidth, screenHeight);
247         setColor(currentColor);
248         setDrawMode(originalMode);
249 
250         popTransform();
251     }
252 
253     /**
254      * Must be called before all OpenGL operations to maintain context for dynamic images
255      */
256     private void predraw() {
257         setCurrent(this);
258     }
259 
260     /**
261      * Must be called after all OpenGL operations to maintain context for dynamic images
262      */
263     private void postdraw() {
264     }
265 
266     /**
267      * Enable rendering to this graphics context
268      */
269     protected void enable() {
270     }
271 
272     /**
273      * Flush this graphics context to the underlying rendering context
274      */
275     protected void flush() {
276         if (currentGraphics == this) {
277             currentGraphics.disable();
278             currentGraphics = null;
279         }
280     }
281 
282     /**
283      * Disable rendering to this graphics context
284      */
285     protected void disable() {
286     }
287 
288     /**
289      * Get the current font
290      *
291      * @return The current font
292      */
293     public Font getFont() {
294         return font;
295     }
296 
297     /**
298      * Set the background colour of the graphics context. This colour is used when clearing the context. Note that
299      * calling this method alone does not cause the context to be cleared.
300      *
301      * @param color
302      *            The background color of the graphics context
303      */
304     public void setBackground(@Nonnull Color color) {
305         predraw();
306         GL.glClearColor(color.r, color.g, color.b, color.a);
307         postdraw();
308     }
309 
310     /**
311      * Get the current graphics context background color
312      *
313      * @return The background color of this graphics context
314      */
315     @Nonnull
316     public Color getBackground() {
317         predraw();
318         FloatBuffer buffer = BufferUtils.createFloatBuffer(16);
319         GL.glGetFloat(SGL.GL_COLOR_CLEAR_VALUE, buffer);
320         postdraw();
321 
322         return new Color(buffer);
323     }
324 
325     /**
326      * Clear the graphics context
327      */
328     public void clear() {
329         predraw();
330         GL.glClear(SGL.GL_COLOR_BUFFER_BIT);
331         postdraw();
332     }
333 
334     /**
335      * Reset the transformation on this graphics context
336      */
337     public void resetTransform() {
338         sx = 1;
339         sy = 1;
340 
341         if (pushed) {
342             predraw();
343             GL.glPopMatrix();
344             pushed = false;
345             postdraw();
346         }
347     }
348 
349     /**
350      * Check if we've pushed the previous matrix, if not then push it now.
351      */
352     private void checkPush() {
353         if (!pushed) {
354             predraw();
355             GL.glPushMatrix();
356             pushed = true;
357             postdraw();
358         }
359     }
360 
361     /**
362      * Apply a scaling factor to everything drawn on the graphics context
363      *
364      * @param sx
365      *            The scaling factor to apply to the x axis
366      * @param sy
367      *            The scaling factor to apply to the y axis
368      */
369     public void scale(float sx, float sy) {
370         this.sx = this.sx * sx;
371         this.sy = this.sy * sy;
372 
373         checkPush();
374 
375         predraw();
376         GL.glScalef(sx, sy, 1);
377         postdraw();
378     }
379 
380     /**
381      * Apply a rotation to everything draw on the graphics context
382      *
383      * @param rx
384      *            The x coordinate of the center of rotation
385      * @param ry
386      *            The y coordinate of the center of rotation
387      * @param ang
388      *            The angle (in degrees) to rotate by
389      */
390     public void rotate(float rx, float ry, float ang) {
391         checkPush();
392 
393         predraw();
394         translate(rx, ry);
395         GL.glRotatef(ang, 0, 0, 1);
396         translate(-rx, -ry);
397         postdraw();
398     }
399 
400     /**
401      * Apply a translation to everything drawn to the context
402      *
403      * @param x
404      *            The amount to translate on the x-axis
405      * @param y
406      *            The amount of translate on the y-axis
407      */
408     void translate(float x, float y) {
409         checkPush();
410 
411         predraw();
412         GL.glTranslatef(x, y, 0);
413         postdraw();
414     }
415 
416     /**
417      * Set the font to be used when rendering text
418      *
419      * @param font
420      *            The font to be used when rendering text
421      */
422     public void setFont(Font font) {
423         this.font = font;
424     }
425 
426     /**
427      * Reset to using the default font for this context
428      */
429     public void resetFont() {
430         font = DEFAULT_FONT;
431     }
432 
433     /**
434      * Set the color to use when rendering to this context
435      *
436      * @param color
437      *            The color to use when rendering to this context
438      */
439     void setColor(@Nullable Color color) {
440         if (color == null) {
441             return;
442         }
443 
444         currentColor = new Color(color);
445         predraw();
446         currentColor.bind();
447         postdraw();
448     }
449 
450     /**
451      * Get the color in use by this graphics context
452      *
453      * @return The color in use by this graphics context
454      */
455     @Nonnull
456     public Color getColor() {
457         return new Color(currentColor);
458     }
459 
460     /**
461      * Draw a line on the canvas in the current colour
462      *
463      * @param x1
464      *            The x coordinate of the start point
465      * @param y1
466      *            The y coordinate of the start point
467      * @param x2
468      *            The x coordinate of the end point
469      * @param y2
470      *            The y coordinate of the end point
471      */
472     void drawLine(float x1, float y1, float x2, float y2) {
473         float lineWidth = this.lineWidth - 1;
474 
475         if (LSR.applyGLLineFixes()) {
476             if (x1 == x2) {
477                 if (y1 > y2) {
478                     float temp = y2;
479                     y2 = y1;
480                     y1 = temp;
481                 }
482                 float step = 1 / sy;
483                 lineWidth = lineWidth / sy;
484                 fillRect(x1 - (lineWidth / 2.0f), y1 - (lineWidth / 2.0f), lineWidth + step, (y2 - y1) + lineWidth + step);
485                 return;
486             } else if (y1 == y2) {
487                 if (x1 > x2) {
488                     float temp = x2;
489                     x2 = x1;
490                     x1 = temp;
491                 }
492                 float step = 1 / sx;
493                 lineWidth = lineWidth / sx;
494                 fillRect(x1 - (lineWidth / 2.0f), y1 - (lineWidth / 2.0f), (x2 - x1) + lineWidth + step, lineWidth + step);
495                 return;
496             }
497         }
498 
499         predraw();
500         currentColor.bind();
501         TextureImpl.bindNone();
502 
503         LSR.start();
504         LSR.vertex(x1, y1);
505         LSR.vertex(x2, y2);
506         LSR.end();
507 
508         postdraw();
509     }
510 
511     /**
512      * Draw the outline of the given shape.
513      *
514      * @param shape
515      *            The shape to draw.
516      * @param fill
517      *            The fill type to apply
518      */
519     public void draw(@Nonnull Shape shape, @Nonnull ShapeFill fill) {
520         predraw();
521         TextureImpl.bindNone();
522 
523         ShapeRenderer.draw(shape, fill);
524 
525         currentColor.bind();
526         postdraw();
527     }
528 
529     /**
530      * Draw the the given shape filled in.
531      *
532      * @param shape
533      *            The shape to fill.
534      * @param fill
535      *            The fill type to apply
536      */
537     public void fill(@Nonnull Shape shape, @Nonnull ShapeFill fill) {
538         predraw();
539         TextureImpl.bindNone();
540 
541         ShapeRenderer.fill(shape, fill);
542 
543         currentColor.bind();
544         postdraw();
545     }
546 
547     /**
548      * Draw the outline of the given shape.
549      *
550      * @param shape
551      *            The shape to draw.
552      */
553     public void draw(@Nonnull Shape shape) {
554         predraw();
555         TextureImpl.bindNone();
556         currentColor.bind();
557 
558         ShapeRenderer.draw(shape);
559 
560         postdraw();
561     }
562 
563     /**
564      * Draw the the given shape filled in.
565      *
566      * @param shape
567      *            The shape to fill.
568      */
569     public void fill(@Nonnull Shape shape) {
570         predraw();
571         TextureImpl.bindNone();
572         currentColor.bind();
573 
574         ShapeRenderer.fill(shape);
575 
576         postdraw();
577     }
578 
579     /**
580      * Draw the the given shape filled in with a texture
581      *
582      * @param shape
583      *            The shape to texture.
584      * @param image
585      *            The image to tile across the shape
586      */
587     public void texture(@Nonnull Shape shape, @Nonnull Image image) {
588         texture(shape, image, 0.01f, 0.01f, false);
589     }
590 
591     /**
592      * Draw the the given shape filled in with a texture
593      *
594      * @param shape
595      *            The shape to texture.
596      * @param image
597      *            The image to tile across the shape
598      * @param fill
599      *            The shape fill to apply
600      */
601     public void texture(@Nonnull Shape shape, @Nonnull Image image, @Nonnull ShapeFill fill) {
602         texture(shape, image, 0.01f, 0.01f, fill);
603     }
604 
605     /**
606      * Draw the the given shape filled in with a texture
607      *
608      * @param shape
609      *            The shape to texture.
610      * @param image
611      *            The image to tile across the shape
612      * @param fit
613      *            True if we want to fit the image on to the shape
614      */
615     public void texture(@Nonnull Shape shape, @Nonnull Image image, boolean fit) {
616         if (fit) {
617             texture(shape, image, 1, 1, true);
618         } else {
619             texture(shape, image, 0.01f, 0.01f, false);
620         }
621     }
622 
623     /**
624      * Draw the the given shape filled in with a texture
625      *
626      * @param shape
627      *            The shape to texture.
628      * @param image
629      *            The image to tile across the shape
630      * @param scaleX
631      *            The scale to apply on the x axis for texturing
632      * @param scaleY
633      *            The scale to apply on the y axis for texturing
634      */
635     public void texture(@Nonnull Shape shape, @Nonnull Image image, float scaleX, float scaleY) {
636         texture(shape, image, scaleX, scaleY, false);
637     }
638 
639     /**
640      * Draw the the given shape filled in with a texture
641      *
642      * @param shape
643      *            The shape to texture.
644      * @param image
645      *            The image to tile across the shape
646      * @param scaleX
647      *            The scale to apply on the x axis for texturing
648      * @param scaleY
649      *            The scale to apply on the y axis for texturing
650      * @param fit
651      *            True if we want to fit the image on to the shape
652      */
653     void texture(@Nonnull Shape shape, @Nonnull Image image, float scaleX, float scaleY, boolean fit) {
654         predraw();
655         TextureImpl.bindNone();
656         currentColor.bind();
657 
658         if (fit) {
659             ShapeRenderer.textureFit(shape, image, scaleX, scaleY);
660         } else {
661             ShapeRenderer.texture(shape, image, scaleX, scaleY);
662         }
663 
664         postdraw();
665     }
666 
667     /**
668      * Draw the the given shape filled in with a texture
669      *
670      * @param shape
671      *            The shape to texture.
672      * @param image
673      *            The image to tile across the shape
674      * @param scaleX
675      *            The scale to apply on the x axis for texturing
676      * @param scaleY
677      *            The scale to apply on the y axis for texturing
678      * @param fill
679      *            The shape fill to apply
680      */
681     void texture(@Nonnull Shape shape, @Nonnull Image image, float scaleX, float scaleY, @Nonnull ShapeFill fill) {
682         predraw();
683         TextureImpl.bindNone();
684         currentColor.bind();
685 
686         ShapeRenderer.texture(shape, image, scaleX, scaleY, fill);
687 
688         postdraw();
689     }
690 
691     /**
692      * Draw a rectangle to the canvas in the current colour
693      *
694      * @param x1
695      *            The x coordinate of the top left corner
696      * @param y1
697      *            The y coordinate of the top left corner
698      * @param width
699      *            The width of the rectangle to draw
700      * @param height
701      *            The height of the rectangle to draw
702      */
703     void drawRect(float x1, float y1, float width, float height) {
704         getLineWidth();
705 
706         drawLine(x1, y1, x1 + width, y1);
707         drawLine(x1 + width, y1, x1 + width, y1 + height);
708         drawLine(x1 + width, y1 + height, x1, y1 + height);
709         drawLine(x1, y1 + height, x1, y1);
710     }
711 
712     /**
713      * Clear the clipping being applied. This will allow graphics to be drawn anywhere on the screen
714      */
715     void clearClip() {
716         clip = null;
717         predraw();
718         GL.glDisable(SGL.GL_SCISSOR_TEST);
719         postdraw();
720     }
721 
722     /**
723      * Set clipping that controls which areas of the world will be drawn to. Note that world clip is different from
724      * standard screen clip in that it's defined in the space of the current world coordinate - i.e. it's affected by
725      * translate, rotate, scale etc.
726      *
727      * @param x
728      *            The x coordinate of the top left corner of the allowed area
729      * @param y
730      *            The y coordinate of the top left corner of the allowed area
731      * @param width
732      *            The width of the allowed area
733      * @param height
734      *            The height of the allowed area
735      */
736     void setWorldClip(float x, float y, float width, float height) {
737         predraw();
738         worldClipRecord = new Rectangle(x, y, width, height);
739 
740         GL.glEnable(SGL.GL_CLIP_PLANE0);
741         worldClip.put(1).put(0).put(0).put(-x).flip();
742         GL.glClipPlane(SGL.GL_CLIP_PLANE0, worldClip);
743         GL.glEnable(SGL.GL_CLIP_PLANE1);
744         worldClip.put(-1).put(0).put(0).put(x + width).flip();
745         GL.glClipPlane(SGL.GL_CLIP_PLANE1, worldClip);
746 
747         GL.glEnable(SGL.GL_CLIP_PLANE2);
748         worldClip.put(0).put(1).put(0).put(-y).flip();
749         GL.glClipPlane(SGL.GL_CLIP_PLANE2, worldClip);
750         GL.glEnable(SGL.GL_CLIP_PLANE3);
751         worldClip.put(0).put(-1).put(0).put(y + height).flip();
752         GL.glClipPlane(SGL.GL_CLIP_PLANE3, worldClip);
753         postdraw();
754     }
755 
756     /**
757      * Clear world clipping setup. This does not effect screen clipping
758      */
759     void clearWorldClip() {
760         predraw();
761         worldClipRecord = null;
762         GL.glDisable(SGL.GL_CLIP_PLANE0);
763         GL.glDisable(SGL.GL_CLIP_PLANE1);
764         GL.glDisable(SGL.GL_CLIP_PLANE2);
765         GL.glDisable(SGL.GL_CLIP_PLANE3);
766         postdraw();
767     }
768 
769     /**
770      * Set the world clip to be applied
771      *
772      * @see #setWorldClip(float, float, float, float)
773      * @param clip
774      *            The area still visible
775      */
776     void setWorldClip(@Nullable Rectangle clip) {
777         if (clip == null) {
778             clearWorldClip();
779         } else {
780             setWorldClip(clip.getX(), clip.getY(), clip.getWidth(), clip.getHeight());
781         }
782     }
783 
784     /**
785      * Get the last set world clip or null of the world clip isn't set
786      *
787      * @return The last set world clip rectangle
788      */
789     @Nullable
790     Rectangle getWorldClip() {
791         return worldClipRecord;
792     }
793 
794     /**
795      * Set the clipping to apply to the drawing. Note that this clipping takes no note of the transforms that have been
796      * applied to the context and is always in absolute screen space coordinates.
797      *
798      * @param x
799      *            The x coordinate of the top left corner of the allowed area
800      * @param y
801      *            The y coordinate of the top left corner of the allowed area
802      * @param width
803      *            The width of the allowed area
804      * @param height
805      *            The height of the allowed area
806      */
807     void setClip(int x, int y, int width, int height) {
808         predraw();
809 
810         if (clip == null) {
811             GL.glEnable(SGL.GL_SCISSOR_TEST);
812             clip = new Rectangle(x, y, width, height);
813         } else {
814             clip.setBounds(x, y, width, height);
815         }
816 
817         GL.glScissor(x, screenHeight - y - height, width, height);
818         postdraw();
819     }
820 
821     /**
822      * Set the clipping to apply to the drawing. Note that this clipping takes no note of the transforms that have been
823      * applied to the context and is always in absolute screen space coordinates.
824      *
825      * @param rect
826      *            The rectangle describing the clipped area in screen coordinates
827      */
828     public void setClip(@Nullable Rectangle rect) {
829         if (rect == null) {
830             clearClip();
831             return;
832         }
833 
834         setClip((int) rect.getX(), (int) rect.getY(), (int) rect.getWidth(), (int) rect.getHeight());
835     }
836 
837     /**
838      * Return the currently applied clipping rectangle
839      *
840      * @return The current applied clipping rectangle or null if no clipping is applied
841      */
842     @Nullable
843     public Rectangle getClip() {
844         return clip;
845     }
846 
847     /**
848      * Tile a rectangle with a pattern specifing the offset from the top corner that one tile should match
849      *
850      * @param x
851      *            The x coordinate of the rectangle
852      * @param y
853      *            The y coordinate of the rectangle
854      * @param width
855      *            The width of the rectangle
856      * @param height
857      *            The height of the rectangle
858      * @param pattern
859      *            The image to tile across the rectangle
860      * @param offX
861      *            The offset on the x axis from the top left corner
862      * @param offY
863      *            The offset on the y axis from the top left corner
864      */
865     public void fillRect(float x, float y, float width, float height, @Nonnull Image pattern, float offX, float offY) {
866         int cols = ((int) Math.ceil(width / pattern.getWidth())) + 2;
867         int rows = ((int) Math.ceil(height / pattern.getHeight())) + 2;
868 
869         Rectangle preClip = getWorldClip();
870         setWorldClip(x, y, width, height);
871 
872         predraw();
873         // Draw all the quads we need
874         for (int c = 0; c < cols; c++) {
875             for (int r = 0; r < rows; r++) {
876                 pattern.draw(c * pattern.getWidth() + x - offX, r * pattern.getHeight() + y - offY);
877             }
878         }
879         postdraw();
880 
881         setWorldClip(preClip);
882     }
883 
884     /**
885      * Fill a rectangle on the canvas in the current color
886      *
887      * @param x1
888      *            The x coordinate of the top left corner
889      * @param y1
890      *            The y coordinate of the top left corner
891      * @param width
892      *            The width of the rectangle to fill
893      * @param height
894      *            The height of the rectangle to fill
895      */
896     void fillRect(float x1, float y1, float width, float height) {
897         predraw();
898         TextureImpl.bindNone();
899         currentColor.bind();
900 
901         GL.glBegin(SGL.GL_QUADS);
902         GL.glVertex2f(x1, y1);
903         GL.glVertex2f(x1 + width, y1);
904         GL.glVertex2f(x1 + width, y1 + height);
905         GL.glVertex2f(x1, y1 + height);
906         GL.glEnd();
907         postdraw();
908     }
909 
910     /**
911      * Draw an oval to the canvas
912      *
913      * @param x1
914      *            The x coordinate of the top left corner of a box containing the oval
915      * @param y1
916      *            The y coordinate of the top left corner of a box containing the oval
917      * @param width
918      *            The width of the oval
919      * @param height
920      *            The height of the oval
921      */
922     public void drawOval(float x1, float y1, float width, float height) {
923         drawOval(x1, y1, width, height, DEFAULT_SEGMENTS);
924     }
925 
926     /**
927      * Draw an oval to the canvas
928      *
929      * @param x1
930      *            The x coordinate of the top left corner of a box containing the oval
931      * @param y1
932      *            The y coordinate of the top left corner of a box containing the oval
933      * @param width
934      *            The width of the oval
935      * @param height
936      *            The height of the oval
937      * @param segments
938      *            The number of line segments to use when drawing the oval
939      */
940     void drawOval(float x1, float y1, float width, float height, int segments) {
941         drawArc(x1, y1, width, height, segments, 0, 360);
942     }
943 
944     /**
945      * Draw an oval to the canvas
946      *
947      * @param x1
948      *            The x coordinate of the top left corner of a box containing the arc
949      * @param y1
950      *            The y coordinate of the top left corner of a box containing the arc
951      * @param width
952      *            The width of the arc
953      * @param height
954      *            The height of the arc
955      * @param start
956      *            The angle the arc starts at
957      * @param end
958      *            The angle the arc ends at
959      */
960     public void drawArc(float x1, float y1, float width, float height, float start, float end) {
961         drawArc(x1, y1, width, height, DEFAULT_SEGMENTS, start, end);
962     }
963 
964     /**
965      * Draw an oval to the canvas
966      *
967      * @param x1
968      *            The x coordinate of the top left corner of a box containing the arc
969      * @param y1
970      *            The y coordinate of the top left corner of a box containing the arc
971      * @param width
972      *            The width of the arc
973      * @param height
974      *            The height of the arc
975      * @param segments
976      *            The number of line segments to use when drawing the arc
977      * @param start
978      *            The angle the arc starts at
979      * @param end
980      *            The angle the arc ends at
981      */
982     void drawArc(float x1, float y1, float width, float height, int segments, float start, float end) {
983         predraw();
984         TextureImpl.bindNone();
985         currentColor.bind();
986 
987         while (end < start) {
988             end += 360;
989         }
990 
991         float cx = x1 + (width / 2.0f);
992         float cy = y1 + (height / 2.0f);
993 
994         LSR.start();
995         int step = 360 / segments;
996 
997         for (int a = (int) start; a < (int) (end + step); a += step) {
998             float ang = a;
999             if (ang > end) {
1000                 ang = end;
1001             }
1002             float x = (float) (cx + (FastTrig.cos(Math.toRadians(ang)) * width / 2.0f));
1003             float y = (float) (cy + (FastTrig.sin(Math.toRadians(ang)) * height / 2.0f));
1004 
1005             LSR.vertex(x, y);
1006         }
1007         LSR.end();
1008         postdraw();
1009     }
1010 
1011     /**
1012      * Fill an oval to the canvas
1013      *
1014      * @param x1
1015      *            The x coordinate of the top left corner of a box containing the oval
1016      * @param y1
1017      *            The y coordinate of the top left corner of a box containing the oval
1018      * @param width
1019      *            The width of the oval
1020      * @param height
1021      *            The height of the oval
1022      */
1023     public void fillOval(float x1, float y1, float width, float height) {
1024         fillOval(x1, y1, width, height, DEFAULT_SEGMENTS);
1025     }
1026 
1027     /**
1028      * Fill an oval to the canvas
1029      *
1030      * @param x1
1031      *            The x coordinate of the top left corner of a box containing the oval
1032      * @param y1
1033      *            The y coordinate of the top left corner of a box containing the oval
1034      * @param width
1035      *            The width of the oval
1036      * @param height
1037      *            The height of the oval
1038      * @param segments
1039      *            The number of line segments to use when filling the oval
1040      */
1041     void fillOval(float x1, float y1, float width, float height, int segments) {
1042         fillArc(x1, y1, width, height, segments, 0, 360);
1043     }
1044 
1045     /**
1046      * Fill an arc to the canvas (a wedge)
1047      *
1048      * @param x1
1049      *            The x coordinate of the top left corner of a box containing the arc
1050      * @param y1
1051      *            The y coordinate of the top left corner of a box containing the arc
1052      * @param width
1053      *            The width of the arc
1054      * @param height
1055      *            The height of the arc
1056      * @param start
1057      *            The angle the arc starts at
1058      * @param end
1059      *            The angle the arc ends at
1060      */
1061     public void fillArc(float x1, float y1, float width, float height, float start, float end) {
1062         fillArc(x1, y1, width, height, DEFAULT_SEGMENTS, start, end);
1063     }
1064 
1065     /**
1066      * Fill an arc to the canvas (a wedge)
1067      *
1068      * @param x1
1069      *            The x coordinate of the top left corner of a box containing the arc
1070      * @param y1
1071      *            The y coordinate of the top left corner of a box containing the arc
1072      * @param width
1073      *            The width of the arc
1074      * @param height
1075      *            The height of the arc
1076      * @param segments
1077      *            The number of line segments to use when filling the arc
1078      * @param start
1079      *            The angle the arc starts at
1080      * @param end
1081      *            The angle the arc ends at
1082      */
1083     void fillArc(float x1, float y1, float width, float height, int segments, float start, float end) {
1084         predraw();
1085         TextureImpl.bindNone();
1086         currentColor.bind();
1087 
1088         while (end < start) {
1089             end += 360;
1090         }
1091 
1092         float cx = x1 + (width / 2.0f);
1093         float cy = y1 + (height / 2.0f);
1094 
1095         GL.glBegin(SGL.GL_TRIANGLE_FAN);
1096         int step = 360 / segments;
1097 
1098         GL.glVertex2f(cx, cy);
1099 
1100         for (int a = (int) start; a < (int) (end + step); a += step) {
1101             float ang = a;
1102             if (ang > end) {
1103                 ang = end;
1104             }
1105 
1106             float x = (float) (cx + (FastTrig.cos(Math.toRadians(ang)) * width / 2.0f));
1107             float y = (float) (cy + (FastTrig.sin(Math.toRadians(ang)) * height / 2.0f));
1108 
1109             GL.glVertex2f(x, y);
1110         }
1111         GL.glEnd();
1112 
1113         if (antialias) {
1114             GL.glBegin(SGL.GL_TRIANGLE_FAN);
1115             GL.glVertex2f(cx, cy);
1116             if (end != 360) {
1117                 end -= 10;
1118             }
1119 
1120             for (int a = (int) start; a < (int) (end + step); a += step) {
1121                 float ang = a;
1122                 if (ang > end) {
1123                     ang = end;
1124                 }
1125 
1126                 float x = (float) (cx + (FastTrig.cos(Math.toRadians(ang + 10)) * width / 2.0f));
1127                 float y = (float) (cy + (FastTrig.sin(Math.toRadians(ang + 10)) * height / 2.0f));
1128 
1129                 GL.glVertex2f(x, y);
1130             }
1131             GL.glEnd();
1132         }
1133 
1134         postdraw();
1135     }
1136 
1137     /**
1138      * Draw a rounded rectangle
1139      *
1140      * @param x
1141      *            The x coordinate of the top left corner of the rectangle
1142      * @param y
1143      *            The y coordinate of the top left corner of the rectangle
1144      * @param width
1145      *            The width of the rectangle
1146      * @param height
1147      *            The height of the rectangle
1148      * @param cornerRadius
1149      *            The radius of the rounded edges on the corners
1150      */
1151     public void drawRoundRect(float x, float y, float width, float height, int cornerRadius) {
1152         drawRoundRect(x, y, width, height, cornerRadius, DEFAULT_SEGMENTS);
1153     }
1154 
1155     /**
1156      * Draw a rounded rectangle
1157      *
1158      * @param x
1159      *            The x coordinate of the top left corner of the rectangle
1160      * @param y
1161      *            The y coordinate of the top left corner of the rectangle
1162      * @param width
1163      *            The width of the rectangle
1164      * @param height
1165      *            The height of the rectangle
1166      * @param cornerRadius
1167      *            The radius of the rounded edges on the corners
1168      * @param segs
1169      *            The number of segments to make the corners out of
1170      */
1171     void drawRoundRect(float x, float y, float width, float height, int cornerRadius, int segs) {
1172         if (cornerRadius < 0)
1173             throw new IllegalArgumentException("corner radius must be > 0");
1174         if (cornerRadius == 0) {
1175             drawRect(x, y, width, height);
1176             return;
1177         }
1178 
1179         int mr = (int) Math.min(width, height) / 2;
1180         // make sure that w & h are larger than 2*cornerRadius
1181         if (cornerRadius > mr) {
1182             cornerRadius = mr;
1183         }
1184 
1185         drawLine(x + cornerRadius, y, x + width - cornerRadius, y);
1186         drawLine(x, y + cornerRadius, x, y + height - cornerRadius);
1187         drawLine(x + width, y + cornerRadius, x + width, y + height - cornerRadius);
1188         drawLine(x + cornerRadius, y + height, x + width - cornerRadius, y + height);
1189 
1190         float d = cornerRadius * 2;
1191         // bottom right - 0, 90
1192         drawArc(x + width - d, y + height - d, d, d, segs, 0, 90);
1193         // bottom left - 90, 180
1194         drawArc(x, y + height - d, d, d, segs, 90, 180);
1195         // top right - 270, 360
1196         drawArc(x + width - d, y, d, d, segs, 270, 360);
1197         // top left - 180, 270
1198         drawArc(x, y, d, d, segs, 180, 270);
1199     }
1200 
1201     /**
1202      * Fill a rounded rectangle
1203      *
1204      * @param x
1205      *            The x coordinate of the top left corner of the rectangle
1206      * @param y
1207      *            The y coordinate of the top left corner of the rectangle
1208      * @param width
1209      *            The width of the rectangle
1210      * @param height
1211      *            The height of the rectangle
1212      * @param cornerRadius
1213      *            The radius of the rounded edges on the corners
1214      */
1215     public void fillRoundRect(float x, float y, float width, float height, int cornerRadius) {
1216         fillRoundRect(x, y, width, height, cornerRadius, DEFAULT_SEGMENTS);
1217     }
1218 
1219     /**
1220      * Fill a rounded rectangle
1221      *
1222      * @param x
1223      *            The x coordinate of the top left corner of the rectangle
1224      * @param y
1225      *            The y coordinate of the top left corner of the rectangle
1226      * @param width
1227      *            The width of the rectangle
1228      * @param height
1229      *            The height of the rectangle
1230      * @param cornerRadius
1231      *            The radius of the rounded edges on the corners
1232      * @param segs
1233      *            The number of segments to make the corners out of
1234      */
1235     void fillRoundRect(float x, float y, float width, float height, int cornerRadius, int segs) {
1236         if (cornerRadius < 0)
1237             throw new IllegalArgumentException("corner radius must be > 0");
1238         if (cornerRadius == 0) {
1239             fillRect(x, y, width, height);
1240             return;
1241         }
1242 
1243         int mr = (int) Math.min(width, height) / 2;
1244         // make sure that w & h are larger than 2*cornerRadius
1245         if (cornerRadius > mr) {
1246             cornerRadius = mr;
1247         }
1248 
1249         float d = cornerRadius * 2;
1250 
1251         fillRect(x + cornerRadius, y, width - d, cornerRadius);
1252         fillRect(x, y + cornerRadius, cornerRadius, height - d);
1253         fillRect(x + width - cornerRadius, y + cornerRadius, cornerRadius, height - d);
1254         fillRect(x + cornerRadius, y + height - cornerRadius, width - d, cornerRadius);
1255         fillRect(x + cornerRadius, y + cornerRadius, width - d, height - d);
1256 
1257         // bottom right - 0, 90
1258         fillArc(x + width - d, y + height - d, d, d, segs, 0, 90);
1259         // bottom left - 90, 180
1260         fillArc(x, y + height - d, d, d, segs, 90, 180);
1261         // top right - 270, 360
1262         fillArc(x + width - d, y, d, d, segs, 270, 360);
1263         // top left - 180, 270
1264         fillArc(x, y, d, d, segs, 180, 270);
1265     }
1266 
1267     /**
1268      * Set the with of the line to be used when drawing line based primitives
1269      *
1270      * @param width
1271      *            The width of the line to be used when drawing line based primitives
1272      */
1273     public void setLineWidth(float width) {
1274         predraw();
1275         this.lineWidth = width;
1276         LSR.setWidth(width);
1277         GL.glPointSize(width);
1278         postdraw();
1279     }
1280 
1281     /**
1282      * Get the width of lines being drawn in this context
1283      *
1284      * @return The width of lines being draw in this context
1285      */
1286     float getLineWidth() {
1287         return lineWidth;
1288     }
1289 
1290     /**
1291      * Reset the line width in use to the default for this graphics context
1292      */
1293     public void resetLineWidth() {
1294         predraw();
1295 
1296         Renderer.getLineStripRenderer().setWidth(1.0f);
1297         GL.glLineWidth(1.0f);
1298         GL.glPointSize(1.0f);
1299 
1300         postdraw();
1301     }
1302 
1303     /**
1304      * Indicate if we should antialias as we draw primitives
1305      *
1306      * @param anti
1307      *            True if we should antialias
1308      */
1309     public void setAntiAlias(boolean anti) {
1310         predraw();
1311         antialias = anti;
1312         LSR.setAntiAlias(anti);
1313         if (anti) {
1314             GL.glEnable(SGL.GL_POLYGON_SMOOTH);
1315         } else {
1316             GL.glDisable(SGL.GL_POLYGON_SMOOTH);
1317         }
1318         postdraw();
1319     }
1320 
1321     /**
1322      * True if antialiasing has been turned on for this graphics context
1323      *
1324      * @return True if antialiasing has been turned on for this graphics context
1325      */
1326     public boolean isAntiAlias() {
1327         return antialias;
1328     }
1329 
1330     /**
1331      * Draw a string to the screen using the current font
1332      *
1333      * @param str
1334      *            The string to draw
1335      * @param x
1336      *            The x coordinate to draw the string at
1337      * @param y
1338      *            The y coordinate to draw the string at
1339      */
1340     public void drawString(String str, float x, float y) {
1341         predraw();
1342         font.drawString(x, y, str, currentColor);
1343         postdraw();
1344     }
1345 
1346     /**
1347      * Draw an image to the screen
1348      *
1349      * @param image
1350      *            The image to draw to the screen
1351      * @param x
1352      *            The x location at which to draw the image
1353      * @param y
1354      *            The y location at which to draw the image
1355      * @param col
1356      *            The color to apply to the image as a filter
1357      */
1358     void drawImage(@Nonnull Image image, float x, float y, Color col) {
1359         predraw();
1360         image.draw(x, y, col);
1361         currentColor.bind();
1362         postdraw();
1363     }
1364 
1365     /**
1366      * Draw an animation to this graphics context
1367      *
1368      * @param anim
1369      *            The animation to be drawn
1370      * @param x
1371      *            The x position to draw the animation at
1372      * @param y
1373      *            The y position to draw the animation at
1374      */
1375     public void drawAnimation(@Nonnull Animation anim, float x, float y) {
1376         drawAnimation(anim, x, y, Color.white);
1377     }
1378 
1379     /**
1380      * Draw an animation to this graphics context
1381      *
1382      * @param anim
1383      *            The animation to be drawn
1384      * @param x
1385      *            The x position to draw the animation at
1386      * @param y
1387      *            The y position to draw the animation at
1388      * @param col
1389      *            The color to apply to the animation as a filter
1390      */
1391     void drawAnimation(@Nonnull Animation anim, float x, float y, Color col) {
1392         predraw();
1393         anim.draw(x, y, col);
1394         currentColor.bind();
1395         postdraw();
1396     }
1397 
1398     /**
1399      * Draw an image to the screen
1400      *
1401      * @param image
1402      *            The image to draw to the screen
1403      * @param x
1404      *            The x location at which to draw the image
1405      * @param y
1406      *            The y location at which to draw the image
1407      */
1408     protected void drawImage(@Nonnull Image image, float x, float y) {
1409         drawImage(image, x, y, Color.white);
1410     }
1411 
1412     /**
1413      * Draw a section of an image at a particular location and scale on the screen
1414      *
1415      * @param image
1416      *            The image to draw a section of
1417      * @param x
1418      *            The x position to draw the image
1419      * @param y
1420      *            The y position to draw the image
1421      * @param x2
1422      *            The x position of the bottom right corner of the drawn image
1423      * @param y2
1424      *            The y position of the bottom right corner of the drawn image
1425      * @param srcx
1426      *            The x position of the rectangle to draw from this image (i.e. relative to the image)
1427      * @param srcy
1428      *            The y position of the rectangle to draw from this image (i.e. relative to the image)
1429      * @param srcx2
1430      *            The x position of the bottom right cornder of rectangle to draw from this image (i.e. relative to the
1431      *            image)
1432      * @param srcy2
1433      *            The t position of the bottom right cornder of rectangle to draw from this image (i.e. relative to the
1434      *            image)
1435      */
1436     void drawImage(@Nonnull Image image, float x, float y, float x2, float y2, float srcx, float srcy, float srcx2, float srcy2) {
1437         predraw();
1438         image.draw(x, y, x2, y2, srcx, srcy, srcx2, srcy2);
1439         currentColor.bind();
1440         postdraw();
1441     }
1442 
1443     /**
1444      * Draw a section of an image at a particular location and scale on the screen
1445      *
1446      * @param image
1447      *            The image to draw a section of
1448      * @param x
1449      *            The x position to draw the image
1450      * @param y
1451      *            The y position to draw the image
1452      * @param srcx
1453      *            The x position of the rectangle to draw from this image (i.e. relative to the image)
1454      * @param srcy
1455      *            The y position of the rectangle to draw from this image (i.e. relative to the image)
1456      * @param srcx2
1457      *            The x position of the bottom right cornder of rectangle to draw from this image (i.e. relative to the
1458      *            image)
1459      * @param srcy2
1460      *            The t position of the bottom right cornder of rectangle to draw from this image (i.e. relative to the
1461      *            image)
1462      */
1463     public void drawImage(@Nonnull Image image, float x, float y, float srcx, float srcy, float srcx2, float srcy2) {
1464         drawImage(image, x, y, x + image.getWidth(), y + image.getHeight(), srcx, srcy, srcx2, srcy2);
1465     }
1466 
1467     /**
1468      * Copy an area of the rendered screen into an image. The width and height of the area are assumed to match that of
1469      * the image, and the destination offset is (0, 0).
1470      *
1471      * @param target
1472      *            The target image
1473      * @param x
1474      *            The x position to copy from
1475      * @param y
1476      *            The y position to copy from
1477      */
1478     public void copyArea(@Nonnull Image target, int x, int y) {
1479         copyArea(target, x, y, 0, 0, target.getWidth(), target.getHeight());
1480     }
1481 
1482     /**
1483      * Copies a sub-section of the rendered screen into the given image. The x/y offset determines where
1484      * on the target image to place the copied data; the width/height values determine how much to copy
1485      * from the screen.
1486      *
1487      * Note that invalid values, such as a height that is larger than the image's texture, may lead to
1488      * unexpected results.
1489      *
1490      * @param target the target image to copy the screen into
1491      * @param x the x position of the screen to start copying
1492      * @param y the y position of the screen to start copying
1493      * @param xoff the x destination on the target at which to place the copied data
1494      * @param yoff the y destination on the target at which to place the copied data
1495      * @param width the width of the data to copy from the screen
1496      * @param height the height of the data to copy from the screen
1497      */
1498     void copyArea(@Nonnull Image target, int x, int y, int xoff, int yoff, int width, int height) {
1499         predraw();
1500         target.getTexture();
1501         target.bind();
1502         if (isYFlipped()) {
1503             GL11.glCopyTexSubImage2D(SGL.GL_TEXTURE_2D, 0, xoff, yoff, x, y, width, height);
1504         } else {
1505             int yoff2 = target.getHeight()-height-yoff;
1506             GL11.glCopyTexSubImage2D(SGL.GL_TEXTURE_2D, 0, xoff, yoff2, x, screenHeight - (y+height), width, height);
1507             target.ensureInverted();
1508         }
1509         postdraw();
1510     }
1511 
1512     /**
1513      * Translate an unsigned int into a signed integer
1514      *
1515      * @param b
1516      *            The byte to convert
1517      * @return The integer value represented by the byte
1518      */
1519     private int translate(byte b) {
1520         if (b < 0) {
1521             return 256 + b;
1522         }
1523 
1524         return b;
1525     }
1526 
1527     /**
1528      * Get the colour of a single pixel in this graphics context
1529      *
1530      * @param x
1531      *            The x coordinate of the pixel to read
1532      * @param y
1533      *            The y coordinate of the pixel to read
1534      * @return The colour of the pixel at the specified location
1535      */
1536     @Nonnull
1537     public Color getPixel(int x, int y) {
1538         predraw();
1539         GL.glReadPixels(x, screenHeight - y, 1, 1, SGL.GL_RGBA, SGL.GL_UNSIGNED_BYTE, readBuffer);
1540         postdraw();
1541 
1542         return new Color(translate(readBuffer.get(0)), translate(readBuffer.get(1)), translate(readBuffer.get(2)), translate(readBuffer.get(3)));
1543     }
1544 
1545     /**
1546      * Get an ara of pixels as RGBA values into a buffer
1547      *
1548      * @param x
1549      *            The x position in the context to grab from
1550      * @param y
1551      *            The y position in the context to grab from
1552      * @param width
1553      *            The width of the area to grab from
1554      * @param height
1555      *            The hiehgt of the area to grab from
1556      * @param target
1557      *            The target buffer to grab into
1558      */
1559     public void getArea(int x, int y, int width, int height, @Nonnull ByteBuffer target) {
1560         if (target.capacity() < width * height * 4) {
1561             throw new IllegalArgumentException("Byte buffer provided to get area is not big enough");
1562         }
1563 
1564         predraw();
1565         GL.glReadPixels(x, screenHeight - y - height, width, height, SGL.GL_RGBA, SGL.GL_UNSIGNED_BYTE, target);
1566         postdraw();
1567     }
1568 
1569     /**
1570      * Draw a section of an image at a particular location and scale on the screen
1571      *
1572      * @param image
1573      *            The image to draw a section of
1574      * @param x
1575      *            The x position to draw the image
1576      * @param y
1577      *            The y position to draw the image
1578      * @param x2
1579      *            The x position of the bottom right corner of the drawn image
1580      * @param y2
1581      *            The y position of the bottom right corner of the drawn image
1582      * @param srcx
1583      *            The x position of the rectangle to draw from this image (i.e. relative to the image)
1584      * @param srcy
1585      *            The y position of the rectangle to draw from this image (i.e. relative to the image)
1586      * @param srcx2
1587      *            The x position of the bottom right cornder of rectangle to draw from this image (i.e. relative to the
1588      *            image)
1589      * @param srcy2
1590      *            The t position of the bottom right cornder of rectangle to draw from this image (i.e. relative to the
1591      *            image)
1592      * @param col
1593      *            The color to apply to the image as a filter
1594      */
1595     void drawImage(@Nonnull Image image, float x, float y, float x2, float y2, float srcx, float srcy, float srcx2, float srcy2, Color col) {
1596         predraw();
1597         image.draw(x, y, x2, y2, srcx, srcy, srcx2, srcy2, col);
1598         currentColor.bind();
1599         postdraw();
1600     }
1601 
1602     /**
1603      * Draw a section of an image at a particular location and scale on the screen
1604      *
1605      * @param image
1606      *            The image to draw a section of
1607      * @param x
1608      *            The x position to draw the image
1609      * @param y
1610      *            The y position to draw the image
1611      * @param srcx
1612      *            The x position of the rectangle to draw from this image (i.e. relative to the image)
1613      * @param srcy
1614      *            The y position of the rectangle to draw from this image (i.e. relative to the image)
1615      * @param srcx2
1616      *            The x position of the bottom right cornder of rectangle to draw from this image (i.e. relative to the
1617      *            image)
1618      * @param srcy2
1619      *            The t position of the bottom right cornder of rectangle to draw from this image (i.e. relative to the
1620      *            image)
1621      * @param col
1622      *            The color to apply to the image as a filter
1623      */
1624     public void drawImage(@Nonnull Image image, float x, float y, float srcx, float srcy, float srcx2, float srcy2, Color col) {
1625         drawImage(image, x, y, x + image.getWidth(), y + image.getHeight(), srcx, srcy, srcx2, srcy2, col);
1626     }
1627 
1628     /**
1629      * Draw a line with a gradient between the two points.
1630      *
1631      * @param x1
1632      *            The starting x position to draw the line
1633      * @param y1
1634      *            The starting y position to draw the line
1635      * @param red1
1636      *            The starting position's shade of red
1637      * @param green1
1638      *            The starting position's shade of green
1639      * @param blue1
1640      *            The starting position's shade of blue
1641      * @param alpha1
1642      *            The starting position's alpha value
1643      * @param x2
1644      *            The ending x position to draw the line
1645      * @param y2
1646      *            The ending y position to draw the line
1647      * @param red2
1648      *            The ending position's shade of red
1649      * @param green2
1650      *            The ending position's shade of green
1651      * @param blue2
1652      *            The ending position's shade of blue
1653      * @param alpha2
1654      *            The ending position's alpha value
1655      */
1656     public void drawGradientLine(float x1, float y1, float red1, float green1, float blue1, float alpha1, float x2, float y2, float red2,
1657             float green2, float blue2, float alpha2) {
1658         predraw();
1659 
1660         TextureImpl.bindNone();
1661 
1662         GL.glBegin(SGL.GL_LINES);
1663 
1664         GL.glColor4f(red1, green1, blue1, alpha1);
1665         GL.glVertex2f(x1, y1);
1666 
1667         GL.glColor4f(red2, green2, blue2, alpha2);
1668         GL.glVertex2f(x2, y2);
1669 
1670         GL.glEnd();
1671 
1672         postdraw();
1673     }
1674 
1675     /**
1676      * Draw a line with a gradient between the two points.
1677      *
1678      * @param x1
1679      *            The starting x position to draw the line
1680      * @param y1
1681      *            The starting y position to draw the line
1682      * @param Color1
1683      *            The starting position's color
1684      * @param x2
1685      *            The ending x position to draw the line
1686      * @param y2
1687      *            The ending y position to draw the line
1688      * @param Color2
1689      *            The ending position's color
1690      */
1691     public void drawGradientLine(float x1, float y1, @Nonnull Color Color1, float x2, float y2, @Nonnull Color Color2) {
1692         predraw();
1693 
1694         TextureImpl.bindNone();
1695 
1696         GL.glBegin(SGL.GL_LINES);
1697 
1698         Color1.bind();
1699         GL.glVertex2f(x1, y1);
1700 
1701         Color2.bind();
1702         GL.glVertex2f(x2, y2);
1703 
1704         GL.glEnd();
1705 
1706         postdraw();
1707     }
1708 
1709     /**
1710      * Push the current state of the transform from this graphics contexts onto the underlying graphics stack's
1711      * transform stack. An associated popTransform() must be performed to restore the state before the end of the
1712      * rendering loop.
1713      */
1714     void pushTransform() {
1715         predraw();
1716 
1717         FloatBuffer buffer;
1718         if (stackIndex >= stack.size()) {
1719             buffer = BufferUtils.createFloatBuffer(18);
1720             stack.add(buffer);
1721         } else {
1722             buffer = stack.get(stackIndex);
1723         }
1724 
1725         GL.glGetFloat(SGL.GL_MODELVIEW_MATRIX, buffer);
1726         buffer.put(16, sx);
1727         buffer.put(17, sy);
1728         stackIndex++;
1729 
1730         postdraw();
1731     }
1732 
1733     /**
1734      * Pop a previously pushed transform from the stack to the current. This should only be called if a transform has
1735      * been previously pushed.
1736      */
1737     void popTransform() {
1738         if (stackIndex == 0) {
1739             throw new RuntimeException("Attempt to pop a transform that hasn't be pushed");
1740         }
1741 
1742         predraw();
1743 
1744         stackIndex--;
1745         FloatBuffer oldBuffer = stack.get(stackIndex);
1746         GL.glLoadMatrix(oldBuffer);
1747         sx = oldBuffer.get(16);
1748         sy = oldBuffer.get(17);
1749 
1750         postdraw();
1751     }
1752 
1753     /**
1754      * Dispose this graphics context, this will release any underlying resourses. However this will also invalidate it's
1755      * use
1756      */
1757     public void destroy() {
1758 
1759     }
1760 
1761     protected boolean isYFlipped() {
1762         return false;
1763     }
1764 }