Преглед на файлове

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 години
родител
ревизия
aa69a695d3
променени са 43 файла, в които са добавени 1005 реда и са изтрити 846 реда
  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,