Browse Source

Canvases can now be used in SpriteBatches, ParticleSystems, and Meshes (resolves issue #782).
Canvases and Images are now subclasses of the new Texture class.
Added setTexture and getTexture methods for Meshes, ParticleSystems, and SpriteBatches (the old set/getImage methods are deprecated but not removed.)
Canvas texture coordinates are no longer flipped.

Alex Szpakowski 11 years ago
parent
commit
aa69a695d3
43 changed files with 1005 additions and 846 deletions
  1. 3 4
      CMakeLists.txt
  2. 10 14
      platform/macosx/love-framework.xcodeproj/project.pbxproj
  3. 1 0
      src/common/runtime.cpp
  4. 4 4
      src/common/types.h
  5. 0 65
      src/modules/graphics/DrawQable.h
  6. 1 1
      src/modules/graphics/Drawable.h
  7. 0 96
      src/modules/graphics/Image.cpp
  8. 130 0
      src/modules/graphics/Texture.cpp
  9. 57 12
      src/modules/graphics/Texture.h
  10. 123 150
      src/modules/graphics/opengl/Canvas.cpp
  11. 20 42
      src/modules/graphics/opengl/Canvas.h
  12. 7 6
      src/modules/graphics/opengl/Font.cpp
  13. 6 5
      src/modules/graphics/opengl/Font.h
  14. 26 15
      src/modules/graphics/opengl/Graphics.cpp
  15. 7 7
      src/modules/graphics/opengl/Graphics.h
  16. 22 40
      src/modules/graphics/opengl/Image.cpp
  17. 8 38
      src/modules/graphics/opengl/Image.h
  18. 18 16
      src/modules/graphics/opengl/Mesh.cpp
  19. 9 9
      src/modules/graphics/opengl/Mesh.h
  20. 58 48
      src/modules/graphics/opengl/OpenGL.cpp
  21. 16 10
      src/modules/graphics/opengl/OpenGL.h
  22. 26 23
      src/modules/graphics/opengl/ParticleSystem.cpp
  23. 10 10
      src/modules/graphics/opengl/ParticleSystem.h
  24. 2 0
      src/modules/graphics/opengl/Polyline.cpp
  25. 134 55
      src/modules/graphics/opengl/Shader.cpp
  26. 31 13
      src/modules/graphics/opengl/Shader.h
  27. 15 14
      src/modules/graphics/opengl/SpriteBatch.cpp
  28. 5 5
      src/modules/graphics/opengl/SpriteBatch.h
  29. 35 3
      src/modules/graphics/opengl/Texture.h
  30. 12 13
      src/modules/graphics/opengl/wrap_Canvas.cpp
  31. 6 6
      src/modules/graphics/opengl/wrap_Font.cpp
  32. 32 32
      src/modules/graphics/opengl/wrap_Graphics.cpp
  33. 17 18
      src/modules/graphics/opengl/wrap_Image.cpp
  34. 32 12
      src/modules/graphics/opengl/wrap_Mesh.cpp
  35. 2 2
      src/modules/graphics/opengl/wrap_Mesh.h
  36. 33 10
      src/modules/graphics/opengl/wrap_ParticleSystem.cpp
  37. 2 3
      src/modules/graphics/opengl/wrap_ParticleSystem.h
  38. 13 21
      src/modules/graphics/opengl/wrap_Shader.cpp
  39. 1 2
      src/modules/graphics/opengl/wrap_Shader.h
  40. 32 12
      src/modules/graphics/opengl/wrap_SpriteBatch.cpp
  41. 2 2
      src/modules/graphics/opengl/wrap_SpriteBatch.h
  42. 12 3
      src/scripts/graphics.lua
  43. 25 5
      src/scripts/graphics.lua.h

+ 3 - 4
CMakeLists.txt

@@ -249,15 +249,13 @@ source_group("modules\\font\\freetype" FILES ${LOVE_SRC_MODULE_FONT_FREETYPE})
 
 set(LOVE_SRC_MODULE_GRAPHICS_ROOT
 	src/modules/graphics/Color.h
-	src/modules/graphics/Drawable.cpp
 	src/modules/graphics/Drawable.h
-	src/modules/graphics/DrawQable.h
 	src/modules/graphics/Graphics.cpp
 	src/modules/graphics/Graphics.h
-	src/modules/graphics/Image.cpp
-	src/modules/graphics/Image.h
 	src/modules/graphics/Quad.cpp
 	src/modules/graphics/Quad.h
+	src/modules/graphics/Texture.cpp
+	src/modules/graphics/Texture.h
 	src/modules/graphics/Volatile.cpp
 	src/modules/graphics/Volatile.h
 )
@@ -285,6 +283,7 @@ set(LOVE_SRC_MODULE_GRAPHICS_OPENGL
 	src/modules/graphics/opengl/Shader.h
 	src/modules/graphics/opengl/SpriteBatch.cpp
 	src/modules/graphics/opengl/SpriteBatch.h
+	src/modules/graphics/opengl/Texture.h
 	src/modules/graphics/opengl/VertexBuffer.cpp
 	src/modules/graphics/opengl/VertexBuffer.h
 	src/modules/graphics/opengl/wrap_Canvas.cpp

+ 10 - 14
platform/macosx/love-framework.xcodeproj/project.pbxproj

@@ -109,9 +109,8 @@
 		FA08F61216C753E700F007B5 /* Rasterizer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1B1C4E4D288A1D2F29E57B1B /* Rasterizer.cpp */; };
 		FA08F61316C753E700F007B5 /* wrap_GlyphData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1B4E22F1388E2B2E76E3377B /* wrap_GlyphData.cpp */; };
 		FA08F61416C753E700F007B5 /* wrap_Rasterizer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 11745DE315E859F71E881D76 /* wrap_Rasterizer.cpp */; };
-		FA08F61516C753F600F007B5 /* Drawable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 58BA2BB460AF3C591B22690E /* Drawable.cpp */; };
 		FA08F61716C753F600F007B5 /* Graphics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 03F17FF546D637744E263961 /* Graphics.cpp */; };
-		FA08F61816C753F600F007B5 /* Image.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 58CC50E70A375FDF53EF01B6 /* Image.cpp */; };
+		FA08F61816C753F600F007B5 /* Texture.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 58CC50E70A375FDF53EF01B6 /* Texture.cpp */; };
 		FA08F61A16C753F600F007B5 /* Volatile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B731754147B27AF73AC5358 /* Volatile.cpp */; };
 		FA08F61B16C7541400F007B5 /* Canvas.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4AD52074367950B735707CE1 /* Canvas.cpp */; };
 		FA08F61C16C7541400F007B5 /* Font.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 583037E9238A6EF00DD20B1A /* Font.cpp */; };
@@ -267,6 +266,7 @@
 		FA7AA59317F6AC1F00704BE2 /* wrap_Mesh.h in Headers */ = {isa = PBXBuildFile; fileRef = FA7AA59117F6AC1F00704BE2 /* wrap_Mesh.h */; };
 		FA7C937A16DCC6C2006F2BEE /* wrap_Math.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA7C937516DCC6C2006F2BEE /* wrap_Math.cpp */; };
 		FA7C937B16DCC6C2006F2BEE /* wrap_Math.h in Headers */ = {isa = PBXBuildFile; fileRef = FA7C937616DCC6C2006F2BEE /* wrap_Math.h */; };
+		FA9B492A1875EFB900201DA9 /* Texture.h in Headers */ = {isa = PBXBuildFile; fileRef = FA9B49281875EFB900201DA9 /* Texture.h */; };
 		FA9B4A0816E1578300074F42 /* SDL2.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA9B4A0716E1578300074F42 /* SDL2.framework */; };
 		FA9FC0B0173D6E3E005027FF /* wrap_Window.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA9FC0AE173D6E3E005027FF /* wrap_Window.cpp */; };
 		FA9FC0B1173D6E3E005027FF /* wrap_Window.h in Headers */ = {isa = PBXBuildFile; fileRef = FA9FC0AF173D6E3E005027FF /* wrap_Window.h */; };
@@ -285,7 +285,6 @@
 		FAC5710317402D1100D147E4 /* wrap_BezierCurve.h in Headers */ = {isa = PBXBuildFile; fileRef = FAC570FF17402D1100D147E4 /* wrap_BezierCurve.h */; };
 		FAC86E631724552C00EED715 /* wrap_Quad.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FAC86E611724552C00EED715 /* wrap_Quad.cpp */; };
 		FAC86E641724552C00EED715 /* wrap_Quad.h in Headers */ = {isa = PBXBuildFile; fileRef = FAC86E621724552C00EED715 /* wrap_Quad.h */; };
-		FAC86E6A1724555D00EED715 /* DrawQable.h in Headers */ = {isa = PBXBuildFile; fileRef = FAC86E661724555D00EED715 /* DrawQable.h */; };
 		FAC86E6B1724555D00EED715 /* Quad.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FAC86E671724555D00EED715 /* Quad.cpp */; };
 		FAC86E6C1724555D00EED715 /* Quad.h in Headers */ = {isa = PBXBuildFile; fileRef = FAC86E681724555D00EED715 /* Quad.h */; };
 		FAE010DB170DDE99006F29D0 /* ddsinfo.h in Headers */ = {isa = PBXBuildFile; fileRef = FAE010D8170DDE99006F29D0 /* ddsinfo.h */; };
@@ -438,7 +437,7 @@
 		1CAA69E00D0808BA2108238B /* wrap_ChainShape.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_ChainShape.cpp; sourceTree = "<group>"; };
 		1CD02D1975803957282F28AB /* auxiliar.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = auxiliar.c; sourceTree = "<group>"; };
 		1CE84F1F19BC2AA412C638B1 /* timeout.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = timeout.h; sourceTree = "<group>"; };
-		1DA41DFF0869489411A71AFC /* Image.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Image.h; sourceTree = "<group>"; };
+		1DA41DFF0869489411A71AFC /* Texture.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Texture.h; sourceTree = "<group>"; };
 		1E22646A710E5EFC27FE3932 /* FrictionJoint.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FrictionJoint.cpp; sourceTree = "<group>"; };
 		1E27263847302FCA1F843B47 /* b2PrismaticJoint.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = b2PrismaticJoint.cpp; sourceTree = "<group>"; };
 		1E827AE8548C52493ED95629 /* wrap_Filesystem.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_Filesystem.cpp; sourceTree = "<group>"; };
@@ -629,8 +628,7 @@
 		583037E9238A6EF00DD20B1A /* Font.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Font.cpp; sourceTree = "<group>"; };
 		584E16AE09E12536206C46FE /* Mouse.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Mouse.cpp; sourceTree = "<group>"; };
 		58792BC1126C2917432D706B /* b2CircleContact.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = b2CircleContact.cpp; sourceTree = "<group>"; };
-		58BA2BB460AF3C591B22690E /* Drawable.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Drawable.cpp; sourceTree = "<group>"; };
-		58CC50E70A375FDF53EF01B6 /* Image.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Image.cpp; sourceTree = "<group>"; };
+		58CC50E70A375FDF53EF01B6 /* Texture.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Texture.cpp; sourceTree = "<group>"; };
 		58CD093F254501E37CA47CA8 /* types.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = types.h; sourceTree = "<group>"; };
 		597478A255B82B56488B4717 /* wrap_FileData.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_FileData.cpp; sourceTree = "<group>"; };
 		59BE634A2ACE722F14B86F89 /* b2Island.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = b2Island.cpp; sourceTree = "<group>"; };
@@ -824,6 +822,7 @@
 		FA7AA59117F6AC1F00704BE2 /* wrap_Mesh.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_Mesh.h; sourceTree = "<group>"; };
 		FA7C937516DCC6C2006F2BEE /* wrap_Math.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_Math.cpp; sourceTree = "<group>"; };
 		FA7C937616DCC6C2006F2BEE /* wrap_Math.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_Math.h; sourceTree = "<group>"; };
+		FA9B49281875EFB900201DA9 /* Texture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Texture.h; sourceTree = "<group>"; };
 		FA9B4A0716E1578300074F42 /* SDL2.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SDL2.framework; path = /Library/Frameworks/SDL2.framework; sourceTree = "<absolute>"; };
 		FA9FC0AE173D6E3E005027FF /* wrap_Window.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_Window.cpp; sourceTree = "<group>"; };
 		FA9FC0AF173D6E3E005027FF /* wrap_Window.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_Window.h; sourceTree = "<group>"; };
@@ -842,7 +841,6 @@
 		FAC570FF17402D1100D147E4 /* wrap_BezierCurve.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_BezierCurve.h; sourceTree = "<group>"; };
 		FAC86E611724552C00EED715 /* wrap_Quad.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_Quad.cpp; sourceTree = "<group>"; };
 		FAC86E621724552C00EED715 /* wrap_Quad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_Quad.h; sourceTree = "<group>"; };
-		FAC86E661724555D00EED715 /* DrawQable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DrawQable.h; sourceTree = "<group>"; };
 		FAC86E671724555D00EED715 /* Quad.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Quad.cpp; sourceTree = "<group>"; };
 		FAC86E681724555D00EED715 /* Quad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Quad.h; sourceTree = "<group>"; };
 		FAE010D8170DDE99006F29D0 /* ddsinfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ddsinfo.h; sourceTree = "<group>"; };
@@ -1476,16 +1474,14 @@
 			isa = PBXGroup;
 			children = (
 				4941079838020ECA049B5C21 /* Color.h */,
-				58BA2BB460AF3C591B22690E /* Drawable.cpp */,
 				5D93601669875EE06721689E /* Drawable.h */,
-				FAC86E661724555D00EED715 /* DrawQable.h */,
 				03F17FF546D637744E263961 /* Graphics.cpp */,
 				777352284E262F48543E6E7F /* Graphics.h */,
-				58CC50E70A375FDF53EF01B6 /* Image.cpp */,
-				1DA41DFF0869489411A71AFC /* Image.h */,
 				75093EE94918576801F50993 /* opengl */,
 				FAC86E671724555D00EED715 /* Quad.cpp */,
 				FAC86E681724555D00EED715 /* Quad.h */,
+				58CC50E70A375FDF53EF01B6 /* Texture.cpp */,
+				1DA41DFF0869489411A71AFC /* Texture.h */,
 				4B731754147B27AF73AC5358 /* Volatile.cpp */,
 				0CFF64090F0F4F481BB80CF0 /* Volatile.h */,
 			);
@@ -1683,6 +1679,7 @@
 				FA577A8616C71CF000860150 /* Shader.h */,
 				4D700D182EAA46273D1E2CC4 /* SpriteBatch.cpp */,
 				727D23FA1CC755B902471A45 /* SpriteBatch.h */,
+				FA9B49281875EFB900201DA9 /* Texture.h */,
 				426B1C4475DC54505B824B7F /* VertexBuffer.cpp */,
 				577B66502A5360AE60733B10 /* VertexBuffer.h */,
 				4E3251027026699A1D4D310D /* wrap_Canvas.cpp */,
@@ -1979,7 +1976,6 @@
 				FA636D8B171B70920065623F /* RandomGenerator.h in Headers */,
 				FA636D8F171B72A70065623F /* wrap_RandomGenerator.h in Headers */,
 				FAC86E641724552C00EED715 /* wrap_Quad.h in Headers */,
-				FAC86E6A1724555D00EED715 /* DrawQable.h in Headers */,
 				FAC86E6C1724555D00EED715 /* Quad.h in Headers */,
 				FA03546D1731F3A700284828 /* simplexnoise1234.h in Headers */,
 				FA3D9E0E16E68DE600CA6630 /* Cursor.h in Headers */,
@@ -1990,6 +1986,7 @@
 				FAF6705218184FF800DBDEEA /* wuff_internal.h in Headers */,
 				FAB007941740C28900A9664D /* Joystick.h in Headers */,
 				FAB007981740C87D00A9664D /* wrap_Joystick.h in Headers */,
+				FA9B492A1875EFB900201DA9 /* Texture.h in Headers */,
 				FAC5710117402D1100D147E4 /* BezierCurve.h in Headers */,
 				FAC5710317402D1100D147E4 /* wrap_BezierCurve.h in Headers */,
 				FA5FDC7E1788D548002F0ED2 /* callbacks.h in Headers */,
@@ -2179,9 +2176,8 @@
 				FA08F61216C753E700F007B5 /* Rasterizer.cpp in Sources */,
 				FA08F61316C753E700F007B5 /* wrap_GlyphData.cpp in Sources */,
 				FA08F61416C753E700F007B5 /* wrap_Rasterizer.cpp in Sources */,
-				FA08F61516C753F600F007B5 /* Drawable.cpp in Sources */,
 				FA08F61716C753F600F007B5 /* Graphics.cpp in Sources */,
-				FA08F61816C753F600F007B5 /* Image.cpp in Sources */,
+				FA08F61816C753F600F007B5 /* Texture.cpp in Sources */,
 				FA08F61A16C753F600F007B5 /* Volatile.cpp in Sources */,
 				FA08F61B16C7541400F007B5 /* Canvas.cpp in Sources */,
 				FA08F61C16C7541400F007B5 /* Font.cpp in Sources */,

+ 1 - 0
src/common/runtime.cpp

@@ -671,6 +671,7 @@ StringMap<Type, TYPE_MAX_ENUM>::Entry typeEntries[] =
 
 	// Graphics
 	{"Drawable", GRAPHICS_DRAWABLE_ID},
+	{"Texture", GRAPHICS_TEXTURE_ID},
 	{"Image", GRAPHICS_IMAGE_ID},
 	{"Quad", GRAPHICS_QUAD_ID},
 	{"Font", GRAPHICS_FONT_ID},

+ 4 - 4
src/common/types.h

@@ -45,7 +45,7 @@ enum Type
 
 	// Graphics
 	GRAPHICS_DRAWABLE_ID,
-	GRAPHICS_DRAWQABLE_ID,
+	GRAPHICS_TEXTURE_ID,
 	GRAPHICS_IMAGE_ID,
 	GRAPHICS_QUAD_ID,
 	GRAPHICS_FONT_ID,
@@ -130,13 +130,13 @@ const bits FONT_RASTERIZER_T = (bits(1) << FONT_RASTERIZER_ID) | OBJECT_T;
 
 // Graphics.
 const bits GRAPHICS_DRAWABLE_T = (bits(1) << GRAPHICS_DRAWABLE_ID) | OBJECT_T;
-const bits GRAPHICS_DRAWQABLE_T = (bits(1) << GRAPHICS_DRAWQABLE_ID) | GRAPHICS_DRAWABLE_T;
-const bits GRAPHICS_IMAGE_T = (bits(1) << GRAPHICS_IMAGE_ID) | GRAPHICS_DRAWQABLE_T;
+const bits GRAPHICS_TEXTURE_T = (bits(1) << GRAPHICS_TEXTURE_ID) | GRAPHICS_DRAWABLE_T;
+const bits GRAPHICS_IMAGE_T = (bits(1) << GRAPHICS_IMAGE_ID) | GRAPHICS_TEXTURE_T;
 const bits GRAPHICS_QUAD_T = (bits(1) << GRAPHICS_QUAD_ID) | OBJECT_T;
 const bits GRAPHICS_FONT_T = (bits(1) << GRAPHICS_FONT_ID) | OBJECT_T;
 const bits GRAPHICS_PARTICLE_SYSTEM_T = (bits(1) << GRAPHICS_PARTICLE_SYSTEM_ID) | GRAPHICS_DRAWABLE_T;
 const bits GRAPHICS_SPRITE_BATCH_T = (bits(1) << GRAPHICS_SPRITE_BATCH_ID) | GRAPHICS_DRAWABLE_T;
-const bits GRAPHICS_CANVAS_T = (bits(1) << GRAPHICS_CANVAS_ID) | GRAPHICS_DRAWQABLE_T;
+const bits GRAPHICS_CANVAS_T = (bits(1) << GRAPHICS_CANVAS_ID) | GRAPHICS_TEXTURE_T;
 const bits GRAPHICS_SHADER_T = (bits(1) << GRAPHICS_SHADER_ID) | OBJECT_T;
 const bits GRAPHICS_MESH_T = (bits(1) << GRAPHICS_MESH_ID) | GRAPHICS_DRAWABLE_T;
 

+ 0 - 65
src/modules/graphics/DrawQable.h

@@ -1,65 +0,0 @@
-/**
- * Copyright (c) 2006-2013 LOVE Development Team
- *
- * This software is provided 'as-is', without any express or implied
- * warranty.  In no event will the authors be held liable for any damages
- * arising from the use of this software.
- *
- * Permission is granted to anyone to use this software for any purpose,
- * including commercial applications, and to alter it and redistribute it
- * freely, subject to the following restrictions:
- *
- * 1. The origin of this software must not be misrepresented; you must not
- *    claim that you wrote the original software. If you use this software
- *    in a product, an acknowledgment in the product documentation would be
- *    appreciated but is not required.
- * 2. Altered source versions must be plainly marked as such, and must not be
- *    misrepresented as being the original software.
- * 3. This notice may not be removed or altered from any source distribution.
- **/
-
-#ifndef LOVE_GRAPHICS_DRAWQABLE_H
-#define LOVE_GRAPHICS_DRAWQABLE_H
-
-// LOVE
-#include "Drawable.h"
-#include "Quad.h"
-
-namespace love
-{
-namespace graphics
-{
-
-/**
- * A DrawQable is anything that be drawn in part with a Quad object.
- **/
-class DrawQable : public Drawable
-{
-public:
-
-	/**
-	 * Destructor.
-	 **/
-	virtual ~DrawQable() {}
-
-	/**
-	 * Draws the object with the specified transformation.
-	 *
-	 * @param quad The Quad object to use to draw the object.
-	 * @param x The position of the object along the x-axis.
-	 * @param y The position of the object along the y-axis.
-	 * @param angle The angle of the object (in radians).
-	 * @param sx The scale factor along the x-axis.
-	 * @param sy The scale factor along the y-axis.
-	 * @param ox The origin offset along the x-axis.
-	 * @param oy The origin offset along the y-axis.
-	 * @param kx Shear along the x-axis.
-	 * @param ky Shear along the y-axis.
-	 **/
-	virtual void drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const = 0;
-};
-
-} // graphics
-} // love
-
-#endif // LOVE_GRAPHICS_DRAWQABLE_H

+ 1 - 1
src/modules/graphics/Drawable.h

@@ -40,7 +40,7 @@ public:
 	/**
 	 * Destructor.
 	 **/
-	virtual ~Drawable();
+	virtual ~Drawable() {}
 
 	/**
 	 * Draws the object with the specified transformation.

+ 0 - 96
src/modules/graphics/Image.cpp

@@ -1,96 +0,0 @@
-/**
- * Copyright (c) 2006-2013 LOVE Development Team
- *
- * This software is provided 'as-is', without any express or implied
- * warranty.  In no event will the authors be held liable for any damages
- * arising from the use of this software.
- *
- * Permission is granted to anyone to use this software for any purpose,
- * including commercial applications, and to alter it and redistribute it
- * freely, subject to the following restrictions:
- *
- * 1. The origin of this software must not be misrepresented; you must not
- *    claim that you wrote the original software. If you use this software
- *    in a product, an acknowledgment in the product documentation would be
- *    appreciated but is not required.
- * 2. Altered source versions must be plainly marked as such, and must not be
- *    misrepresented as being the original software.
- * 3. This notice may not be removed or altered from any source distribution.
- **/
-
-#include "Image.h"
-
-namespace love
-{
-namespace graphics
-{
-
-Image::Filter Image::defaultFilter;
-
-Image::Filter::Filter()
-	: min(FILTER_LINEAR)
-	, mag(FILTER_LINEAR)
-	, mipmap(FILTER_NONE)
-	, anisotropy(1.0f)
-{
-}
-
-Image::Wrap::Wrap()
-	: s(WRAP_CLAMP)
-	, t(WRAP_CLAMP)
-{
-}
-
-Image::~Image()
-{
-}
-
-void Image::setDefaultFilter(const Filter &f)
-{
-	defaultFilter = f;
-}
-
-const Image::Filter &Image::getDefaultFilter()
-{
-	return defaultFilter;
-}
-
-bool Image::getConstant(const char *in, FilterMode &out)
-{
-	return filterModes.find(in, out);
-}
-
-bool Image::getConstant(FilterMode in, const char  *&out)
-{
-	return filterModes.find(in, out);
-}
-
-bool Image::getConstant(const char *in, WrapMode &out)
-{
-	return wrapModes.find(in, out);
-}
-
-bool Image::getConstant(WrapMode in, const char  *&out)
-{
-	return wrapModes.find(in, out);
-}
-
-StringMap<Image::FilterMode, Image::FILTER_MAX_ENUM>::Entry Image::filterModeEntries[] =
-{
-	{ "linear", Image::FILTER_LINEAR },
-	{ "nearest", Image::FILTER_NEAREST },
-};
-
-StringMap<Image::FilterMode, Image::FILTER_MAX_ENUM> Image::filterModes(Image::filterModeEntries, sizeof(Image::filterModeEntries));
-
-StringMap<Image::WrapMode, Image::WRAP_MAX_ENUM>::Entry Image::wrapModeEntries[] =
-{
-	{ "clamp", Image::WRAP_CLAMP },
-	{ "repeat", Image::WRAP_REPEAT },
-};
-
-StringMap<Image::WrapMode, Image::WRAP_MAX_ENUM> Image::wrapModes(Image::wrapModeEntries, sizeof(Image::wrapModeEntries));
-
-
-} // graphics
-} // love

+ 130 - 0
src/modules/graphics/Texture.cpp

@@ -0,0 +1,130 @@
+/**
+ * Copyright (c) 2006-2013 LOVE Development Team
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ *    claim that you wrote the original software. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ **/
+
+#include "Texture.h"
+
+namespace love
+{
+namespace graphics
+{
+
+Texture::Filter Texture::defaultFilter;
+
+Texture::Filter::Filter()
+	: min(FILTER_LINEAR)
+	, mag(FILTER_LINEAR)
+	, mipmap(FILTER_NONE)
+	, anisotropy(1.0f)
+{
+}
+
+Texture::Wrap::Wrap()
+	: s(WRAP_CLAMP)
+	, t(WRAP_CLAMP)
+{
+}
+
+Texture::Texture()
+	: width(0)
+	, height(0)
+	, filter(getDefaultFilter())
+	, wrap()
+	, vertices()
+{
+}
+
+Texture::~Texture()
+{
+}
+
+int Texture::getWidth() const
+{
+	return width;
+}
+
+int Texture::getHeight() const
+{
+	return height;
+}
+
+const Texture::Filter &Texture::getFilter() const
+{
+	return filter;
+}
+
+const Texture::Wrap &Texture::getWrap() const
+{
+	return wrap;
+}
+
+const Vertex *Texture::getVertices() const
+{
+	return vertices;
+}
+
+void Texture::setDefaultFilter(const Filter &f)
+{
+	defaultFilter = f;
+}
+
+const Texture::Filter &Texture::getDefaultFilter()
+{
+	return defaultFilter;
+}
+
+bool Texture::getConstant(const char *in, FilterMode &out)
+{
+	return filterModes.find(in, out);
+}
+
+bool Texture::getConstant(FilterMode in, const char  *&out)
+{
+	return filterModes.find(in, out);
+}
+
+bool Texture::getConstant(const char *in, WrapMode &out)
+{
+	return wrapModes.find(in, out);
+}
+
+bool Texture::getConstant(WrapMode in, const char  *&out)
+{
+	return wrapModes.find(in, out);
+}
+
+StringMap<Texture::FilterMode, Texture::FILTER_MAX_ENUM>::Entry Texture::filterModeEntries[] =
+{
+	{ "linear", Texture::FILTER_LINEAR },
+	{ "nearest", Texture::FILTER_NEAREST },
+};
+
+StringMap<Texture::FilterMode, Texture::FILTER_MAX_ENUM> Texture::filterModes(Texture::filterModeEntries, sizeof(Texture::filterModeEntries));
+
+StringMap<Texture::WrapMode, Texture::WRAP_MAX_ENUM>::Entry Texture::wrapModeEntries[] =
+{
+	{ "clamp", Texture::WRAP_CLAMP },
+	{ "repeat", Texture::WRAP_REPEAT },
+};
+
+StringMap<Texture::WrapMode, Texture::WRAP_MAX_ENUM> Texture::wrapModes(Texture::wrapModeEntries, sizeof(Texture::wrapModeEntries));
+
+
+} // graphics
+} // love

+ 57 - 12
src/modules/graphics/Image.h → src/modules/graphics/Texture.h

@@ -18,35 +18,40 @@
  * 3. This notice may not be removed or altered from any source distribution.
  **/
 
-#ifndef LOVE_GRAPHICS_IMAGE_H
-#define LOVE_GRAPHICS_IMAGE_H
+#ifndef LOVE_GRAPHICS_TEXTURE_H
+#define LOVE_GRAPHICS_TEXTURE_H
 
 // LOVE
-#include "graphics/Volatile.h"
-#include "graphics/DrawQable.h"
 #include "common/StringMap.h"
+#include "common/math.h"
+#include "Drawable.h"
+#include "Quad.h"
 
 namespace love
 {
 namespace graphics
 {
 
-class Image : public DrawQable, public Volatile
+/**
+ * Base class for 2D textures. All textures can be drawn with Quads, have a
+ * width and height, and have filter and wrap modes.
+ **/
+class Texture : public Drawable
 {
 public:
 
 	enum WrapMode
 	{
-		WRAP_CLAMP = 1,
+		WRAP_CLAMP,
 		WRAP_REPEAT,
 		WRAP_MAX_ENUM
 	};
 
 	enum FilterMode
 	{
-		FILTER_LINEAR = 1,
-		FILTER_NEAREST,
 		FILTER_NONE,
+		FILTER_LINEAR,
+		FILTER_NEAREST,
 		FILTER_MAX_ENUM
 	};
 
@@ -66,17 +71,56 @@ public:
 		WrapMode t;
 	};
 
-	virtual ~Image();
-	
+	Texture();
+	virtual ~Texture();
+
+	/**
+	 * Draws the texture using the specified transformation with a Quad applied.
+	 *
+	 * @param quad The Quad object to use to draw the object.
+	 * @param x The position of the object along the x-axis.
+	 * @param y The position of the object along the y-axis.
+	 * @param angle The angle of the object (in radians).
+	 * @param sx The scale factor along the x-axis.
+	 * @param sy The scale factor along the y-axis.
+	 * @param ox The origin offset along the x-axis.
+	 * @param oy The origin offset along the y-axis.
+	 * @param kx Shear along the x-axis.
+	 * @param ky Shear along the y-axis.
+	 **/
+	virtual void drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const = 0;
+
+	virtual int getWidth() const;
+	virtual int getHeight() const;
+
+	virtual void setFilter(const Filter &f) = 0;
+	virtual const Filter &getFilter() const;
+
+	virtual void setWrap(const Wrap &w) = 0;
+	virtual const Wrap &getWrap() const;
+
+	virtual const Vertex *getVertices() const;
+
 	// The default filter.
 	static void setDefaultFilter(const Filter &f);
 	static const Filter &getDefaultFilter();
 
 	static bool getConstant(const char *in, FilterMode &out);
 	static bool getConstant(FilterMode in, const char  *&out);
+
 	static bool getConstant(const char *in, WrapMode &out);
 	static bool getConstant(WrapMode in, const char  *&out);
 
+protected:
+
+	int width;
+	int height;
+
+	Filter filter;
+	Wrap wrap;
+
+	Vertex vertices[4];
+
 private:
 
 	// The default texture filter.
@@ -84,12 +128,13 @@ private:
 
 	static StringMap<FilterMode, FILTER_MAX_ENUM>::Entry filterModeEntries[];
 	static StringMap<FilterMode, FILTER_MAX_ENUM> filterModes;
+
 	static StringMap<WrapMode, WRAP_MAX_ENUM>::Entry wrapModeEntries[];
 	static StringMap<WrapMode, WRAP_MAX_ENUM> wrapModes;
 
-}; // Image
+}; // Texture
 
 } // graphics
 } // love
 
-#endif // LOVE_GRAPHICS_IMAGE_H
+#endif // LOVE_GRAPHICS_TEXTURE_H

+ 123 - 150
src/modules/graphics/opengl/Canvas.cpp

@@ -115,7 +115,8 @@ struct FramebufferStrategyGL3 : public FramebufferStrategy
 		glGenTextures(1, &img);
 		gl.bindTexture(img);
 
-		gl.setTextureFilter(Image::getDefaultFilter());
+		Texture::Filter filter = Texture::getDefaultFilter();
+		gl.setTextureFilter(filter);
 
 		glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height,
 			0, GL_RGBA, format, NULL);
@@ -183,7 +184,7 @@ struct FramebufferStrategyGL3 : public FramebufferStrategy
 		for (size_t i = 0; i < canvases.size(); i++)
 		{
 			glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1 + i,
-				GL_TEXTURE_2D, canvases[i]->getTextureName(), 0);
+				GL_TEXTURE_2D, canvases[i]->getGLTexture(), 0);
 			drawbuffers.push_back(GL_COLOR_ATTACHMENT1 + i);
 		}
 
@@ -224,7 +225,8 @@ struct FramebufferStrategyPackedEXT : public FramebufferStrategy
 		glGenTextures(1, &img);
 		gl.bindTexture(img);
 
-		gl.setTextureFilter(Image::getDefaultFilter());
+		Texture::Filter filter = Texture::getDefaultFilter();
+		gl.setTextureFilter(filter);
 
 		glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height,
 			0, GL_RGBA, format, NULL);
@@ -292,7 +294,7 @@ struct FramebufferStrategyPackedEXT : public FramebufferStrategy
 		for (size_t i = 0; i < canvases.size(); i++)
 		{
 			glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1_EXT + i,
-								   GL_TEXTURE_2D, canvases[i]->getTextureName(), 0);
+								   GL_TEXTURE_2D, canvases[i]->getGLTexture(), 0);
 			drawbuffers.push_back(GL_COLOR_ATTACHMENT1_EXT + i);
 		}
 
@@ -364,37 +366,36 @@ static int maxFBOColorAttachments = 0;
 static int maxDrawBuffers = 0;
 
 Canvas::Canvas(int width, int height, TextureType texture_type)
-	: width(width)
-	, height(height)
-	, fbo(0)
+	: fbo(0)
 	, depth_stencil(0)
 	, img(0)
 	, texture_type(texture_type)
 {
+	this->width = width;
+	this->height = height;
+
 	float w = static_cast<float>(width);
 	float h = static_cast<float>(height);
 
 	// world coordinates
 	vertices[0].x = 0;
-	vertices[0].y = h;
-	vertices[1].x = w;
+	vertices[0].y = 0;
+	vertices[1].x = 0;
 	vertices[1].y = h;
 	vertices[2].x = w;
-	vertices[2].y = 0;
-	vertices[3].x = 0;
+	vertices[2].y = h;
+	vertices[3].x = w;
 	vertices[3].y = 0;
 
 	// texture coordinates
 	vertices[0].s = 0;
 	vertices[0].t = 0;
-	vertices[1].s = 1;
-	vertices[1].t = 0;
+	vertices[1].s = 0;
+	vertices[1].t = 1;
 	vertices[2].s = 1;
 	vertices[2].t = 1;
-	vertices[3].s = 0;
-	vertices[3].t = 1;
-
-	settings.filter = Image::getDefaultFilter();
+	vertices[3].s = 1;
+	vertices[3].t = 0;
 
 	getStrategy();
 
@@ -410,36 +411,100 @@ Canvas::~Canvas()
 	unloadVolatile();
 }
 
-bool Canvas::isSupported()
+bool Canvas::loadVolatile()
 {
-	getStrategy();
-	return (strategy != &strategyNone);
+	fbo = depth_stencil = img = 0;
+
+	// glTexImage2D is guaranteed to error in this case.
+	if (width > gl.getMaxTextureSize() || height > gl.getMaxTextureSize())
+	{
+		status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
+		return false;
+	}
+
+	status = strategy->createFBO(fbo, img, width, height, texture_type);
+	if (status != GL_FRAMEBUFFER_COMPLETE)
+		return false;
+
+	setFilter(filter);
+	setWrap(wrap);
+	clear(Color(0, 0, 0, 0));
+	return true;
 }
 
-bool Canvas::isHDRSupported()
+void Canvas::unloadVolatile()
 {
-	return GLEE_VERSION_3_0 || (isSupported() && GLEE_ARB_texture_float);
+	strategy->deleteFBO(fbo, depth_stencil, img);
+	fbo = depth_stencil = img = 0;
+
+	for (size_t i = 0; i < attachedCanvases.size(); i++)
+		attachedCanvases[i]->release();
+
+	attachedCanvases.clear();
 }
 
-bool Canvas::isMultiCanvasSupported()
+void Canvas::drawv(const Matrix &t, const Vertex *v) const
 {
-	if (!(isSupported() && (GLEE_VERSION_2_0 || GLEE_ARB_draw_buffers)))
-		return false;
+	glPushMatrix();
 
-	if (maxFBOColorAttachments == 0 || maxDrawBuffers == 0)
-	{
-		glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &maxFBOColorAttachments);
-		glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers);
-	}
+	glMultMatrixf((const GLfloat *)t.getElements());
 
-	// system must support at least 4 simultanious active canvases
-	return maxFBOColorAttachments >= 4 && maxDrawBuffers >= 4;
+	gl.bindTexture(img);
+
+	glEnableClientState(GL_VERTEX_ARRAY);
+	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+
+	glVertexPointer(2, GL_FLOAT, sizeof(Vertex), (GLvoid *)&v[0].x);
+	glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), (GLvoid *)&v[0].s);
+
+	gl.prepareDraw();
+	glDrawArrays(GL_QUADS, 0, 4);
+
+	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+	glDisableClientState(GL_VERTEX_ARRAY);
+	
+	glPopMatrix();
 }
 
-void Canvas::bindDefaultCanvas()
+void Canvas::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const
 {
-	if (current != NULL)
-		current->stopGrab();
+	static Matrix t;
+	t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
+
+	drawv(t, vertices);
+}
+
+void Canvas::drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const
+{
+	static Matrix t;
+	t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
+
+	const Vertex *v = quad->getVertices();
+	drawv(t, v);
+}
+
+void Canvas::setFilter(const Texture::Filter &f)
+{
+	filter = f;
+	gl.bindTexture(img);
+	gl.setTextureFilter(filter);
+}
+
+void Canvas::setWrap(const Texture::Wrap &w)
+{
+	wrap = w;
+	gl.bindTexture(img);
+	gl.setTextureWrap(wrap);
+}
+
+GLuint Canvas::getGLTexture() const
+{
+	return img;
+}
+
+void Canvas::predraw() const
+{
+	gl.bindTexture(img);
 }
 
 void Canvas::setupGrab()
@@ -463,7 +528,7 @@ void Canvas::setupGrab()
 	glLoadIdentity();
 
 	// Set up orthographic view (no depth)
-	glOrtho(0.0, width, height, 0.0, -1.0, 1.0);
+	glOrtho(0.0, width, 0.0, height, -1.0, 1.0);
 
 	// Switch back to modelview matrix
 	glMatrixMode(GL_MODELVIEW);
@@ -545,8 +610,10 @@ void Canvas::stopGrab()
 	glMatrixMode(GL_PROJECTION);
 	glPopMatrix();
 	glMatrixMode(GL_MODELVIEW);
+
+	current = nullptr;
+
 	gl.setViewport(systemViewport);
-	current = NULL;
 }
 
 void Canvas::clear(Color c)
@@ -558,7 +625,7 @@ void Canvas::clear(Color c)
 
 	if (current != this)
 	{
-		if (current != NULL)
+		if (current != nullptr)
 			previous = current->fbo;
 
 		strategy->bindFBO(fbo);
@@ -601,30 +668,6 @@ void Canvas::clear(Color c)
 		strategy->bindFBO(previous);
 }
 
-void Canvas::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const
-{
-	static Matrix t;
-	t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
-
-	drawv(t, vertices);
-}
-
-void Canvas::drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const
-{
-	static Matrix t;
-	t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
-
-	const Vertex *v = quad->getVertices();
-
-	// flip texture coordinates vertically.
-	Vertex w[4];
-	memcpy(w, v, sizeof(Vertex) * 4);
-	for (size_t i = 0; i < 4; i++)
-		w[i].t = 1.0f - w[i].t;
-
-	drawv(t, w);
-}
-
 bool Canvas::checkCreateStencil()
 {
 	// Do nothing if we've already created the stencil buffer.
@@ -649,7 +692,6 @@ love::image::ImageData *Canvas::getImageData(love::image::Image *image)
 	int row = 4 * width;
 	int size = row * height;
 	GLubyte *pixels  = new GLubyte[size];
-	GLubyte *flipped = new GLubyte[size];
 
 	strategy->bindFBO(fbo);
 	glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
@@ -658,15 +700,8 @@ love::image::ImageData *Canvas::getImageData(love::image::Image *image)
 	else
 		strategy->bindFBO(0);
 
-	GLubyte *src = pixels, *dst = flipped + size - row;
-	for (int i = 0; i < height; ++i, dst -= row, src += row)
-		memcpy(dst, src, row);
-
-	love::image::ImageData *img = image->newImageData(width, height, (void *)flipped, true);
-
-	// The new ImageData now owns the flipped data, so we don't delete it here.
-	delete[] pixels;
-
+	// The new ImageData now owns the pixel data, so we don't delete it here.
+	love::image::ImageData *img = image->newImageData(width, height, (void *)pixels, true);
 	return img;
 }
 
@@ -683,98 +718,36 @@ void Canvas::getPixel(unsigned char* pixel_rgba, int x, int y)
 		strategy->bindFBO(0);
 }
 
-const std::vector<Canvas *> &Canvas::getAttachedCanvases() const
-{
-	return attachedCanvases;
-}
-
-void Canvas::setFilter(const Image::Filter &f)
-{
-	settings.filter = f;
-	gl.bindTexture(img);
-	settings.filter.anisotropy = gl.setTextureFilter(f);
-}
-
-Image::Filter Canvas::getFilter() const
-{
-	gl.bindTexture(img);
-	return gl.getTextureFilter();
-}
-
-void Canvas::setWrap(const Image::Wrap &w)
+bool Canvas::isSupported()
 {
-	settings.wrap = w;
-	gl.bindTexture(img);
-	gl.setTextureWrap(w);
+	getStrategy();
+	return (strategy != &strategyNone);
 }
 
-Image::Wrap Canvas::getWrap() const
+bool Canvas::isHDRSupported()
 {
-	return settings.wrap;
+	return GLEE_VERSION_3_0 || (isSupported() && GLEE_ARB_texture_float);
 }
 
-bool Canvas::loadVolatile()
+bool Canvas::isMultiCanvasSupported()
 {
-	fbo = depth_stencil = img = 0;
+	if (!(isSupported() && (GLEE_VERSION_2_0 || GLEE_ARB_draw_buffers)))
+		return false;
 
-	// glTexImage2D is guaranteed to error in this case.
-	if (width > gl.getMaxTextureSize() || height > gl.getMaxTextureSize())
+	if (maxFBOColorAttachments == 0 || maxDrawBuffers == 0)
 	{
-		status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
-		return false;
+		glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &maxFBOColorAttachments);
+		glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers);
 	}
 
-	status = strategy->createFBO(fbo, img, width, height, texture_type);
-	if (status != GL_FRAMEBUFFER_COMPLETE)
-		return false;
-
-	setFilter(settings.filter);
-	setWrap(settings.wrap);
-	clear(Color(0, 0, 0, 0));
-	return true;
-}
-
-void Canvas::unloadVolatile()
-{
-	strategy->deleteFBO(fbo, depth_stencil, img);
-	fbo = depth_stencil = img = 0;
-
-	for (size_t i = 0; i < attachedCanvases.size(); i++)
-		attachedCanvases[i]->release();
-
-	attachedCanvases.clear();
-}
-
-int Canvas::getWidth()
-{
-	return width;
-}
-
-int Canvas::getHeight()
-{
-	return height;
+	// system must support at least 4 simultanious active canvases
+	return maxFBOColorAttachments >= 4 && maxDrawBuffers >= 4;
 }
 
-void Canvas::drawv(const Matrix &t, const Vertex *v) const
+void Canvas::bindDefaultCanvas()
 {
-	glPushMatrix();
-
-	glMultMatrixf((const GLfloat *)t.getElements());
-
-	gl.bindTexture(img);
-
-	glEnableClientState(GL_VERTEX_ARRAY);
-	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
-
-	glVertexPointer(2, GL_FLOAT, sizeof(Vertex), (GLvoid *)&v[0].x);
-	glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), (GLvoid *)&v[0].s);
-
-	glDrawArrays(GL_QUADS, 0, 4);
-
-	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
-	glDisableClientState(GL_VERTEX_ARRAY);
-
-	glPopMatrix();
+	if (current != nullptr)
+		current->stopGrab();
 }
 
 bool Canvas::getConstant(const char *in, Canvas::TextureType &out)

+ 20 - 42
src/modules/graphics/opengl/Canvas.h

@@ -21,14 +21,11 @@
 #ifndef LOVE_GRAPHICS_OPENGL_CANVAS_H
 #define LOVE_GRAPHICS_OPENGL_CANVAS_H
 
-#include "graphics/DrawQable.h"
-#include "graphics/Volatile.h"
-#include "graphics/Image.h"
 #include "graphics/Color.h"
 #include "image/Image.h"
 #include "image/ImageData.h"
-#include "common/math.h"
 #include "common/Matrix.h"
+#include "Texture.h"
 #include "OpenGL.h"
 
 namespace love
@@ -38,7 +35,7 @@ namespace graphics
 namespace opengl
 {
 
-class Canvas : public DrawQable, public Volatile
+class Canvas : public Texture
 {
 public:
 
@@ -52,6 +49,20 @@ public:
 	Canvas(int width, int height, TextureType texture_type = TYPE_NORMAL);
 	virtual ~Canvas();
 
+	// Implements Volatile.
+	virtual bool loadVolatile();
+	virtual void unloadVolatile();
+
+	// Implements Drawable.
+	virtual void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const;
+
+	// Implements Texture.
+	virtual void drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const;
+	virtual void setFilter(const Texture::Filter &f);
+	virtual void setWrap(const Texture::Wrap &w);
+	virtual GLuint getGLTexture() const;
+	virtual void predraw() const;
+
 	/**
 	 * @param canvases A list of other canvases to temporarily attach to this one,
 	 * to allow drawing to multiple canvases at once.
@@ -62,13 +73,6 @@ public:
 
 	void clear(Color c);
 
-	virtual void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const;
-
-	/**
-	 * @copydoc DrawQable::drawq()
-	 **/
-	void drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const;
-
 	/**
 	 * Create and attach a stencil buffer to this Canvas' framebuffer, if necessary.
 	 **/
@@ -78,16 +82,10 @@ public:
 
 	void getPixel(unsigned char* pixel_rgba, int x, int y);
 
-	const std::vector<Canvas *> &getAttachedCanvases() const;
-
-	void setFilter(const Image::Filter &f);
-	Image::Filter getFilter() const;
-
-	void setWrap(const Image::Wrap &w);
-	Image::Wrap getWrap() const;
-
-	int getWidth();
-	int getHeight();
+	inline const std::vector<Canvas *> &getAttachedCanvases() const
+	{
+		return attachedCanvases;
+	}
 
 	inline GLenum getStatus() const
 	{
@@ -99,9 +97,6 @@ public:
 		return texture_type;
 	}
 
-	bool loadVolatile();
-	void unloadVolatile();
-
 	static bool isSupported();
 	static bool isHDRSupported();
 	static bool isMultiCanvasSupported();
@@ -115,33 +110,16 @@ public:
 	// The viewport dimensions of the system (default) framebuffer.
 	static OpenGL::Viewport systemViewport;
 
-	GLuint getTextureName() const
-	{
-		return img;
-	}
-
 private:
 
-	friend class Shader;
-
-	GLsizei width;
-	GLsizei height;
 	GLuint fbo;
 	GLuint depth_stencil;
 	GLuint img;
 
 	TextureType texture_type;
 
-	Vertex vertices[4];
-
 	GLenum status;
 
-	struct
-	{
-		Image::Filter filter;
-		Image::Wrap   wrap;
-	} settings;
-
 	std::vector<Canvas *> attachedCanvases;
 
 	void setupGrab();

+ 7 - 6
src/modules/graphics/opengl/Font.cpp

@@ -20,7 +20,6 @@
 #include "common/config.h"
 #include "Font.h"
 #include "font/GlyphData.h"
-#include "Image.h"
 
 #include "libraries/utf8/utf8.h"
 
@@ -41,14 +40,14 @@ namespace opengl
 const int Font::TEXTURE_WIDTHS[]  = {128, 256, 256, 512, 512, 1024, 1024};
 const int Font::TEXTURE_HEIGHTS[] = {128, 128, 256, 256, 512, 512,  1024};
 
-Font::Font(love::font::Rasterizer *r, const Image::Filter &filter)
+Font::Font(love::font::Rasterizer *r, const Texture::Filter &filter)
 	: rasterizer(r)
 	, height(r->getHeight())
 	, lineHeight(1)
 	, mSpacing(1)
 	, filter(filter)
 {
-	this->filter.mipmap = Image::FILTER_NONE;
+	this->filter.mipmap = Texture::FILTER_NONE;
 
 	// Try to find the best texture size match for the font size. default to the
 	// largest texture size if no rough match is found.
@@ -350,6 +349,8 @@ void Font::print(const std::string &text, float x, float y, float extra_spacing,
 	glVertexPointer(2, GL_FLOAT, sizeof(GlyphVertex), (GLvoid *)&glyphverts[0].x);
 	glTexCoordPointer(2, GL_FLOAT, sizeof(GlyphVertex), (GLvoid *)&glyphverts[0].s);
 
+	gl.prepareDraw();
+
 	// We need to draw a new vertex array for every section of the string which
 	// uses a different texture than the previous section.
 	std::vector<GlyphArrayDrawInfo>::const_iterator it;
@@ -494,18 +495,18 @@ float Font::getSpacing() const
 	return mSpacing;
 }
 
-void Font::setFilter(const Image::Filter &f)
+void Font::setFilter(const Texture::Filter &f)
 {
 	filter = f;
 
 	for (auto it = textures.begin(); it != textures.end(); ++it)
 	{
 		gl.bindTexture(*it);
-		filter.anisotropy = gl.setTextureFilter(f);
+		gl.setTextureFilter(filter);
 	}
 }
 
-const Image::Filter &Font::getFilter()
+const Texture::Filter &Font::getFilter()
 {
 	return filter;
 }

+ 6 - 5
src/modules/graphics/opengl/Font.h

@@ -29,7 +29,8 @@
 // LOVE
 #include "common/Object.h"
 #include "font/Rasterizer.h"
-#include "graphics/Image.h"
+#include "graphics/Texture.h"
+#include "graphics/Volatile.h"
 
 #include "OpenGL.h"
 
@@ -44,7 +45,7 @@ class Font : public Object, public Volatile
 {
 public:
 
-	Font(love::font::Rasterizer *r, const Image::Filter &filter = Image::getDefaultFilter());
+	Font(love::font::Rasterizer *r, const Texture::Filter &filter = Texture::getDefaultFilter());
 
 	virtual ~Font();
 
@@ -122,8 +123,8 @@ public:
 	 **/
 	float getSpacing() const;
 
-	void setFilter(const Image::Filter &f);
-	const Image::Filter &getFilter();
+	void setFilter(const Texture::Filter &f);
+	const Texture::Filter &getFilter();
 
 	// Implements Volatile.
 	bool loadVolatile();
@@ -199,7 +200,7 @@ private:
 	std::map<uint32, Glyph *> glyphs;
 
 	FontType type;
-	Image::Filter filter;
+	Texture::Filter filter;
 
 	static const int NUM_TEXTURE_SIZES = 7;
 	static const int TEXTURE_WIDTHS[NUM_TEXTURE_SIZES];

+ 26 - 15
src/modules/graphics/opengl/Graphics.cpp

@@ -327,7 +327,11 @@ Image *Graphics::newImage(love::image::ImageData *data)
 {
 	// Create the image.
 	Image *image = new Image(data);
-	bool success;
+
+	if (!isCreated())
+		return image;
+
+	bool success = false;
 	try
 	{
 		success = image->load();
@@ -350,7 +354,11 @@ Image *Graphics::newImage(love::image::CompressedData *cdata)
 {
 	// Create the image.
 	Image *image = new Image(cdata);
-	bool success;
+
+	if (!isCreated())
+		return image;
+
+	bool success = false;
 	try
 	{
 		success = image->load();
@@ -374,19 +382,19 @@ Quad *Graphics::newQuad(Quad::Viewport v, float sw, float sh)
 	return new Quad(v, sw, sh);
 }
 
-Font *Graphics::newFont(love::font::Rasterizer *r, const Image::Filter &filter)
+Font *Graphics::newFont(love::font::Rasterizer *r, const Texture::Filter &filter)
 {
 	return new Font(r, filter);
 }
 
-SpriteBatch *Graphics::newSpriteBatch(Image *image, int size, int usage)
+SpriteBatch *Graphics::newSpriteBatch(Texture *texture, int size, int usage)
 {
-	return new SpriteBatch(image, size, usage);
+	return new SpriteBatch(texture, size, usage);
 }
 
-ParticleSystem *Graphics::newParticleSystem(Image *image, int size)
+ParticleSystem *Graphics::newParticleSystem(Texture *texture, int size)
 {
-	return new ParticleSystem(image, size);
+	return new ParticleSystem(texture, size);
 }
 
 Canvas *Graphics::newCanvas(int width, int height, Canvas::TextureType texture_type)
@@ -629,23 +637,23 @@ Graphics::BlendMode Graphics::getBlendMode() const
 	throw Exception("Unknown blend mode");
 }
 
-void Graphics::setDefaultFilter(const Image::Filter &f)
+void Graphics::setDefaultFilter(const Texture::Filter &f)
 {
-	Image::setDefaultFilter(f);
+	Texture::setDefaultFilter(f);
 }
 
-const Image::Filter &Graphics::getDefaultFilter() const
+const Texture::Filter &Graphics::getDefaultFilter() const
 {
-	return Image::getDefaultFilter();
+	return Texture::getDefaultFilter();
 }
 
-void Graphics::setDefaultMipmapFilter(Image::FilterMode filter, float sharpness)
+void Graphics::setDefaultMipmapFilter(Texture::FilterMode filter, float sharpness)
 {
 	Image::setDefaultMipmapFilter(filter);
 	Image::setDefaultMipmapSharpness(sharpness);
 }
 
-void Graphics::getDefaultMipmapFilter(Image::FilterMode *filter, float *sharpness) const
+void Graphics::getDefaultMipmapFilter(Texture::FilterMode *filter, float *sharpness) const
 {
 	*filter = Image::getDefaultMipmapFilter();
 	*sharpness = Image::getDefaultMipmapSharpness();
@@ -718,13 +726,13 @@ int Graphics::getMaxPointSize() const
 
 void Graphics::print(const std::string &str, float x, float y , float angle, float sx, float sy, float ox, float oy, float kx, float ky)
 {
-	if (currentFont != 0)
+	if (currentFont != nullptr)
 		currentFont->print(str, x, y, 0.0, angle, sx, sy, ox, oy, kx, ky);
 }
 
 void Graphics::printf(const std::string &str, float x, float y, float wrap, AlignMode align, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
 {
-	if (currentFont == 0)
+	if (currentFont == nullptr)
 		return;
 
 	if (wrap < 0.0f)
@@ -797,6 +805,7 @@ void Graphics::printf(const std::string &str, float x, float y, float wrap, Alig
 
 void Graphics::point(float x, float y)
 {
+	gl.prepareDraw();
 	gl.bindTexture(0);
 	glBegin(GL_POINTS);
 	glVertex2f(x, y);
@@ -890,6 +899,7 @@ void Graphics::arc(DrawMode mode, float x, float y, float radius, float angle1,
 	}
 	else
 	{
+		gl.prepareDraw();
 		gl.bindTexture(0);
 		glEnableClientState(GL_VERTEX_ARRAY);
 		glVertexPointer(2, GL_FLOAT, 0, (const GLvoid *) coords);
@@ -913,6 +923,7 @@ void Graphics::polygon(DrawMode mode, const float *coords, size_t count)
 	}
 	else
 	{
+		gl.prepareDraw();
 		gl.bindTexture(0);
 		glEnableClientState(GL_VERTEX_ARRAY);
 		glVertexPointer(2, GL_FLOAT, 0, (const GLvoid *)coords);

+ 7 - 7
src/modules/graphics/opengl/Graphics.h

@@ -203,11 +203,11 @@ public:
 	/**
 	 * Creates a Font object.
 	 **/
-	Font *newFont(love::font::Rasterizer *data, const Image::Filter &filter = Image::Filter());
+	Font *newFont(love::font::Rasterizer *data, const Texture::Filter &filter = Texture::getDefaultFilter());
 
-	SpriteBatch *newSpriteBatch(Image *image, int size, int usage);
+	SpriteBatch *newSpriteBatch(Texture *texture, int size, int usage);
 
-	ParticleSystem *newParticleSystem(Image *image, int size);
+	ParticleSystem *newParticleSystem(Texture *texture, int size);
 
 	Canvas *newCanvas(int width, int height, Canvas::TextureType texture_type = Canvas::TYPE_NORMAL);
 
@@ -270,18 +270,18 @@ public:
 	/**
 	 * Sets the default filter for images, canvases, and fonts.
 	 **/
-	void setDefaultFilter(const Image::Filter &f);
+	void setDefaultFilter(const Texture::Filter &f);
 
 	/**
 	 * Gets the default filter for images, canvases, and fonts.
 	 **/
-	const Image::Filter &getDefaultFilter() const;
+	const Texture::Filter &getDefaultFilter() const;
 
 	/**
 	 * Default Image mipmap filter mode and sharpness values.
 	 **/
-	void setDefaultMipmapFilter(Image::FilterMode filter, float sharpness);
-	void getDefaultMipmapFilter(Image::FilterMode *filter, float *sharpness) const;
+	void setDefaultMipmapFilter(Texture::FilterMode filter, float sharpness);
+	void getDefaultMipmapFilter(Texture::FilterMode *filter, float *sharpness) const;
 
 	/**
 	 * Sets the line width.

+ 22 - 40
src/modules/graphics/opengl/Image.cpp

@@ -33,14 +33,12 @@ namespace opengl
 
 float Image::maxMipmapSharpness = 0.0f;
 
-Image::FilterMode Image::defaultMipmapFilter = Image::FILTER_NONE;
+Texture::FilterMode Image::defaultMipmapFilter = Texture::FILTER_NONE;
 float Image::defaultMipmapSharpness = 0.0f;
 
 Image::Image(love::image::ImageData *data)
 	: data(data)
-	, cdata(0)
-	, width(data->getWidth())
-	, height(data->getHeight())
+	, cdata(nullptr)
 	, paddedWidth(width)
 	, paddedHeight(height)
 	, texture(0)
@@ -49,15 +47,16 @@ Image::Image(love::image::ImageData *data)
 	, compressed(false)
 	, usingDefaultTexture(false)
 {
+	width = data->getWidth();
+	height = data->getHeight();
+
 	data->retain();
 	preload();
 }
 
 Image::Image(love::image::CompressedData *cdata)
-	: data(0)
+	: data(nullptr)
 	, cdata(cdata)
-	, width(cdata->getWidth(0))
-	, height(cdata->getHeight(0))
 	, paddedWidth(width)
 	, paddedHeight(height)
 	, texture(0)
@@ -66,34 +65,22 @@ Image::Image(love::image::CompressedData *cdata)
 	, compressed(true)
 	, usingDefaultTexture(false)
 {
+	width = cdata->getWidth(0);
+	height = cdata->getHeight(0);
+
 	cdata->retain();
 	preload();
 }
 
 Image::~Image()
 {
-	if (data != 0)
+	if (data != nullptr)
 		data->release();
-	if (cdata != 0)
+	if (cdata != nullptr)
 		cdata->release();
 	unload();
 }
 
-int Image::getWidth() const
-{
-	return width;
-}
-
-int Image::getHeight() const
-{
-	return height;
-}
-
-const Vertex *Image::getVertices() const
-{
-	return vertices;
-}
-
 love::image::ImageData *Image::getImageData() const
 {
 	return data;
@@ -144,6 +131,11 @@ void Image::postdraw() const
 	}
 }
 
+GLuint Image::getGLTexture() const
+{
+	return texture;
+}
+
 void Image::uploadCompressedMipmaps()
 {
 	if (!isCompressed() || !cdata || !hasCompressedTextureSupport(cdata->getFormat()))
@@ -246,7 +238,7 @@ void Image::checkMipmapsCreated()
 	mipmapsCreated = true;
 }
 
-void Image::setFilter(const Image::Filter &f)
+void Image::setFilter(const Texture::Filter &f)
 {
 	filter = f;
 
@@ -258,16 +250,11 @@ void Image::setFilter(const Image::Filter &f)
 	}
 
 	bind();
-	filter.anisotropy = gl.setTextureFilter(filter);
+	gl.setTextureFilter(filter);
 	checkMipmapsCreated();
 }
 
-const Image::Filter &Image::getFilter() const
-{
-	return filter;
-}
-
-void Image::setWrap(const Image::Wrap &w)
+void Image::setWrap(const Texture::Wrap &w)
 {
 	wrap = w;
 
@@ -275,11 +262,6 @@ void Image::setWrap(const Image::Wrap &w)
 	gl.setTextureWrap(w);
 }
 
-const Image::Wrap &Image::getWrap() const
-{
-	return wrap;
-}
-
 void Image::setMipmapSharpness(float sharpness)
 {
 	if (hasMipmapSharpnessSupport())
@@ -331,7 +313,6 @@ void Image::preload()
 	vertices[3].s = 1;
 	vertices[3].t = 0;
 
-	filter = getDefaultFilter();
 	filter.mipmap = defaultMipmapFilter;
 }
 
@@ -544,6 +525,7 @@ void Image::drawv(const Matrix &t, const Vertex *v) const
 	glVertexPointer(2, GL_FLOAT, sizeof(Vertex), (GLvoid *)&v[0].x);
 	glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), (GLvoid *)&v[0].s);
 
+	gl.prepareDraw();
 	glDrawArrays(GL_QUADS, 0, 4);
 
 	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
@@ -564,12 +546,12 @@ float Image::getDefaultMipmapSharpness()
 	return defaultMipmapSharpness;
 }
 
-void Image::setDefaultMipmapFilter(Image::FilterMode f)
+void Image::setDefaultMipmapFilter(Texture::FilterMode f)
 {
 	defaultMipmapFilter = f;
 }
 
-Image::FilterMode Image::getDefaultMipmapFilter()
+Texture::FilterMode Image::getDefaultMipmapFilter()
 {
 	return defaultMipmapFilter;
 }

+ 8 - 38
src/modules/graphics/opengl/Image.h

@@ -28,7 +28,7 @@
 #include "common/math.h"
 #include "image/ImageData.h"
 #include "image/CompressedData.h"
-#include "graphics/Image.h"
+#include "Texture.h"
 
 // OpenGL
 #include "OpenGL.h"
@@ -46,7 +46,7 @@ namespace opengl
  *
  * @author Anders Ruud
  **/
-class Image : public love::graphics::Image
+class Image : public Texture
 {
 public:
 
@@ -70,11 +70,6 @@ public:
 	 **/
 	virtual ~Image();
 
-	int getWidth() const;
-	int getHeight() const;
-
-	const Vertex *getVertices() const;
-
 	love::image::ImageData *getImageData() const;
 	love::image::CompressedData *getCompressedData() const;
 
@@ -84,7 +79,7 @@ public:
 	void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const;
 
 	/**
-	 * @copydoc DrawQable::drawq()
+	 * @copydoc Texture::drawq()
 	 **/
 	void drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const;
 
@@ -93,20 +88,13 @@ public:
 	 * globally scales texture coordinates if the Image has NPOT dimensions and
 	 * NPOT isn't supported, etc.
 	 **/
-	void predraw() const;
-	void postdraw() const;
-
-	/**
-	 * Sets the filter mode.
-	 * @param f The filter mode.
-	 **/
-	void setFilter(const Image::Filter &f);
-
-	const Image::Filter &getFilter() const;
+	virtual void predraw() const;
+	virtual void postdraw() const;
 
-	void setWrap(const Image::Wrap &w);
+	virtual GLuint getGLTexture() const;
 
-	const Image::Wrap &getWrap() const;
+	virtual void setFilter(const Texture::Filter &f);
+	virtual void setWrap(const Texture::Wrap &w);
 
 	void setMipmapSharpness(float sharpness);
 	float getMipmapSharpness() const;
@@ -151,12 +139,6 @@ private:
 
 	void drawv(const Matrix &t, const Vertex *v) const;
 
-	friend class Shader;
-	GLuint getTextureName() const
-	{
-		return texture;
-	}
-
 	// The ImageData from which the texture is created. May be null if
 	// Compressed image data was used to create the texture.
 	love::image::ImageData *data;
@@ -165,18 +147,12 @@ private:
 	// null if raw ImageData was used to create the texture.
 	love::image::CompressedData *cdata;
 
-	// Width and height of the hardware texture.
-	int width, height;
-
 	// Real dimensions of the texture, if it was auto-padded to POT size.
 	int paddedWidth, paddedHeight;
 
 	// OpenGL texture identifier.
 	GLuint texture;
 
-	// The source vertices of the image.
-	Vertex vertices[4];
-
 	// Mipmap texture LOD bias (sharpness) value.
 	float mipmapSharpness;
 
@@ -190,12 +166,6 @@ private:
 	// back to a default texture.
 	bool usingDefaultTexture;
 
-	// The image's filter mode
-	Image::Filter filter;
-
-	// The image's wrap mode
-	Image::Wrap wrap;
-
 	void preload();
 
 	void uploadTexturePadded();

+ 18 - 16
src/modules/graphics/opengl/Mesh.cpp

@@ -36,7 +36,7 @@ Mesh::Mesh(const std::vector<Vertex> &verts, Mesh::DrawMode mode)
 	, ibo(nullptr)
 	, element_count(0)
 	, draw_mode(mode)
-	, image(nullptr)
+	, texture(nullptr)
 	, colors_enabled(false)
 	, wireframe(false)
 {
@@ -170,27 +170,27 @@ size_t Mesh::getVertexMapCount() const
 	return element_count;
 }
 
-void Mesh::setImage(Image *img)
+void Mesh::setTexture(Texture *tex)
 {
-	img->retain();
+	tex->retain();
 
-	if (image)
-		image->release();
+	if (texture)
+		texture->release();
 
-	image = img;
+	texture = tex;
 }
 
-void Mesh::setImage()
+void Mesh::setTexture()
 {
-	if (image)
-		image->release();
+	if (texture)
+		texture->release();
 
-	image = nullptr;
+	texture = nullptr;
 }
 
-Image *Mesh::getImage() const
+Texture *Mesh::getTexture() const
 {
-	return image;
+	return texture;
 }
 
 void Mesh::setDrawMode(Mesh::DrawMode mode)
@@ -232,8 +232,8 @@ void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, flo
 	if (vertex_count == 0)
 		return;
 
-	if (image)
-		image->predraw();
+	if (texture)
+		texture->predraw();
 	else
 		gl.bindTexture(0);
 
@@ -266,6 +266,8 @@ void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, flo
 
 	GLenum mode = getGLDrawMode(draw_mode);
 
+	gl.prepareDraw();
+
 	if (ibo && element_count > 0)
 	{
 		VertexBuffer::Bind ibo_bind(*ibo);
@@ -297,8 +299,8 @@ void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, flo
 
 	glPopMatrix();
 
-	if (image)
-		image->postdraw();
+	if (texture)
+		texture->postdraw();
 }
 
 GLenum Mesh::getGLDrawMode(Mesh::DrawMode mode) const

+ 9 - 9
src/modules/graphics/opengl/Mesh.h

@@ -26,7 +26,7 @@
 #include "common/math.h"
 #include "common/StringMap.h"
 #include "graphics/Drawable.h"
-#include "Image.h"
+#include "Texture.h"
 #include "VertexBuffer.h"
 
 // C++
@@ -110,20 +110,20 @@ public:
 	size_t getVertexMapCount() const;
 
 	/**
-	 * Sets the Image used when drawing the Mesh.
+	 * Sets the texture used when drawing the Mesh.
 	 **/
-	void setImage(Image *img);
+	void setTexture(Texture *texture);
 
 	/**
-	 * Disables any Image from being used when drawing the Mesh.
+	 * Disables any texture from being used when drawing the Mesh.
 	 **/
-	void setImage();
+	void setTexture();
 
 	/**
-	 * Gets the Image used when drawing the Mesh. May return null if no Image is
-	 * set.
+	 * Gets the texture used when drawing the Mesh. May return null if no
+	 * texture is set.
 	 **/
-	Image *getImage() const;
+	Texture *getTexture() const;
 
 	/**
 	 * Sets the draw mode used when drawing the Mesh.
@@ -167,7 +167,7 @@ private:
 
 	DrawMode draw_mode;
 
-	Image *image;
+	Texture *texture;
 
 	// Whether the per-vertex colors are used when drawing.
 	bool colors_enabled;

+ 58 - 48
src/modules/graphics/opengl/OpenGL.cpp

@@ -23,6 +23,7 @@
 #include "OpenGL.h"
 
 #include "Shader.h"
+#include "Canvas.h"
 #include "common/Exception.h"
 
 // C++
@@ -211,6 +212,14 @@ void OpenGL::createDefaultTexture()
 	bindTexture(curtexture);
 }
 
+void OpenGL::prepareDraw()
+{
+	// Make sure the active shader has the correct values for the built-in
+	// screen params uniform.
+	if (Shader::current)
+		Shader::current->checkSetScreenParams();
+}
+
 void OpenGL::setColor(const Color &c)
 {
 	glColor4ubv(&c.r);
@@ -235,16 +244,13 @@ Color OpenGL::getClearColor() const
 
 void OpenGL::setViewport(const OpenGL::Viewport &v)
 {
-	Viewport oldViewport = state.viewport;
-
 	glViewport(v.x, v.y, v.w, v.h);
 	state.viewport = v;
 
 	// glScissor starts from the lower left, so we compensate when setting the
 	// scissor. When the viewport is changed, we need to manually update the
 	// scissor again.
-	if (v.h != oldViewport.h)
-		setScissor(state.scissor);
+	setScissor(state.scissor);
 }
 
 OpenGL::Viewport OpenGL::getViewport() const
@@ -254,9 +260,15 @@ OpenGL::Viewport OpenGL::getViewport() const
 
 void OpenGL::setScissor(const OpenGL::Viewport &v)
 {
-	// We need to compensate for glScissor starting from the lower left of the
-	// viewport instead of the top left.
-	glScissor(v.x,  state.viewport.h - (v.y + v.h), v.w, v.h);
+	if (Canvas::current)
+		glScissor(v.x, v.y, v.w, v.h);
+	else
+	{
+		// With no Canvas active, we need to compensate for glScissor starting
+		// from the lower left of the viewport instead of the top left.
+		glScissor(v.x, state.viewport.h - (v.y + v.h), v.w, v.h);
+	}
+
 	state.scissor = v;
 }
 
@@ -275,7 +287,7 @@ void OpenGL::setTextureUnit(int textureunit)
 		if (state.textureUnits.size() > 1)
 			glActiveTexture(GL_TEXTURE0 + textureunit);
 		else
-			throw love::Exception("Multitexturing not supported.");
+			throw love::Exception("Multitexturing is not supported.");
 	}
 
 	state.curTextureUnit = textureunit;
@@ -322,26 +334,26 @@ void OpenGL::deleteTexture(GLuint texture)
 	glDeleteTextures(1, &texture);
 }
 
-float OpenGL::setTextureFilter(const graphics::Image::Filter &f)
+float OpenGL::setTextureFilter(graphics::Texture::Filter &f)
 {
 	GLint gmin, gmag;
 
-	if (f.mipmap == Image::FILTER_NONE)
+	if (f.mipmap == Texture::FILTER_NONE)
 	{
-		if (f.min == Image::FILTER_NEAREST)
+		if (f.min == Texture::FILTER_NEAREST)
 			gmin = GL_NEAREST;
-		else // f.min == Image::FILTER_LINEAR
+		else // f.min == Texture::FILTER_LINEAR
 			gmin = GL_LINEAR;
 	}
 	else
 	{
-		if (f.min == Image::FILTER_NEAREST && f.mipmap == Image::FILTER_NEAREST)
+		if (f.min == Texture::FILTER_NEAREST && f.mipmap == Texture::FILTER_NEAREST)
 			gmin = GL_NEAREST_MIPMAP_NEAREST;
-		else if (f.min == Image::FILTER_NEAREST && f.mipmap == Image::FILTER_LINEAR)
+		else if (f.min == Texture::FILTER_NEAREST && f.mipmap == Texture::FILTER_LINEAR)
 			gmin = GL_NEAREST_MIPMAP_LINEAR;
-		else if (f.min == Image::FILTER_LINEAR && f.mipmap == Image::FILTER_NEAREST)
+		else if (f.min == Texture::FILTER_LINEAR && f.mipmap == Texture::FILTER_NEAREST)
 			gmin = GL_LINEAR_MIPMAP_NEAREST;
-		else if (f.min == Image::FILTER_LINEAR && f.mipmap == Image::FILTER_LINEAR)
+		else if (f.min == Texture::FILTER_LINEAR && f.mipmap == Texture::FILTER_LINEAR)
 			gmin = GL_LINEAR_MIPMAP_LINEAR;
 		else
 			gmin = GL_LINEAR;
@@ -350,10 +362,10 @@ float OpenGL::setTextureFilter(const graphics::Image::Filter &f)
 
 	switch (f.mag)
 	{
-	case Image::FILTER_NEAREST:
+	case Texture::FILTER_NEAREST:
 		gmag = GL_NEAREST;
 		break;
-	case Image::FILTER_LINEAR:
+	case Texture::FILTER_LINEAR:
 	default:
 		gmag = GL_LINEAR;
 		break;
@@ -362,60 +374,58 @@ float OpenGL::setTextureFilter(const graphics::Image::Filter &f)
 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gmin);
 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gmag);
 
-	float anisotropy = 1.0f;
-
 	if (GLEE_EXT_texture_filter_anisotropic)
 	{
-		anisotropy = std::min(std::max(f.anisotropy, 1.0f), maxAnisotropy);
-		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisotropy);
+		f.anisotropy = std::min(std::max(f.anisotropy, 1.0f), maxAnisotropy);
+		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, f.anisotropy);
 	}
 
-	return anisotropy;
+	return f.anisotropy;
 }
 
-graphics::Image::Filter OpenGL::getTextureFilter()
+graphics::Texture::Filter OpenGL::getTextureFilter()
 {
 	GLint gmin, gmag;
 	glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, &gmin);
 	glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, &gmag);
 
-	Image::Filter f;
+	Texture::Filter f;
 
 	switch (gmin)
 	{
 	case GL_NEAREST:
-		f.min = Image::FILTER_NEAREST;
-		f.mipmap = Image::FILTER_NONE;
+		f.min = Texture::FILTER_NEAREST;
+		f.mipmap = Texture::FILTER_NONE;
 		break;
 	case GL_NEAREST_MIPMAP_NEAREST:
-		f.min = f.mipmap = Image::FILTER_NEAREST;
+		f.min = f.mipmap = Texture::FILTER_NEAREST;
 		break;
 	case GL_NEAREST_MIPMAP_LINEAR:
-		f.min = Image::FILTER_NEAREST;
-		f.mipmap = Image::FILTER_LINEAR;
+		f.min = Texture::FILTER_NEAREST;
+		f.mipmap = Texture::FILTER_LINEAR;
 		break;
 	case GL_LINEAR_MIPMAP_NEAREST:
-		f.min = Image::FILTER_LINEAR;
-		f.mipmap = Image::FILTER_NEAREST;
+		f.min = Texture::FILTER_LINEAR;
+		f.mipmap = Texture::FILTER_NEAREST;
 		break;
 	case GL_LINEAR_MIPMAP_LINEAR:
-		f.min = f.mipmap = Image::FILTER_LINEAR;
+		f.min = f.mipmap = Texture::FILTER_LINEAR;
 		break;
 	case GL_LINEAR:
 	default:
-		f.min = Image::FILTER_LINEAR;
-		f.mipmap = Image::FILTER_NONE;
+		f.min = Texture::FILTER_LINEAR;
+		f.mipmap = Texture::FILTER_NONE;
 		break;
 	}
 
 	switch (gmag)
 	{
 	case GL_NEAREST:
-		f.mag = Image::FILTER_NEAREST;
+		f.mag = Texture::FILTER_NEAREST;
 		break;
 	case GL_LINEAR:
 	default:
-		f.mag = Image::FILTER_LINEAR;
+		f.mag = Texture::FILTER_LINEAR;
 		break;
 	}
 
@@ -425,16 +435,16 @@ graphics::Image::Filter OpenGL::getTextureFilter()
 	return f;
 }
 
-void OpenGL::setTextureWrap(const graphics::Image::Wrap &w)
+void OpenGL::setTextureWrap(const graphics::Texture::Wrap &w)
 {
 	GLint gs, gt;
 
 	switch (w.s)
 	{
-	case Image::WRAP_CLAMP:
+	case Texture::WRAP_CLAMP:
 		gs = GL_CLAMP_TO_EDGE;
 		break;
-	case Image::WRAP_REPEAT:
+	case Texture::WRAP_REPEAT:
 	default:
 		gs = GL_REPEAT;
 		break;
@@ -442,10 +452,10 @@ void OpenGL::setTextureWrap(const graphics::Image::Wrap &w)
 
 	switch (w.t)
 	{
-	case Image::WRAP_CLAMP:
+	case Texture::WRAP_CLAMP:
 		gt = GL_CLAMP_TO_EDGE;
 		break;
-	case Image::WRAP_REPEAT:
+	case Texture::WRAP_REPEAT:
 	default:
 		gt = GL_REPEAT;
 		break;
@@ -455,34 +465,34 @@ void OpenGL::setTextureWrap(const graphics::Image::Wrap &w)
 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, gt);
 }
 
-graphics::Image::Wrap OpenGL::getTextureWrap()
+graphics::Texture::Wrap OpenGL::getTextureWrap()
 {
 	GLint gs, gt;
 
 	glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, &gs);
 	glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, &gt);
 
-	Image::Wrap w;
+	Texture::Wrap w;
 
 	switch (gs)
 	{
 	case GL_CLAMP_TO_EDGE:
-		w.s = Image::WRAP_CLAMP;
+		w.s = Texture::WRAP_CLAMP;
 		break;
 	case GL_REPEAT:
 	default:
-		w.s = Image::WRAP_REPEAT;
+		w.s = Texture::WRAP_REPEAT;
 		break;
 	}
 
 	switch (gt)
 	{
 	case GL_CLAMP_TO_EDGE:
-		w.t = Image::WRAP_CLAMP;
+		w.t = Texture::WRAP_CLAMP;
 		break;
 	case GL_REPEAT:
 	default:
-		w.t = Image::WRAP_REPEAT;
+		w.t = Texture::WRAP_REPEAT;
 		break;
 	}
 

+ 16 - 10
src/modules/graphics/opengl/OpenGL.h

@@ -25,9 +25,9 @@
 
 // LOVE
 #include "graphics/Color.h"
-#include "graphics/Image.h"
+#include "graphics/Texture.h"
 
-// STL
+// C++
 #include <vector>
 
 // The last argument to AttribPointer takes a buffer offset casted to a pointer.
@@ -93,6 +93,12 @@ public:
 	 **/
 	void deInitContext();
 
+	/**
+	 * Set up necessary state (LOVE-provided shader uniforms, etc.) for drawing.
+	 * This *MUST* be called directly before OpenGL drawing functions.
+	 **/
+	void prepareDraw();
+
 	/**
 	 * Sets the current constant color.
 	 **/
@@ -163,25 +169,25 @@ public:
 	void deleteTexture(GLuint texture);
 
 	/**
-	 * Sets the image filter mode for the currently bound texture.
+	 * Sets the texture filter mode for the currently bound texture.
 	 * Returns the actual amount of anisotropic filtering set.
 	 **/
-	float setTextureFilter(const graphics::Image::Filter &f);
+	float setTextureFilter(graphics::Texture::Filter &f);
 
 	/**
-	 * Returns the image filter mode for the currently bound texture.
+	 * Returns the texture filter mode for the currently bound texture.
 	 **/
-	graphics::Image::Filter getTextureFilter();
+	graphics::Texture::Filter getTextureFilter();
 
 	/**
-	 * Sets the image wrap mode for the currently bound texture.
+	 * Sets the texture wrap mode for the currently bound texture.
 	 **/
-	void setTextureWrap(const graphics::Image::Wrap &w);
+	void setTextureWrap(const graphics::Texture::Wrap &w);
 
 	/**
-	 * Returns the image wrap mode for the currently bound texture.
+	 * Returns the texture wrap mode for the currently bound texture.
 	 **/
-	graphics::Image::Wrap getTextureWrap();
+	graphics::Texture::Wrap getTextureWrap();
 
 	/**
 	 * Returns the maximum supported width or height of a texture.

+ 26 - 23
src/modules/graphics/opengl/ParticleSystem.cpp

@@ -58,13 +58,13 @@ float calculate_variation(float inner, float outer, float var)
 
 } // anonymous namespace
 
-ParticleSystem::ParticleSystem(Image *image, uint32 size)
+ParticleSystem::ParticleSystem(Texture *texture, uint32 size)
 	: pMem(nullptr)
 	, pFree(nullptr)
 	, pHead(nullptr)
 	, pTail(nullptr)
 	, particleVerts(nullptr)
-	, image(image)
+	, texture(texture)
 	, active(true)
 	, insertMode(INSERT_MODE_TOP)
 	, maxParticles(0)
@@ -92,8 +92,8 @@ ParticleSystem::ParticleSystem(Image *image, uint32 size)
 	, spinStart(0)
 	, spinEnd(0)
 	, spinVariation(0)
-	, offsetX(float(image->getWidth())*0.5f)
-	, offsetY(float(image->getHeight())*0.5f)
+	, offsetX(float(texture->getWidth())*0.5f)
+	, offsetY(float(texture->getHeight())*0.5f)
 {
 	if (size == 0 || size > MAX_PARTICLES)
 		throw love::Exception("Invalid ParticleSystem size.");
@@ -101,7 +101,7 @@ ParticleSystem::ParticleSystem(Image *image, uint32 size)
 	sizes.push_back(1.0f);
 	colors.push_back(Colorf(1.0f, 1.0f, 1.0f, 1.0f));
 	setBufferSize(size);
-	image->retain();
+	texture->retain();
 }
 
 ParticleSystem::ParticleSystem(const ParticleSystem &p)
@@ -110,7 +110,7 @@ ParticleSystem::ParticleSystem(const ParticleSystem &p)
 	, pHead(nullptr)
 	, pTail(nullptr)
 	, particleVerts(nullptr)
-	, image(p.image)
+	, texture(p.texture)
 	, active(p.active)
 	, insertMode(p.insertMode)
 	, maxParticles(p.maxParticles)
@@ -147,14 +147,14 @@ ParticleSystem::ParticleSystem(const ParticleSystem &p)
 {
 	setBufferSize(maxParticles);
 
-	if (image != nullptr)
-		image->retain();
+	if (texture != nullptr)
+		texture->retain();
 }
 
 ParticleSystem::~ParticleSystem()
 {
-	if (image != nullptr)
-		image->release();
+	if (texture != nullptr)
+		texture->release();
 
 	deleteBuffers();
 }
@@ -403,17 +403,19 @@ ParticleSystem::particle *ParticleSystem::removeParticle(particle *p)
 	return pNext;
 }
 
-void ParticleSystem::setImage(Image *image)
+void ParticleSystem::setTexture(Texture *texture)
 {
-	Object::AutoRelease imagerelease(this->image);
+	Object::AutoRelease imagerelease(this->texture);
 
-	this->image = image;
-	this->image->retain();
+	this->texture = texture;
+
+	if (texture)
+		texture->retain();
 }
 
-Image *ParticleSystem::getImage() const
+Texture *ParticleSystem::getTexture() const
 {
-	return image;
+	return texture;
 }
 
 void ParticleSystem::setInsertMode(InsertMode mode)
@@ -777,7 +779,7 @@ bool ParticleSystem::isFull() const
 void ParticleSystem::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const
 {
 	uint32 pCount = getCount();
-	if (pCount == 0 || image == nullptr || pMem == nullptr || particleVerts == nullptr)
+	if (pCount == 0 || texture == nullptr || pMem == nullptr || particleVerts == nullptr)
 		return;
 
 	Color curcolor = gl.getColor();
@@ -788,7 +790,7 @@ void ParticleSystem::draw(float x, float y, float angle, float sx, float sy, flo
 	t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
 	glMultMatrixf((const GLfloat *)t.getElements());
 
-	const Vertex *imageVerts = image->getVertices();
+	const Vertex *textureVerts = texture->getVertices();
 	Vertex *pVerts = particleVerts;
 	particle *p = pHead;
 
@@ -797,13 +799,13 @@ void ParticleSystem::draw(float x, float y, float angle, float sx, float sy, flo
 	{
 		// particle vertices are image vertices transformed by particle information
 		t.setTransformation(p->position[0], p->position[1], p->rotation, p->size, p->size, offsetX, offsetY, 0.0f, 0.0f);
-		t.transform(pVerts, imageVerts, 4);
+		t.transform(pVerts, textureVerts, 4);
 
 		// set the texture coordinate and color data for particle vertices
 		for (int v = 0; v < 4; v++)
 		{
-			pVerts[v].s = imageVerts[v].s;
-			pVerts[v].t = imageVerts[v].t;
+			pVerts[v].s = textureVerts[v].s;
+			pVerts[v].t = textureVerts[v].t;
 
 			// particle colors are stored as floats (0-1) but vertex colors are stored as unsigned bytes (0-255)
 			pVerts[v].r = (unsigned char) (p->color.r*255);
@@ -816,7 +818,7 @@ void ParticleSystem::draw(float x, float y, float angle, float sx, float sy, flo
 		p = p->next;
 	}
 
-	image->predraw();
+	texture->predraw();
 
 	glEnableClientState(GL_COLOR_ARRAY);
 	glEnableClientState(GL_VERTEX_ARRAY);
@@ -826,13 +828,14 @@ void ParticleSystem::draw(float x, float y, float angle, float sx, float sy, flo
 	glVertexPointer(2, GL_FLOAT, sizeof(Vertex), (GLvoid *) &particleVerts[0].x);
 	glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), (GLvoid *) &particleVerts[0].s);
 
+	gl.prepareDraw();
 	glDrawArrays(GL_QUADS, 0, pCount * 4);
 
 	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
 	glDisableClientState(GL_VERTEX_ARRAY);
 	glDisableClientState(GL_COLOR_ARRAY);
 
-	image->postdraw();
+	texture->postdraw();
 
 	glPopMatrix();
 

+ 10 - 10
src/modules/graphics/opengl/ParticleSystem.h

@@ -27,7 +27,7 @@
 #include "common/Vector.h"
 #include "graphics/Drawable.h"
 #include "graphics/Color.h"
-#include "Image.h"
+#include "Texture.h"
 
 // STL
 #include <vector>
@@ -76,9 +76,9 @@ public:
 	static const uint32 MAX_PARTICLES = LOVE_INT32_MAX / 4;
 
 	/**
-	 * Creates a particle system with the specified buffersize and image.
+	 * Creates a particle system with the specified buffer size and texture.
 	 **/
-	ParticleSystem(Image *image, uint32 buffer);
+	ParticleSystem(Texture *texture, uint32 buffer);
 	ParticleSystem(const ParticleSystem &p);
 
 	/**
@@ -94,15 +94,15 @@ public:
 	ParticleSystem *clone();
 
 	/**
-	 * Sets the image used in the particle system.
-	 * @param image The new image.
+	 * Sets the texture used in the particle system.
+	 * @param texture The new texture.
 	 **/
-	void setImage(Image *image);
+	void setTexture(Texture *texture);
 
 	/**
-	 * Returns the image used when drawing the particle system.
+	 * Returns the texture used when drawing the particle system.
 	 **/
-	Image *getImage() const;
+	Texture *getTexture() const;
 
 	/**
 	 * Clears the current buffer and allocates the appropriate amount of space for the buffer.
@@ -531,8 +531,8 @@ protected:
 	// array of transformed vertex data for all particles, for drawing
 	Vertex *particleVerts;
 
-	// The image to be drawn.
-	Image *image;
+	// The texture to be drawn.
+	Texture *texture;
 
 	// Whether the particle emitter is active.
 	bool active;

+ 2 - 0
src/modules/graphics/opengl/Polyline.cpp

@@ -327,6 +327,8 @@ Polyline::~Polyline()
 
 void Polyline::draw()
 {
+	gl.prepareDraw();
+
 	// draw the core line
 	gl.bindTexture(0);
 	glEnableClientState(GL_VERTEX_ARRAY);

+ 134 - 55
src/modules/graphics/opengl/Shader.cpp

@@ -18,11 +18,13 @@
  * 3. This notice may not be removed or altered from any source distribution.
  **/
 
+// LOVE
 #include "common/config.h"
 
 #include "Shader.h"
-#include "Graphics.h"
+#include "Canvas.h"
 
+// C++
 #include <algorithm>
 
 namespace love
@@ -47,7 +49,7 @@ namespace
 
 		~TemporaryAttacher()
 		{
-			if (prevShader != NULL)
+			if (prevShader != nullptr)
 				prevShader->attach();
 			else
 				curShader->detach();
@@ -59,28 +61,30 @@ namespace
 } // anonymous namespace
 
 
-Shader *Shader::current = NULL;
+Shader *Shader::current = nullptr;
 
-GLint Shader::maxTextureUnits = 0;
+GLint Shader::maxTexUnits = 0;
 std::vector<int> Shader::textureCounters;
 
 Shader::Shader(const ShaderSources &sources)
 	: shaderSources(sources)
 	, program(0)
+	, builtinUniforms()
+	, lastCanvas((Canvas *) -1)
 {
 	if (shaderSources.empty())
 		throw love::Exception("Cannot create shader: no source code!");
 
-	if (maxTextureUnits <= 0)
+	if (maxTexUnits <= 0)
 	{
 		GLint maxtexunits;
 		glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxtexunits);
-		maxTextureUnits = std::max(maxtexunits - 1, 0);
+		maxTexUnits = std::max(maxtexunits - 1, 0);
 	}
 
 	// initialize global texture id counters if needed
-	if (textureCounters.size() < (size_t) maxTextureUnits)
-		textureCounters.resize(maxTextureUnits, 0);
+	if (textureCounters.size() < (size_t) maxTexUnits)
+		textureCounters.resize(maxTexUnits, 0);
 
 	// load shader source and create program object
 	loadVolatile();
@@ -147,7 +151,7 @@ GLuint Shader::compileCode(ShaderType type, const std::string &code)
 	glGetShaderiv(shaderid, GL_INFO_LOG_LENGTH, &infologlen);
 
 	GLchar *infolog = new GLchar[infologlen + 1];
-	glGetShaderInfoLog(shaderid, infologlen, NULL, infolog);
+	glGetShaderInfoLog(shaderid, infologlen, nullptr, infolog);
 
 	// Save any warnings for later querying.
 	if (infologlen > 0)
@@ -232,6 +236,11 @@ void Shader::mapActiveUniforms()
 				u.name.erase(u.name.length() - 3);
 		}
 
+		// If this is a built-in (LOVE-created) uniform, store the location.
+		BuiltinExtern builtin;
+		if (builtinNames.find(u.name.c_str(), builtin))
+			builtinUniforms[int(builtin)] = u.location;
+
 		if (u.location != -1)
 			uniforms[u.name] = u;
 	}
@@ -240,8 +249,12 @@ void Shader::mapActiveUniforms()
 bool Shader::loadVolatile()
 {
 	// zero out active texture list
-	activeTextureUnits.clear();
-	activeTextureUnits.insert(activeTextureUnits.begin(), maxTextureUnits, 0);
+	activeTexUnits.clear();
+	activeTexUnits.insert(activeTexUnits.begin(), maxTexUnits, 0);
+
+	// Built-in uniform locations default to -1 (nonexistant.)
+	for (int i = 0; i < int(BUILTIN_MAX_ENUM); i++)
+		builtinUniforms[i] = -1;
 
 	std::vector<GLuint> shaderids;
 
@@ -263,7 +276,7 @@ bool Shader::loadVolatile()
 	if (current == this)
 	{
 		// make sure glUseProgram gets called.
-		current = NULL;
+		current = nullptr;
 		attach();
 	}
 
@@ -282,19 +295,23 @@ void Shader::unloadVolatile()
 	}
 
 	// decrement global texture id counters for texture units which had textures bound from this shader
-	for (size_t i = 0; i < activeTextureUnits.size(); ++i)
+	for (size_t i = 0; i < activeTexUnits.size(); ++i)
 	{
-		if (activeTextureUnits[i] > 0)
+		if (activeTexUnits[i] > 0)
 			textureCounters[i] = std::max(textureCounters[i] - 1, 0);
 	}
 
 	// active texture list is probably invalid, clear it
-	activeTextureUnits.clear();
-	activeTextureUnits.insert(activeTextureUnits.begin(), maxTextureUnits, 0);
+	activeTexUnits.clear();
+	activeTexUnits.insert(activeTexUnits.begin(), maxTexUnits, 0);
 
 	// same with uniform location list
 	uniforms.clear();
 
+	// And the locations of any built-in uniform variables.
+	for (int i = 0; i < int(BUILTIN_MAX_ENUM); i++)
+		builtinUniforms[i] = -1;
+
 	shaderWarnings.clear();
 }
 
@@ -337,7 +354,7 @@ void Shader::attach(bool temporary)
 {
 	if (current != this)
 	{
-		if (current != NULL)
+		if (current != nullptr)
 			current->release();
 
 		glUseProgram(program);
@@ -350,10 +367,10 @@ void Shader::attach(bool temporary)
 	{
 		// make sure all sent textures are properly bound to their respective texture units
 		// note: list potentially contains texture ids of deleted/invalid textures!
-		for (size_t i = 0; i < activeTextureUnits.size(); ++i)
+		for (size_t i = 0; i < activeTexUnits.size(); ++i)
 		{
-			if (activeTextureUnits[i] > 0)
-				gl.bindTextureToUnit(activeTextureUnits[i], i + 1, false);
+			if (activeTexUnits[i] > 0)
+				gl.bindTextureToUnit(activeTexUnits[i], i + 1, false);
 		}
 
 		// We always want to use texture unit 0 for everyhing else.
@@ -363,10 +380,10 @@ void Shader::attach(bool temporary)
 
 void Shader::detach()
 {
-	if (current != NULL)
+	if (current != nullptr)
 		glUseProgram(0);
 
-	current = NULL;
+	current = nullptr;
 }
 
 const Shader::Uniform &Shader::getUniform(const std::string &name) const
@@ -544,61 +561,53 @@ void Shader::sendMatrix(const std::string &name, int size, const GLfloat *m, int
 	}
 }
 
-void Shader::sendTexture(const std::string &name, GLuint texture)
+void Shader::sendTexture(const std::string &name, Texture *texture)
 {
+	GLuint gltex = texture->getGLTexture();
+
 	TemporaryAttacher attacher(this);
 
-	int textureunit = getTextureUnit(name);
+	int texunit = getTextureUnit(name);
 
 	const Uniform &u = getUniform(name);
 	checkSetUniformError(u, 1, 1, UNIFORM_SAMPLER);
 
 	// bind texture to assigned texture unit and send uniform to shader program
-	gl.bindTextureToUnit(texture, textureunit, false);
+	gl.bindTextureToUnit(gltex, texunit, false);
 
-	glUniform1i(u.location, textureunit);
+	glUniform1i(u.location, texunit);
 
 	// reset texture unit
 	gl.setTextureUnit(0);
 
 	// increment global shader texture id counter for this texture unit, if we haven't already
-	if (activeTextureUnits[textureunit-1] == 0)
-		++textureCounters[textureunit-1];
+	if (activeTexUnits[texunit-1] == 0)
+		++textureCounters[texunit-1];
 
 	// store texture id so it can be re-bound to the proper texture unit later
-	activeTextureUnits[textureunit-1] = texture;
+	activeTexUnits[texunit-1] = gltex;
+
+	retainObject(name, texture);
 }
 
-void Shader::retainTexture(const std::string &name, Object *texture)
+void Shader::retainObject(const std::string &name, Object *object)
 {
 	auto it = boundRetainables.find(name);
 	if (it != boundRetainables.end())
 		it->second->release();
 
-	texture->retain();
-	boundRetainables[name] = texture;
-}
-
-void Shader::sendImage(const std::string &name, Image &image)
-{
-	sendTexture(name, image.getTextureName());
-	retainTexture(name, &image);
-}
-
-void Shader::sendCanvas(const std::string &name, Canvas &canvas)
-{
-	sendTexture(name, canvas.getTextureName());
-	retainTexture(name, &canvas);
+	object->retain();
+	boundRetainables[name] = object;
 }
 
 int Shader::getTextureUnit(const std::string &name)
 {
-	auto it = textureUnitPool.find(name);
+	auto it = texUnitPool.find(name);
 
-	if (it != textureUnitPool.end())
+	if (it != texUnitPool.end())
 		return it->second;
 
-	int textureunit = 1;
+	int texunit = 1;
 
 	// prefer texture units which are unused by all other shaders
 	auto freeunit_it = std::find(textureCounters.begin(), textureCounters.end(), 0);
@@ -606,33 +615,96 @@ int Shader::getTextureUnit(const std::string &name)
 	if (freeunit_it != textureCounters.end())
 	{
 		// we don't want to use unit 0
-		textureunit = std::distance(textureCounters.begin(), freeunit_it) + 1;
+		texunit = std::distance(textureCounters.begin(), freeunit_it) + 1;
 	}
 	else
 	{
 		// no completely unused texture units exist, try to use next free slot in our own list
-		auto nextunit_it = std::find(activeTextureUnits.begin(), activeTextureUnits.end(), 0);
+		auto nextunit_it = std::find(activeTexUnits.begin(), activeTexUnits.end(), 0);
 
-		if (nextunit_it == activeTextureUnits.end())
+		if (nextunit_it == activeTexUnits.end())
 			throw love::Exception("No more texture units available for shader.");
 
 		// we don't want to use unit 0
-		textureunit = std::distance(activeTextureUnits.begin(), nextunit_it) + 1;
+		texunit = std::distance(activeTexUnits.begin(), nextunit_it) + 1;
+	}
+
+	texUnitPool[name] = texunit;
+	return texunit;
+}
+
+bool Shader::hasBuiltinExtern(BuiltinExtern builtin) const
+{
+	return builtinUniforms[int(builtin)] != -1;
+}
+
+bool Shader::sendBuiltinFloat(BuiltinExtern builtin, int size, const GLfloat *vec, int count)
+{
+	if (!hasBuiltinExtern(builtin))
+		return false;
+
+	GLint location = builtinUniforms[int(builtin)];
+
+	TemporaryAttacher attacher(this);
+
+	switch (size)
+	{
+	case 1:
+		glUniform1fv(location, count, vec);
+		break;
+	case 2:
+		glUniform2fv(location, count, vec);
+		break;
+	case 3:
+		glUniform3fv(location, count, vec);
+		break;
+	case 4:
+		glUniform4fv(location, count, vec);
+		break;
+	default:
+		return false;
 	}
+	
+	return true;
+}
 
-	textureUnitPool[name] = textureunit;
-	return textureunit;
+void Shader::checkSetScreenParams()
+{
+	if (lastCanvas == Canvas::current)
+		return;
+
+	// In the shader, we do pixcoord.y = gl_FragCoord.y * params[0] + params[1].
+	// This lets us flip pixcoord.y when needed, to be consistent (Canvases
+	// have flipped y-values for pixel coordinates.)
+	GLfloat params[] = {0.0f, 0.0f};
+
+	if (Canvas::current != nullptr)
+	{
+		// gl_FragCoord.y is flipped in Canvases, so we un-flip:
+		// pixcoord.y = gl_FragCoord.y * -1.0 + height.
+		params[0] = -1.0f;
+		params[1] = (float) Canvas::current->getHeight();
+	}
+	else
+	{
+		// No flipping: pixcoord.y = gl_FragCoord.y * 1.0 + 0.0.
+		params[0] = 1.0f;
+		params[1] = 0.0f;
+	}
+
+	sendBuiltinFloat(BUILTIN_SCREEN_PARAMS, 2, params, 1);
+	lastCanvas = Canvas::current;
 }
 
 std::string Shader::getGLSLVersion()
 {
-	const char *tmp = 0;
+	const char *tmp = nullptr;
 
 	// GL_SHADING_LANGUAGE_VERSION isn't available in OpenGL < 2.0.
 	if (GLEE_VERSION_2_0 || GLEE_ARB_shading_language_100)
 		tmp = (const char *) glGetString(GL_SHADING_LANGUAGE_VERSION);
 
-	if (tmp == 0)
+	if (tmp == nullptr)
 		return "0.0";
 
 	// the version string always begins with a version number of the format
@@ -658,6 +730,13 @@ StringMap<Shader::ShaderType, Shader::TYPE_MAX_ENUM>::Entry Shader::typeNameEntr
 
 StringMap<Shader::ShaderType, Shader::TYPE_MAX_ENUM> Shader::typeNames(Shader::typeNameEntries, sizeof(Shader::typeNameEntries));
 
+StringMap<Shader::BuiltinExtern, Shader::BUILTIN_MAX_ENUM>::Entry Shader::builtinNameEntries[] =
+{
+	{"love_ScreenParams", Shader::BUILTIN_SCREEN_PARAMS},
+};
+
+StringMap<Shader::BuiltinExtern, Shader::BUILTIN_MAX_ENUM> Shader::builtinNames(Shader::builtinNameEntries, sizeof(Shader::builtinNameEntries));
+
 } // opengl
 } // graphics
 } // love

+ 31 - 13
src/modules/graphics/opengl/Shader.h

@@ -25,8 +25,7 @@
 #include "common/Object.h"
 #include "common/StringMap.h"
 #include "OpenGL.h"
-#include "Image.h"
-#include "Canvas.h"
+#include "Texture.h"
 
 // STL
 #include <string>
@@ -39,6 +38,9 @@ namespace graphics
 {
 namespace opengl
 {
+
+class Canvas;
+
 // A GLSL shader
 class Shader : public Object, public Volatile
 {
@@ -54,6 +56,13 @@ public:
 		TYPE_MAX_ENUM
 	};
 
+	// Built-in extern (uniform) variables.
+	enum BuiltinExtern
+	{
+		BUILTIN_SCREEN_PARAMS,
+		BUILTIN_MAX_ENUM
+	};
+
 	// Type for a list of shader source codes in the form of sources[shadertype] = code
 	typedef std::map<ShaderType, std::string> ShaderSources;
 
@@ -120,18 +129,18 @@ public:
 	void sendMatrix(const std::string &name, int size, const GLfloat *m, int count);
 
 	/**
-	 * Send an image to this Shader as a uniform.
+	 * Send a texture to this Shader as a uniform.
 	 *
 	 * @param name The name of the uniform variable in the source code.
 	 **/
-	void sendImage(const std::string &name, Image &image);
+	void sendTexture(const std::string &name, Texture *texture);
 
 	/**
-	 * Send a canvas to this Shader as a uniform.
-	 *
-	 * @param name The name of the uniform variable in the source code.
+	 * Internal use only.
 	 **/
-	void sendCanvas(const std::string &name, Canvas &canvas);
+	bool hasBuiltinExtern(BuiltinExtern builtin) const;
+	bool sendBuiltinFloat(BuiltinExtern builtin, int size, const GLfloat *m, int count);
+	void checkSetScreenParams();
 
 	static std::string getGLSLVersion();
 	static bool isSupported();
@@ -172,8 +181,7 @@ private:
 
 	int getTextureUnit(const std::string &name);
 
-	void sendTexture(const std::string &name, GLuint texture);
-	void retainTexture(const std::string &name, Object *texture);
+	void retainObject(const std::string &name, Object *object);
 
 	// Get any warnings or errors generated only by the shader program object.
 	std::string getProgramWarnings() const;
@@ -187,24 +195,34 @@ private:
 	// volatile
 	GLuint program;
 
+	// Location values for any built-in uniform variables.
+	GLint builtinUniforms[BUILTIN_MAX_ENUM];
+
 	// Uniform location buffer map
 	std::map<std::string, Uniform> uniforms;
 
 	// Texture unit pool for setting images
-	std::map<std::string, GLint> textureUnitPool; // textureUnitPool[name] = textureunit
-	std::vector<GLuint> activeTextureUnits; // activeTextureUnits[textureunit-1] = textureid
+	std::map<std::string, GLint> texUnitPool; // texUnitPool[name] = textureunit
+	std::vector<GLuint> activeTexUnits; // activeTexUnits[textureunit-1] = textureid
 
 	// Uniform name to retainable objects
 	std::map<std::string, Object*> boundRetainables;
 
+	// Pointer to the active Canvas when the screen params were last checked.
+	Canvas *lastCanvas;
+
 	// Max GPU texture units available for sent images
-	static GLint maxTextureUnits;
+	static GLint maxTexUnits;
 
 	// Counts total number of textures bound to each texture unit in all shaders
 	static std::vector<int> textureCounters;
 
 	static StringMap<ShaderType, TYPE_MAX_ENUM>::Entry typeNameEntries[];
 	static StringMap<ShaderType, TYPE_MAX_ENUM> typeNames;
+
+	// Names for the built-in uniform variables.
+	static StringMap<BuiltinExtern, BUILTIN_MAX_ENUM>::Entry builtinNameEntries[];
+	static StringMap<BuiltinExtern, BUILTIN_MAX_ENUM> builtinNames;
 };
 
 } // opengl

+ 15 - 14
src/modules/graphics/opengl/SpriteBatch.cpp

@@ -25,8 +25,8 @@
 #include "OpenGL.h"
 
 // LOVE
-#include "Image.h"
 #include "VertexBuffer.h"
+#include "Texture.h"
 
 // C++
 #include <algorithm>
@@ -41,8 +41,8 @@ namespace graphics
 namespace opengl
 {
 
-SpriteBatch::SpriteBatch(Image *image, int size, int usage)
-	: image(image)
+SpriteBatch::SpriteBatch(Texture *texture, int size, int usage)
+	: texture(texture)
 	, size(size)
 	, next(0)
 	, color(0)
@@ -87,12 +87,12 @@ SpriteBatch::SpriteBatch(Image *image, int size, int usage)
 		throw love::Exception("Out of memory.");
 	}
 
-	image->retain();
+	texture->retain();
 }
 
 SpriteBatch::~SpriteBatch()
 {
-	image->release();
+	texture->release();
 
 	delete color;
 	delete array_buf;
@@ -106,7 +106,7 @@ int SpriteBatch::add(float x, float y, float a, float sx, float sy, float ox, fl
 		return -1;
 
 	// Needed for colors.
-	memcpy(sprite, image->getVertices(), sizeof(Vertex)*4);
+	memcpy(sprite, texture->getVertices(), sizeof(Vertex) * 4);
 
 	// Transform.
 	static Matrix t;
@@ -170,17 +170,17 @@ void SpriteBatch::unlock()
 	array_buf->unmap();
 }
 
-void SpriteBatch::setImage(Image *newimage)
+void SpriteBatch::setTexture(Texture *newtexture)
 {
-	Object::AutoRelease imagerelease(image);
+	Object::AutoRelease imagerelease(texture);
 
-	newimage->retain();
-	image = newimage;
+	newtexture->retain();
+	texture = newtexture;
 }
 
-Image *SpriteBatch::getImage()
+Texture *SpriteBatch::getTexture()
 {
-	return image;
+	return texture;
 }
 
 void SpriteBatch::setColor(const Color &color)
@@ -279,7 +279,7 @@ void SpriteBatch::draw(float x, float y, float angle, float sx, float sy, float
 	t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
 	glMultMatrixf((const GLfloat *)t.getElements());
 
-	image->predraw();
+	texture->predraw();
 
 	VertexBuffer::Bind array_bind(*array_buf);
 	VertexBuffer::Bind element_bind(*element_buf->getVertexBuffer());
@@ -299,6 +299,7 @@ void SpriteBatch::draw(float x, float y, float angle, float sx, float sy, float
 	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
 	glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), array_buf->getPointer(texel_offset));
 
+	gl.prepareDraw();
 	glDrawElements(GL_TRIANGLES, element_buf->getIndexCount(next), element_buf->getType(), element_buf->getPointer(0));
 
 	glDisableClientState(GL_VERTEX_ARRAY);
@@ -310,7 +311,7 @@ void SpriteBatch::draw(float x, float y, float angle, float sx, float sy, float
 		gl.setColor(curcolor);
 	}
 
-	image->postdraw();
+	texture->postdraw();
 
 	glPopMatrix();
 }

+ 5 - 5
src/modules/graphics/opengl/SpriteBatch.h

@@ -42,7 +42,7 @@ namespace opengl
 {
 
 // Forward declarations.
-class Image;
+class Texture;
 class VertexBuffer;
 class VertexIndex;
 
@@ -58,7 +58,7 @@ public:
 		USAGE_MAX_ENUM
 	};
 
-	SpriteBatch(Image *image, int size, int usage);
+	SpriteBatch(Texture *texture, int size, int usage);
 	virtual ~SpriteBatch();
 
 	int add(float x, float y, float a, float sx, float sy, float ox, float oy, float kx, float ky, int index = -1);
@@ -68,8 +68,8 @@ public:
 	void *lock();
 	void unlock();
 
-	void setImage(Image *newimage);
-	Image *getImage();
+	void setTexture(Texture *newtexture);
+	Texture *getTexture();
 
 	/**
 	 * Set the current color for this SpriteBatch. The sprites added
@@ -127,7 +127,7 @@ private:
 	 */
 	void setColorv(Vertex *v, const Color &color);
 
-	Image *image;
+	Texture *texture;
 
 	// Max number of sprites in the batch.
 	int size;

+ 35 - 3
src/modules/graphics/Drawable.cpp → src/modules/graphics/opengl/Texture.h

@@ -18,16 +18,48 @@
  * 3. This notice may not be removed or altered from any source distribution.
  **/
 
-#include "Drawable.h"
+#ifndef LOVE_GRAPHICS_OPENGL_TEXTURE_H
+#define LOVE_GRAPHICS_OPENGL_TEXTURE_H
+
+// LOVE
+#include "graphics/Texture.h"
+#include "graphics/Volatile.h"
+#include "OpenGL.h"
 
 namespace love
 {
 namespace graphics
 {
+namespace opengl
+{
 
-Drawable::~Drawable()
+class Texture : public love::graphics::Texture, public Volatile
 {
-}
+public:
+
+	virtual ~Texture() {}
+
+	/**
+	 * Gets the OpenGL id for this texture.
+	 **/
+	virtual GLuint getGLTexture() const = 0;
+
+	/**
+	 * Any setup the texture might need to do before drawing, e.g. binding
+	 * the OpenGL texture for use.
+	 **/
+	virtual void predraw() const {}
 
+	/**
+	 * Any cleanup the texture might need to do directly after drawing.
+	 **/
+	virtual void postdraw() const {}
+
+
+}; // Texture
+
+} // opengl
 } // graphics
 } // love
+
+#endif // LOVE_GRAPHICS_OPENGL_TEXTURE_H

+ 12 - 13
src/modules/graphics/opengl/wrap_Canvas.cpp

@@ -18,7 +18,6 @@
  * 3. This notice may not be removed or altered from any source distribution.
  **/
 
-#include "Graphics.h"
 #include "wrap_Canvas.h"
 
 namespace love
@@ -84,14 +83,14 @@ int w_Canvas_setFilter(lua_State *L)
 {
 	Canvas *canvas = luax_checkcanvas(L, 1);
 
-	Image::Filter f;
+	Texture::Filter f;
 
 	const char *minstr = luaL_checkstring(L, 2);
 	const char *magstr = luaL_optstring(L, 3, minstr);
 
-	if (!Image::getConstant(minstr, f.min))
+	if (!Texture::getConstant(minstr, f.min))
 		return luaL_error(L, "Invalid filter mode: %s", minstr);
-	if (!Image::getConstant(magstr, f.mag))
+	if (!Texture::getConstant(magstr, f.mag))
 		return luaL_error(L, "Invalid filter mode: %s", magstr);
 
 	f.anisotropy = (float) luaL_optnumber(L, 4, 1.0);
@@ -105,12 +104,12 @@ int w_Canvas_setFilter(lua_State *L)
 int w_Canvas_getFilter(lua_State *L)
 {
 	Canvas *canvas = luax_checkcanvas(L, 1);
-	const Image::Filter f = canvas->getFilter();
+	const Texture::Filter f = canvas->getFilter();
 
 	const char *minstr;
 	const char *magstr;
-	Image::getConstant(f.min, minstr);
-	Image::getConstant(f.mag, magstr);
+	Texture::getConstant(f.min, minstr);
+	Texture::getConstant(f.mag, magstr);
 
 	lua_pushstring(L, minstr);
 	lua_pushstring(L, magstr);
@@ -123,14 +122,14 @@ int w_Canvas_setWrap(lua_State *L)
 {
 	Canvas *canvas = luax_checkcanvas(L, 1);
 
-	Image::Wrap w;
+	Texture::Wrap w;
 
 	const char *sstr = luaL_checkstring(L, 2);
 	const char *tstr = luaL_optstring(L, 3, sstr);
 
-	if (!Image::getConstant(sstr, w.s))
+	if (!Texture::getConstant(sstr, w.s))
 		return luaL_error(L, "Invalid wrap mode: %s", sstr);
-	if (!Image::getConstant(tstr, w.t))
+	if (!Texture::getConstant(tstr, w.t))
 		return luaL_error(L, "Invalid wrap mode, %s", tstr);
 
 	canvas->setWrap(w);
@@ -141,12 +140,12 @@ int w_Canvas_setWrap(lua_State *L)
 int w_Canvas_getWrap(lua_State *L)
 {
 	Canvas *canvas = luax_checkcanvas(L, 1);
-	const Image::Wrap w = canvas->getWrap();
+	const Texture::Wrap w = canvas->getWrap();
 
 	const char *wrap_s;
 	const char *wrap_t;
-	Image::getConstant(w.s, wrap_s);
-	Image::getConstant(w.t, wrap_t);
+	Texture::getConstant(w.s, wrap_s);
+	Texture::getConstant(w.t, wrap_t);
 
 	lua_pushstring(L, wrap_s);
 	lua_pushstring(L, wrap_t);

+ 6 - 6
src/modules/graphics/opengl/wrap_Font.cpp

@@ -84,14 +84,14 @@ int w_Font_getLineHeight(lua_State *L)
 int w_Font_setFilter(lua_State *L)
 {
 	Font *t = luax_checkfont(L, 1);
-	Image::Filter f = t->getFilter();
+	Texture::Filter f = t->getFilter();
 
 	const char *minstr = luaL_checkstring(L, 2);
 	const char *magstr = luaL_optstring(L, 3, minstr);
 
-	if (!Image::getConstant(minstr, f.min))
+	if (!Texture::getConstant(minstr, f.min))
 		return luaL_error(L, "Invalid filter mode: %s", minstr);
-	if (!Image::getConstant(magstr, f.mag))
+	if (!Texture::getConstant(magstr, f.mag))
 		return luaL_error(L, "Invalid filter mode: %s", magstr);
 
 	f.anisotropy = (float) luaL_optnumber(L, 4, 1.0);
@@ -103,11 +103,11 @@ int w_Font_setFilter(lua_State *L)
 int w_Font_getFilter(lua_State *L)
 {
 	Font *t = luax_checkfont(L, 1);
-	const Image::Filter f = t->getFilter();
+	const Texture::Filter f = t->getFilter();
 	const char *minstr;
 	const char *magstr;
-	Image::getConstant(f.min, minstr);
-	Image::getConstant(f.mag, magstr);
+	Texture::getConstant(f.min, minstr);
+	Texture::getConstant(f.mag, magstr);
 	lua_pushstring(L, minstr);
 	lua_pushstring(L, magstr);
 	lua_pushnumber(L, f.anisotropy);

+ 32 - 32
src/modules/graphics/opengl/wrap_Graphics.cpp

@@ -20,7 +20,7 @@
 
 #include "wrap_Graphics.h"
 #include "OpenGL.h"
-#include "graphics/DrawQable.h"
+#include "graphics/Texture.h"
 #include "image/ImageData.h"
 #include "font/Rasterizer.h"
 
@@ -34,7 +34,7 @@ namespace graphics
 namespace opengl
 {
 
-static Graphics *instance = 0;
+static Graphics *instance = nullptr;
 
 int w_reset(lua_State *)
 {
@@ -237,7 +237,7 @@ int w_newFont(lua_State *L)
 int w_newImageFont(lua_State *L)
 {
 	// filter for glyphs
-	Image::Filter filter = instance->getDefaultFilter();
+	Texture::Filter filter = instance->getDefaultFilter();
 
 	// Convert to ImageData if necessary.
 	if (lua_isstring(L, 1) || luax_istype(L, 1, FILESYSTEM_FILE_T) || luax_istype(L, 1, FILESYSTEM_FILE_DATA_T))
@@ -277,7 +277,7 @@ int w_newImageFont(lua_State *L)
 
 int w_newSpriteBatch(lua_State *L)
 {
-	Image *image = luax_checkimage(L, 1);
+	Texture *texture = luax_checktype<Texture>(L, 1, "Texture", GRAPHICS_TEXTURE_T);
 	int size = luaL_optint(L, 2, 1000);
 	SpriteBatch::UsageHint usage = SpriteBatch::USAGE_DYNAMIC;
 	if (lua_gettop(L) > 2)
@@ -287,8 +287,8 @@ int w_newSpriteBatch(lua_State *L)
 			return luaL_error(L, "Invalid SpriteBatch usage hint: %s", usagestr);
 	}
 
-	SpriteBatch *t = 0;
-	EXCEPT_GUARD(t = instance->newSpriteBatch(image, size, usage);)
+	SpriteBatch *t = nullptr;
+	EXCEPT_GUARD(t = instance->newSpriteBatch(texture, size, usage);)
 
 	luax_pushtype(L, "SpriteBatch", GRAPHICS_SPRITE_BATCH_T, t);
 	return 1;
@@ -296,13 +296,13 @@ int w_newSpriteBatch(lua_State *L)
 
 int w_newParticleSystem(lua_State *L)
 {
-	Image *image = luax_checkimage(L, 1);
+	Texture *texture = luax_checktype<Texture>(L, 1, "Texture", GRAPHICS_TEXTURE_T);
 	lua_Number size = luaL_optnumber(L, 2, 1000);
 	ParticleSystem *t = 0;
 	if (size < 1.0 || size > ParticleSystem::MAX_PARTICLES)
 		return luaL_error(L, "Invalid ParticleSystem size");	
 
-	EXCEPT_GUARD(t = instance->newParticleSystem(image, int(size));)
+	EXCEPT_GUARD(t = instance->newParticleSystem(texture, int(size));)
 
 	luax_pushtype(L, "ParticleSystem", GRAPHICS_PARTICLE_SYSTEM_T, t);
 	return 1;
@@ -434,10 +434,10 @@ int w_newMesh(lua_State *L)
 	// Check first argument: mandatory table of vertices.
 	luaL_checktype(L, 1, LUA_TTABLE);
 
-	// Second argument: optional image.
-	Image *img = 0;
+	// Second argument: optional texture.
+	Texture *tex = nullptr;
 	if (!lua_isnoneornil(L, 2))
-		img = luax_checkimage(L, 2);
+		tex = luax_checktype<Texture>(L, 2, "Texture", GRAPHICS_TEXTURE_T);
 
 	// Third argument: optional draw mode.
 	const char *str = 0;
@@ -485,11 +485,11 @@ int w_newMesh(lua_State *L)
 		vertices.push_back(v);
 	}
 
-	Mesh *t = 0;
+	Mesh *t = nullptr;
 	EXCEPT_GUARD(t = instance->newMesh(vertices, mode);)
 
-	if (img)
-		t->setImage(img);
+	if (tex)
+		t->setTexture(tex);
 
 	t->setVertexColors(use_colors);
 
@@ -646,20 +646,20 @@ int w_getBlendMode(lua_State *L)
 
 int w_setDefaultFilter(lua_State *L)
 {
-	Image::FilterMode min;
-	Image::FilterMode mag;
+	Texture::FilterMode min;
+	Texture::FilterMode mag;
 
 	const char *minstr = luaL_checkstring(L, 1);
 	const char *magstr = luaL_optstring(L, 2, minstr);
 
-	if (!Image::getConstant(minstr, min))
+	if (!Texture::getConstant(minstr, min))
 		return luaL_error(L, "Invalid filter mode: %s", minstr);
-	if (!Image::getConstant(magstr, mag))
+	if (!Texture::getConstant(magstr, mag))
 		return luaL_error(L, "Invalid filter mode: %s", magstr);
 
 	float anisotropy = (float) luaL_optnumber(L, 3, 1.0);
 
-	Image::Filter f;
+	Texture::Filter f;
 	f.min = min;
 	f.mag = mag;
 	f.anisotropy = anisotropy;
@@ -671,12 +671,12 @@ int w_setDefaultFilter(lua_State *L)
 
 int w_getDefaultFilter(lua_State *L)
 {
-	const Image::Filter &f = instance->getDefaultFilter();
+	const Texture::Filter &f = instance->getDefaultFilter();
 	const char *minstr;
 	const char *magstr;
-	if (!Image::getConstant(f.min, minstr))
+	if (!Texture::getConstant(f.min, minstr))
 		return luaL_error(L, "Unknown minification filter mode");
-	if (!Image::getConstant(f.mag, magstr))
+	if (!Texture::getConstant(f.mag, magstr))
 		return luaL_error(L, "Unknown magnification filter mode");
 	lua_pushstring(L, minstr);
 	lua_pushstring(L, magstr);
@@ -686,11 +686,11 @@ int w_getDefaultFilter(lua_State *L)
 
 int w_setDefaultMipmapFilter(lua_State *L)
 {
-	Image::FilterMode filter = Image::FILTER_NONE;
+	Texture::FilterMode filter = Texture::FILTER_NONE;
 	if (!lua_isnoneornil(L, 1))
 	{
 		const char *str = luaL_checkstring(L, 1);
-		if (!Image::getConstant(str, filter))
+		if (!Texture::getConstant(str, filter))
 			return luaL_error(L, "Invalid filter mode: %s", str);
 	}
 
@@ -703,13 +703,13 @@ int w_setDefaultMipmapFilter(lua_State *L)
 
 int w_getDefaultMipmapFilter(lua_State *L)
 {
-	Image::FilterMode filter;
+	Texture::FilterMode filter;
 	float sharpness;
 
 	instance->getDefaultMipmapFilter(&filter, &sharpness);
 
 	const char *str;
-	if (Image::getConstant(filter, str))
+	if (Texture::getConstant(filter, str))
 		lua_pushstring(L, str);
 	else
 		lua_pushnil(L);
@@ -1004,14 +1004,14 @@ int w_getRendererInfo(lua_State *L)
 
 int w_draw(lua_State *L)
 {
-	Drawable *drawable = 0;
-	DrawQable *drawqable = 0;
-	Quad *quad = 0;
+	Drawable *drawable = nullptr;
+	Texture *texture = nullptr;
+	Quad *quad = nullptr;
 	int startidx = 2;
 
 	if (luax_istype(L, 2, GRAPHICS_QUAD_T))
 	{
-		drawqable = luax_checktype<DrawQable>(L, 1, "DrawQable", GRAPHICS_DRAWQABLE_T);
+		texture = luax_checktype<Texture>(L, 1, "Texture", GRAPHICS_TEXTURE_T);
 		quad = luax_totype<Quad>(L, 2, "Quad", GRAPHICS_QUAD_T);
 		startidx = 3;
 	}
@@ -1035,8 +1035,8 @@ int w_draw(lua_State *L)
 	float kx = (float) luaL_optnumber(L, startidx + 7, 0.0);
 	float ky = (float) luaL_optnumber(L, startidx + 8, 0.0);
 
-	if (drawqable && quad)
-		drawqable->drawq(quad, x, y, a, sx, sy, ox, oy, kx, ky);
+	if (texture && quad)
+		texture->drawq(quad, x, y, a, sx, sy, ox, oy, kx, ky);
 	else if (drawable)
 		drawable->draw(x, y, a, sx, sy, ox, oy, kx, ky);
 

+ 17 - 18
src/modules/graphics/opengl/wrap_Image.cpp

@@ -58,14 +58,14 @@ int w_Image_getDimensions(lua_State *L)
 int w_Image_setFilter(lua_State *L)
 {
 	Image *t = luax_checkimage(L, 1);
-	Image::Filter f = t->getFilter();
+	Texture::Filter f = t->getFilter();
 
 	const char *minstr = luaL_checkstring(L, 2);
 	const char *magstr = luaL_optstring(L, 3, minstr);
 
-	if (!Image::getConstant(minstr, f.min))
+	if (!Texture::getConstant(minstr, f.min))
 		return luaL_error(L, "Invalid filter mode: %s", minstr);
-	if (!Image::getConstant(magstr, f.mag))
+	if (!Texture::getConstant(magstr, f.mag))
 		return luaL_error(L, "Invalid filter mode: %s", magstr);
 
 	f.anisotropy = (float) luaL_optnumber(L, 4, 1.0);
@@ -77,11 +77,11 @@ int w_Image_setFilter(lua_State *L)
 int w_Image_getFilter(lua_State *L)
 {
 	Image *t = luax_checkimage(L, 1);
-	const Image::Filter f = t->getFilter();
+	const Texture::Filter f = t->getFilter();
 	const char *minstr;
 	const char *magstr;
-	Image::getConstant(f.min, minstr);
-	Image::getConstant(f.mag, magstr);
+	Texture::getConstant(f.min, minstr);
+	Texture::getConstant(f.mag, magstr);
 	lua_pushstring(L, minstr);
 	lua_pushstring(L, magstr);
 	lua_pushnumber(L, f.anisotropy);
@@ -91,14 +91,14 @@ int w_Image_getFilter(lua_State *L)
 int w_Image_setMipmapFilter(lua_State *L)
 {
 	Image *t = luax_checkimage(L, 1);
-	Image::Filter f = t->getFilter();
+	Texture::Filter f = t->getFilter();
 
 	if (lua_isnoneornil(L, 2))
-		f.mipmap = Image::FILTER_NONE; // mipmapping is disabled if no argument is given
+		f.mipmap = Texture::FILTER_NONE; // mipmapping is disabled if no argument is given
 	else
 	{
 		const char *mipmapstr = luaL_checkstring(L, 2);
-		if (!Image::getConstant(mipmapstr, f.mipmap))
+		if (!Texture::getConstant(mipmapstr, f.mipmap))
 			return luaL_error(L, "Invalid filter mode: %s", mipmapstr);
 	}
 
@@ -114,10 +114,10 @@ int w_Image_getMipmapFilter(lua_State *L)
 {
 	Image *t = luax_checkimage(L, 1);
 
-	const Image::Filter &f = t->getFilter();
+	const Texture::Filter &f = t->getFilter();
 
 	const char *mipmapstr;
-	if (Image::getConstant(f.mipmap, mipmapstr))
+	if (Texture::getConstant(f.mipmap, mipmapstr))
 		lua_pushstring(L, mipmapstr);
 	else
 		lua_pushnil(L); // only return a mipmap filter if mipmapping is enabled
@@ -130,29 +130,28 @@ int w_Image_setWrap(lua_State *L)
 {
 	Image *i = luax_checkimage(L, 1);
 
-	Image::Wrap w;
+	Texture::Wrap w;
 
 	const char *sstr = luaL_checkstring(L, 2);
 	const char *tstr = luaL_optstring(L, 3, sstr);
 
-	if (!Image::getConstant(sstr, w.s))
+	if (!Texture::getConstant(sstr, w.s))
 		return luaL_error(L, "Invalid wrap mode: %s", sstr);
-	if (!Image::getConstant(tstr, w.t))
+	if (!Texture::getConstant(tstr, w.t))
 		return luaL_error(L, "Invalid wrap mode, %s", tstr);
 
 	i->setWrap(w);
-
 	return 0;
 }
 
 int w_Image_getWrap(lua_State *L)
 {
 	Image *i = luax_checkimage(L, 1);
-	const Image::Wrap w = i->getWrap();
+	const Texture::Wrap w = i->getWrap();
 	const char *sstr;
 	const char *tstr;
-	Image::getConstant(w.s, sstr);
-	Image::getConstant(w.t, tstr);
+	Texture::getConstant(w.s, sstr);
+	Texture::getConstant(w.t, tstr);
 	lua_pushstring(L, sstr);
 	lua_pushstring(L, tstr);
 	return 2;

+ 32 - 12
src/modules/graphics/opengl/wrap_Mesh.cpp

@@ -20,7 +20,11 @@
 
 // LOVE
 #include "wrap_Mesh.h"
-#include "wrap_Image.h"
+#include "Image.h"
+#include "Canvas.h"
+
+// C++
+#include <typeinfo>
 
 namespace love
 {
@@ -231,31 +235,42 @@ int w_Mesh_getVertexMap(lua_State *L)
 	return 1;
 }
 
-int w_Mesh_setImage(lua_State *L)
+int w_Mesh_setTexture(lua_State *L)
 {
 	Mesh *t = luax_checkmesh(L, 1);
 
 	if (lua_isnoneornil(L, 2))
-		t->setImage();
+		t->setTexture();
 	else
 	{
-		Image *img = luax_checkimage(L, 2);
-		t->setImage(img);
+		Texture *tex = luax_checktype<Texture>(L, 2, "Texture", GRAPHICS_TEXTURE_T);
+		t->setTexture(tex);
 	}
 
 	return 0;
 }
 
-int w_Mesh_getImage(lua_State *L)
+int w_Mesh_getTexture(lua_State *L)
 {
 	Mesh *t = luax_checkmesh(L, 1);
-	Image *img = t->getImage();
+	Texture *tex = t->getTexture();
 
-	if (img == NULL)
+	if (tex == nullptr)
 		return 0;
 
-	img->retain();
-	luax_pushtype(L, "Image", GRAPHICS_IMAGE_T, img);
+	tex->retain();
+
+	// FIXME: big hack right here.
+	if (typeid(*tex) == typeid(Image))
+		luax_pushtype(L, "Image", GRAPHICS_IMAGE_T, tex);
+	else if (typeid(*tex) == typeid(Canvas))
+		luax_pushtype(L, "Canvas", GRAPHICS_CANVAS_T, tex);
+	else
+	{
+		tex->release();
+		return luaL_error(L, "Unable to determine texture type.");
+	}
+
 	return 1;
 }
 
@@ -322,14 +337,19 @@ static const luaL_Reg functions[] =
 	{ "getVertexCount", w_Mesh_getVertexCount },
 	{ "setVertexMap", w_Mesh_setVertexMap },
 	{ "getVertexMap", w_Mesh_getVertexMap },
-	{ "setImage", w_Mesh_setImage },
-	{ "getImage", w_Mesh_getImage },
+	{ "setTexture", w_Mesh_setTexture },
+	{ "getTexture", w_Mesh_getTexture },
 	{ "setDrawMode", w_Mesh_setDrawMode },
 	{ "getDrawMode", w_Mesh_getDrawMode },
 	{ "setVertexColors", w_Mesh_setVertexColors },
 	{ "hasVertexColors", w_Mesh_hasVertexColors },
 	{ "setWireframe", w_Mesh_setWireframe },
 	{ "isWireframe", w_Mesh_isWireframe },
+
+	// Deprecated since 0.9.1.
+	{ "setImage", w_Mesh_setTexture },
+	{ "getImage", w_Mesh_getTexture },
+
 	{ 0, 0 }
 };
 

+ 2 - 2
src/modules/graphics/opengl/wrap_Mesh.h

@@ -41,8 +41,8 @@ int w_Mesh_getVertices(lua_State *L);
 int w_Mesh_getVertexCount(lua_State *L);
 int w_Mesh_setVertexMap(lua_State *L);
 int w_Mesh_getVertexMap(lua_State *L);
-int w_Mesh_setImage(lua_State *L);
-int w_Mesh_getImage(lua_State *L);
+int w_Mesh_setTexture(lua_State *L);
+int w_Mesh_getTexture(lua_State *L);
 int w_Mesh_setDrawMode(lua_State *L);
 int w_Mesh_getDrawMode(lua_State *L);
 int w_Mesh_setVertexColors(lua_State *L);

+ 33 - 10
src/modules/graphics/opengl/wrap_ParticleSystem.cpp

@@ -18,12 +18,19 @@
  * 3. This notice may not be removed or altered from any source distribution.
  **/
 
+// LOVE
 #include "wrap_ParticleSystem.h"
-
 #include "common/Vector.h"
 
+#include "Image.h"
+#include "Canvas.h"
+
+// C
 #include <cstring>
 
+// C++
+#include <typeinfo>
+
 namespace love
 {
 namespace graphics
@@ -47,20 +54,31 @@ int w_ParticleSystem_clone(lua_State *L)
 	return 1;
 }
 
-int w_ParticleSystem_setImage(lua_State *L)
+int w_ParticleSystem_setTexture(lua_State *L)
 {
 	ParticleSystem *t = luax_checkparticlesystem(L, 1);
-	Image *i = luax_checkimage(L, 2);
-	t->setImage(i);
+	Texture *tex = luax_checktype<Texture>(L, 2, "Texture", GRAPHICS_TEXTURE_T);
+	t->setTexture(tex);
 	return 0;
 }
 
-int w_ParticleSystem_getImage(lua_State *L)
+int w_ParticleSystem_getTexture(lua_State *L)
 {
 	ParticleSystem *t = luax_checkparticlesystem(L, 1);
-	Image *i = t->getImage();
-	i->retain();
-	luax_pushtype(L, "Image", GRAPHICS_IMAGE_T, i);
+	Texture *tex = t->getTexture();
+	tex->retain();
+
+	// FIXME: big hack right here.
+	if (typeid(*tex) == typeid(Image))
+		luax_pushtype(L, "Image", GRAPHICS_IMAGE_T, tex);
+	else if (typeid(*tex) == typeid(Canvas))
+		luax_pushtype(L, "Canvas", GRAPHICS_CANVAS_T, tex);
+	else
+	{
+		tex->release();
+		return luaL_error(L, "Unable to determine texture type.");
+	}
+
 	return 1;
 }
 
@@ -616,8 +634,8 @@ int w_ParticleSystem_update(lua_State *L)
 static const luaL_Reg functions[] =
 {
 	{ "clone", w_ParticleSystem_clone },
-	{ "setImage", w_ParticleSystem_setImage },
-	{ "getImage", w_ParticleSystem_getImage },
+	{ "setTexture", w_ParticleSystem_setTexture },
+	{ "getTexture", w_ParticleSystem_getTexture },
 	{ "setBufferSize", w_ParticleSystem_setBufferSize },
 	{ "getBufferSize", w_ParticleSystem_getBufferSize },
 	{ "setInsertMode", w_ParticleSystem_setInsertMode },
@@ -668,6 +686,11 @@ static const luaL_Reg functions[] =
 	{ "isPaused", w_ParticleSystem_isPaused },
 	{ "isStopped", w_ParticleSystem_isStopped },
 	{ "update", w_ParticleSystem_update },
+
+	// Deprecated since 0.9.1.
+	{ "setImage", w_ParticleSystem_setTexture },
+	{ "getImage", w_ParticleSystem_getTexture },
+
 	{ 0, 0 }
 };
 

+ 2 - 3
src/modules/graphics/opengl/wrap_ParticleSystem.h

@@ -23,7 +23,6 @@
 
 // LOVE
 #include "common/runtime.h"
-#include "wrap_Image.h"
 #include "ParticleSystem.h"
 
 namespace love
@@ -35,8 +34,8 @@ namespace opengl
 
 ParticleSystem *luax_checkparticlesystem(lua_State *L, int idx);
 int w_ParticleSystem_clone(lua_State *L);
-int w_ParticleSystem_setImage(lua_State *L);
-int w_ParticleSystem_getImage(lua_State *L);
+int w_ParticleSystem_setTexture(lua_State *L);
+int w_ParticleSystem_getTexture(lua_State *L);
 int w_ParticleSystem_setBufferSize(lua_State *L);
 int w_ParticleSystem_getBufferSize(lua_State *L);
 int w_ParticleSystem_setInsertMode(lua_State *L);

+ 13 - 21
src/modules/graphics/opengl/wrap_Shader.cpp

@@ -253,23 +253,13 @@ int w_Shader_sendMatrix(lua_State *L)
 	return 0;
 }
 
-int w_Shader_sendImage(lua_State *L)
+int w_Shader_sendTexture(lua_State *L)
 {
 	Shader *shader = luax_checkshader(L, 1);
 	const char *name = luaL_checkstring(L, 2);
-	Image *img = luax_checkimage(L, 3);
+	Texture *texture = luax_checktype<Texture>(L, 3, "Texture", GRAPHICS_TEXTURE_T);
 
-	EXCEPT_GUARD(shader->sendImage(name, *img);)
-	return 0;
-}
-
-int w_Shader_sendCanvas(lua_State *L)
-{
-	Shader *shader = luax_checkshader(L, 1);
-	const char *name = luaL_checkstring(L, 2);
-	Canvas *canvas = luax_checkcanvas(L, 3);
-
-	EXCEPT_GUARD(shader->sendCanvas(name, *canvas);)
+	EXCEPT_GUARD(shader->sendTexture(name, texture);)
 	return 0;
 }
 
@@ -318,7 +308,7 @@ static void w_convertMatrices(lua_State *L, int idx)
 int w_Shader_send(lua_State *L)
 {
 	int ttype = lua_type(L, 3);
-	Proxy *p = 0;
+	Proxy *p = nullptr;
 
 	switch (ttype)
 	{
@@ -328,13 +318,11 @@ int w_Shader_send(lua_State *L)
 		return w_Shader_sendFloat(L);
 		break;
 	case LUA_TUSERDATA:
-		// Image or Canvas.
+		// Texture (Image or Canvas).
 		p = (Proxy *) lua_touserdata(L, 3);
 
-		if (p->flags[GRAPHICS_IMAGE_ID])
-			return w_Shader_sendImage(L);
-		else if (p->flags[GRAPHICS_CANVAS_ID])
-			return w_Shader_sendCanvas(L);
+		if (p->flags[GRAPHICS_TEXTURE_ID])
+			return w_Shader_sendTexture(L);
 
 		break;
 	case LUA_TTABLE:
@@ -366,9 +354,13 @@ static const luaL_Reg functions[] =
 	{ "sendBoolean", w_Shader_sendInt },
 	{ "sendFloat",   w_Shader_sendFloat },
 	{ "sendMatrix",  w_Shader_sendMatrix },
-	{ "sendImage",   w_Shader_sendImage },
-	{ "sendCanvas",  w_Shader_sendCanvas },
+	{ "sendTexture", w_Shader_sendTexture },
 	{ "send",        w_Shader_send },
+
+	// Deprecated since 0.9.1.
+	{ "sendImage",   w_Shader_sendTexture },
+	{ "sendCanvas",  w_Shader_sendTexture },
+
 	{ 0, 0 }
 };
 

+ 1 - 2
src/modules/graphics/opengl/wrap_Shader.h

@@ -36,8 +36,7 @@ int w_Shader_getWarnings(lua_State *L);
 int w_Shader_sendInt(lua_State *L);
 int w_Shader_sendFloat(lua_State *L);
 int w_Shader_sendMatrix(lua_State *L);
-int w_Shader_sendImage(lua_State *L);
-int w_Shader_sendCanvas(lua_State *L);
+int w_Shader_sendTexture(lua_State *L);
 int w_Shader_send(lua_State *L);
 extern "C" int luaopen_shader(lua_State *L);
 

+ 32 - 12
src/modules/graphics/opengl/wrap_SpriteBatch.cpp

@@ -18,8 +18,13 @@
  * 3. This notice may not be removed or altered from any source distribution.
  **/
 
-#include "Image.h"
+// LOVE
 #include "wrap_SpriteBatch.h"
+#include "Image.h"
+#include "Canvas.h"
+
+// C++
+#include <typeinfo>
 
 namespace love
 {
@@ -36,7 +41,7 @@ SpriteBatch *luax_checkspritebatch(lua_State *L, int idx)
 int w_SpriteBatch_add(lua_State *L)
 {
 	SpriteBatch *t = luax_checkspritebatch(L, 1);
-	Quad *quad = 0;
+	Quad *quad = nullptr;
 	int startidx = 2;
 
 	if (luax_istype(L, 2, GRAPHICS_QUAD_T))
@@ -74,7 +79,7 @@ int w_SpriteBatch_set(lua_State *L)
 	SpriteBatch *t = luax_checkspritebatch(L, 1);
 	int id = luaL_checkinteger(L, 2);
 
-	Quad *quad = 0;
+	Quad *quad = nullptr;
 	int startidx = 3;
 
 	if (luax_istype(L, 3, GRAPHICS_QUAD_T))
@@ -126,20 +131,31 @@ int w_SpriteBatch_unbind(lua_State *L)
 	return 0;
 }
 
-int w_SpriteBatch_setImage(lua_State *L)
+int w_SpriteBatch_setTexture(lua_State *L)
 {
 	SpriteBatch *t = luax_checkspritebatch(L, 1);
-	Image *image = luax_checktype<Image>(L, 2, "Image", GRAPHICS_IMAGE_T);
-	t->setImage(image);
+	Texture *tex = luax_checktype<Texture>(L, 2, "Texture", GRAPHICS_TEXTURE_T);
+	t->setTexture(tex);
 	return 0;
 }
 
-int w_SpriteBatch_getImage(lua_State *L)
+int w_SpriteBatch_getTexture(lua_State *L)
 {
 	SpriteBatch *t = luax_checkspritebatch(L, 1);
-	Image *image = t->getImage();
-	image->retain();
-	luax_pushtype(L, "Image", GRAPHICS_IMAGE_T, image);
+	Texture *tex = t->getTexture();
+	tex->retain();
+
+	// FIXME: big hack right here.
+	if (typeid(*tex) == typeid(Image))
+		luax_pushtype(L, "Image", GRAPHICS_IMAGE_T, tex);
+	else if (typeid(*tex) == typeid(Canvas))
+		luax_pushtype(L, "Canvas", GRAPHICS_CANVAS_T, tex);
+	else
+	{
+		tex->release();
+		return luaL_error(L, "Unable to determine texture type.");
+	}
+
 	return 1;
 }
 
@@ -224,13 +240,17 @@ static const luaL_Reg functions[] =
 	{ "clear", w_SpriteBatch_clear },
 	{ "bind", w_SpriteBatch_bind },
 	{ "unbind", w_SpriteBatch_unbind },
-	{ "setImage", w_SpriteBatch_setImage },
-	{ "getImage", w_SpriteBatch_getImage },
+	{ "setTexture", w_SpriteBatch_setTexture },
+	{ "getTexture", w_SpriteBatch_getTexture },
 	{ "setColor", w_SpriteBatch_setColor },
 	{ "getColor", w_SpriteBatch_getColor },
 	{ "getCount", w_SpriteBatch_getCount },
 	{ "setBufferSize", w_SpriteBatch_setBufferSize },
 	{ "getBufferSize", w_SpriteBatch_getBufferSize },
+
+	// Deprecated since 0.9.1.
+	{ "setImage", w_SpriteBatch_setTexture },
+	{ "getImage", w_SpriteBatch_getTexture },
 	{ 0, 0 }
 };
 

+ 2 - 2
src/modules/graphics/opengl/wrap_SpriteBatch.h

@@ -39,8 +39,8 @@ int w_SpriteBatch_setg(lua_State *L);
 int w_SpriteBatch_clear(lua_State *L);
 int w_SpriteBatch_bind(lua_State *L);
 int w_SpriteBatch_unbind(lua_State *L);
-int w_SpriteBatch_setImage(lua_State *L);
-int w_SpriteBatch_getImage(lua_State *L);
+int w_SpriteBatch_setTexture(lua_State *L);
+int w_SpriteBatch_getTexture(lua_State *L);
 int w_SpriteBatch_setColor(lua_State *L);
 int w_SpriteBatch_getColor(lua_State *L);
 int w_SpriteBatch_getCount(lua_State *L);

+ 12 - 3
src/scripts/graphics.lua

@@ -1303,7 +1303,8 @@ do
 #define ProjectionMatrix gl_ProjectionMatrix
 #define TransformProjectionMatrix gl_ModelViewProjectionMatrix
 #define NormalMatrix gl_NormalMatrix
-uniform sampler2D _tex0_;]]
+uniform sampler2D _tex0_;
+uniform vec2 love_ScreenParams;]]
 
 	local GLSL_VERTEX = {
 		HEADER = [[
@@ -1335,14 +1336,22 @@ void main() {
 void main() {
 	// fix crashing issue in OSX when _tex0_ is unused within effect()
 	float dummy = texture2D(_tex0_, vec2(.5)).r;
-	gl_FragColor = effect(VaryingColor, _tex0_, VaryingTexCoord.st, gl_FragCoord.xy);
+
+	// See Shader::checkSetScreenParams in Shader.cpp.
+	vec2 pixelcoord = vec2(gl_FragCoord.x, (gl_FragCoord.y * love_ScreenParams[0]) + love_ScreenParams[1]);
+
+	gl_FragColor = effect(VaryingColor, _tex0_, VaryingTexCoord.st, pixelcoord);
 }]],
 
 		FOOTER_MULTI_CANVAS = [[
 void main() {
 	// fix crashing issue in OSX when _tex0_ is unused within effect()
 	float dummy = texture2D(_tex0_, vec2(.5)).r;
-	effects(VaryingColor, _tex0_, VaryingTexCoord.st, gl_FragCoord.xy);
+
+	// See Shader::checkSetScreenParams in Shader.cpp.
+	vec2 pixelcoord = vec2(gl_FragCoord.x, (gl_FragCoord.y * love_ScreenParams[0]) + love_ScreenParams[1]);
+
+	effects(VaryingColor, _tex0_, VaryingTexCoord.st, pixelcoord);
 }]],
 	}
 

+ 25 - 5
src/scripts/graphics.lua.h

@@ -6294,7 +6294,9 @@ const unsigned char graphics_lua[] =
 	0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x4e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x4d, 0x61, 0x74, 0x72, 
 	0x69, 0x78, 0x20, 0x67, 0x6c, 0x5f, 0x4e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x0a,
 	0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x32, 0x44, 0x20, 
-	0x5f, 0x74, 0x65, 0x78, 0x30, 0x5f, 0x3b, 0x5d, 0x5d, 0x0a,
+	0x5f, 0x74, 0x65, 0x78, 0x30, 0x5f, 0x3b, 0x0a,
+	0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x76, 0x65, 0x63, 0x32, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x5f, 
+	0x53, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x3b, 0x5d, 0x5d, 0x0a,
 	0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x47, 0x4c, 0x53, 0x4c, 0x5f, 0x56, 0x45, 0x52, 0x54, 0x45, 0x58, 
 	0x20, 0x3d, 0x20, 0x7b, 0x0a,
 	0x09, 0x09, 0x48, 0x45, 0x41, 0x44, 0x45, 0x52, 0x20, 0x3d, 0x20, 0x5b, 0x5b, 0x0a,
@@ -6342,11 +6344,20 @@ const unsigned char graphics_lua[] =
 	0x09, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x64, 0x75, 0x6d, 0x6d, 0x79, 0x20, 0x3d, 0x20, 0x74, 0x65, 0x78, 
 	0x74, 0x75, 0x72, 0x65, 0x32, 0x44, 0x28, 0x5f, 0x74, 0x65, 0x78, 0x30, 0x5f, 0x2c, 0x20, 0x76, 0x65, 0x63, 
 	0x32, 0x28, 0x2e, 0x35, 0x29, 0x29, 0x2e, 0x72, 0x3b, 0x0a,
+	0x09, 0x2f, 0x2f, 0x20, 0x53, 0x65, 0x65, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x3a, 0x3a, 0x63, 0x68, 
+	0x65, 0x63, 0x6b, 0x53, 0x65, 0x74, 0x53, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 
+	0x20, 0x69, 0x6e, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x2e, 0x63, 0x70, 0x70, 0x2e, 0x0a,
+	0x09, 0x76, 0x65, 0x63, 0x32, 0x20, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x20, 0x3d, 
+	0x20, 0x76, 0x65, 0x63, 0x32, 0x28, 0x67, 0x6c, 0x5f, 0x46, 0x72, 0x61, 0x67, 0x43, 0x6f, 0x6f, 0x72, 0x64, 
+	0x2e, 0x78, 0x2c, 0x20, 0x28, 0x67, 0x6c, 0x5f, 0x46, 0x72, 0x61, 0x67, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x2e, 
+	0x79, 0x20, 0x2a, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x5f, 0x53, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x50, 0x61, 0x72, 
+	0x61, 0x6d, 0x73, 0x5b, 0x30, 0x5d, 0x29, 0x20, 0x2b, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x5f, 0x53, 0x63, 0x72, 
+	0x65, 0x65, 0x6e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x5b, 0x31, 0x5d, 0x29, 0x3b, 0x0a,
 	0x09, 0x67, 0x6c, 0x5f, 0x46, 0x72, 0x61, 0x67, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x65, 0x66, 
 	0x66, 0x65, 0x63, 0x74, 0x28, 0x56, 0x61, 0x72, 0x79, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x2c, 
 	0x20, 0x5f, 0x74, 0x65, 0x78, 0x30, 0x5f, 0x2c, 0x20, 0x56, 0x61, 0x72, 0x79, 0x69, 0x6e, 0x67, 0x54, 0x65, 
-	0x78, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x2e, 0x73, 0x74, 0x2c, 0x20, 0x67, 0x6c, 0x5f, 0x46, 0x72, 0x61, 0x67, 
-	0x43, 0x6f, 0x6f, 0x72, 0x64, 0x2e, 0x78, 0x79, 0x29, 0x3b, 0x0a,
+	0x78, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x2e, 0x73, 0x74, 0x2c, 0x20, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x63, 0x6f, 
+	0x6f, 0x72, 0x64, 0x29, 0x3b, 0x0a,
 	0x7d, 0x5d, 0x5d, 0x2c, 0x0a,
 	0x09, 0x09, 0x46, 0x4f, 0x4f, 0x54, 0x45, 0x52, 0x5f, 0x4d, 0x55, 0x4c, 0x54, 0x49, 0x5f, 0x43, 0x41, 0x4e, 
 	0x56, 0x41, 0x53, 0x20, 0x3d, 0x20, 0x5b, 0x5b, 0x0a,
@@ -6358,10 +6369,19 @@ const unsigned char graphics_lua[] =
 	0x09, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x64, 0x75, 0x6d, 0x6d, 0x79, 0x20, 0x3d, 0x20, 0x74, 0x65, 0x78, 
 	0x74, 0x75, 0x72, 0x65, 0x32, 0x44, 0x28, 0x5f, 0x74, 0x65, 0x78, 0x30, 0x5f, 0x2c, 0x20, 0x76, 0x65, 0x63, 
 	0x32, 0x28, 0x2e, 0x35, 0x29, 0x29, 0x2e, 0x72, 0x3b, 0x0a,
+	0x09, 0x2f, 0x2f, 0x20, 0x53, 0x65, 0x65, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x3a, 0x3a, 0x63, 0x68, 
+	0x65, 0x63, 0x6b, 0x53, 0x65, 0x74, 0x53, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 
+	0x20, 0x69, 0x6e, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x2e, 0x63, 0x70, 0x70, 0x2e, 0x0a,
+	0x09, 0x76, 0x65, 0x63, 0x32, 0x20, 0x70, 0x69, 0x78, 0x65, 0x6c, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x20, 0x3d, 
+	0x20, 0x76, 0x65, 0x63, 0x32, 0x28, 0x67, 0x6c, 0x5f, 0x46, 0x72, 0x61, 0x67, 0x43, 0x6f, 0x6f, 0x72, 0x64, 
+	0x2e, 0x78, 0x2c, 0x20, 0x28, 0x67, 0x6c, 0x5f, 0x46, 0x72, 0x61, 0x67, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x2e, 
+	0x79, 0x20, 0x2a, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x5f, 0x53, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x50, 0x61, 0x72, 
+	0x61, 0x6d, 0x73, 0x5b, 0x30, 0x5d, 0x29, 0x20, 0x2b, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x5f, 0x53, 0x63, 0x72, 
+	0x65, 0x65, 0x6e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x5b, 0x31, 0x5d, 0x29, 0x3b, 0x0a,
 	0x09, 0x65, 0x66, 0x66, 0x65, 0x63, 0x74, 0x73, 0x28, 0x56, 0x61, 0x72, 0x79, 0x69, 0x6e, 0x67, 0x43, 0x6f, 
 	0x6c, 0x6f, 0x72, 0x2c, 0x20, 0x5f, 0x74, 0x65, 0x78, 0x30, 0x5f, 0x2c, 0x20, 0x56, 0x61, 0x72, 0x79, 0x69, 
-	0x6e, 0x67, 0x54, 0x65, 0x78, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x2e, 0x73, 0x74, 0x2c, 0x20, 0x67, 0x6c, 0x5f, 
-	0x46, 0x72, 0x61, 0x67, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x2e, 0x78, 0x79, 0x29, 0x3b, 0x0a,
+	0x6e, 0x67, 0x54, 0x65, 0x78, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x2e, 0x73, 0x74, 0x2c, 0x20, 0x70, 0x69, 0x78, 
+	0x65, 0x6c, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x29, 0x3b, 0x0a,
 	0x7d, 0x5d, 0x5d, 0x2c, 0x0a,
 	0x09, 0x7d, 0x0a,
 	0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x72,