Browse Source

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

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

+ 3 - 4
CMakeLists.txt

@@ -249,15 +249,13 @@ source_group("modules\\font\\freetype" FILES ${LOVE_SRC_MODULE_FONT_FREETYPE})
 
 
 set(LOVE_SRC_MODULE_GRAPHICS_ROOT
 set(LOVE_SRC_MODULE_GRAPHICS_ROOT
 	src/modules/graphics/Color.h
 	src/modules/graphics/Color.h
-	src/modules/graphics/Drawable.cpp
 	src/modules/graphics/Drawable.h
 	src/modules/graphics/Drawable.h
-	src/modules/graphics/DrawQable.h
 	src/modules/graphics/Graphics.cpp
 	src/modules/graphics/Graphics.cpp
 	src/modules/graphics/Graphics.h
 	src/modules/graphics/Graphics.h
-	src/modules/graphics/Image.cpp
-	src/modules/graphics/Image.h
 	src/modules/graphics/Quad.cpp
 	src/modules/graphics/Quad.cpp
 	src/modules/graphics/Quad.h
 	src/modules/graphics/Quad.h
+	src/modules/graphics/Texture.cpp
+	src/modules/graphics/Texture.h
 	src/modules/graphics/Volatile.cpp
 	src/modules/graphics/Volatile.cpp
 	src/modules/graphics/Volatile.h
 	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/Shader.h
 	src/modules/graphics/opengl/SpriteBatch.cpp
 	src/modules/graphics/opengl/SpriteBatch.cpp
 	src/modules/graphics/opengl/SpriteBatch.h
 	src/modules/graphics/opengl/SpriteBatch.h
+	src/modules/graphics/opengl/Texture.h
 	src/modules/graphics/opengl/VertexBuffer.cpp
 	src/modules/graphics/opengl/VertexBuffer.cpp
 	src/modules/graphics/opengl/VertexBuffer.h
 	src/modules/graphics/opengl/VertexBuffer.h
 	src/modules/graphics/opengl/wrap_Canvas.cpp
 	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 */; };
 		FA08F61216C753E700F007B5 /* Rasterizer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1B1C4E4D288A1D2F29E57B1B /* Rasterizer.cpp */; };
 		FA08F61316C753E700F007B5 /* wrap_GlyphData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1B4E22F1388E2B2E76E3377B /* wrap_GlyphData.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 */; };
 		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 */; };
 		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 */; };
 		FA08F61A16C753F600F007B5 /* Volatile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B731754147B27AF73AC5358 /* Volatile.cpp */; };
 		FA08F61B16C7541400F007B5 /* Canvas.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4AD52074367950B735707CE1 /* Canvas.cpp */; };
 		FA08F61B16C7541400F007B5 /* Canvas.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4AD52074367950B735707CE1 /* Canvas.cpp */; };
 		FA08F61C16C7541400F007B5 /* Font.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 583037E9238A6EF00DD20B1A /* Font.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 */; };
 		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 */; };
 		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 */; };
 		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 */; };
 		FA9B4A0816E1578300074F42 /* SDL2.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA9B4A0716E1578300074F42 /* SDL2.framework */; };
 		FA9FC0B0173D6E3E005027FF /* wrap_Window.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA9FC0AE173D6E3E005027FF /* wrap_Window.cpp */; };
 		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 */; };
 		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 */; };
 		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 */; };
 		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 */; };
 		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 */; };
 		FAC86E6B1724555D00EED715 /* Quad.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FAC86E671724555D00EED715 /* Quad.cpp */; };
 		FAC86E6C1724555D00EED715 /* Quad.h in Headers */ = {isa = PBXBuildFile; fileRef = FAC86E681724555D00EED715 /* Quad.h */; };
 		FAC86E6C1724555D00EED715 /* Quad.h in Headers */ = {isa = PBXBuildFile; fileRef = FAC86E681724555D00EED715 /* Quad.h */; };
 		FAE010DB170DDE99006F29D0 /* ddsinfo.h in Headers */ = {isa = PBXBuildFile; fileRef = FAE010D8170DDE99006F29D0 /* ddsinfo.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>"; };
 		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>"; };
 		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>"; };
 		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>"; };
 		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>"; };
 		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>"; };
 		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>"; };
 		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>"; };
 		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>"; };
 		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>"; };
 		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>"; };
 		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>"; };
 		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>"; };
 		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>"; };
 		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>"; };
 		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>"; };
 		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>"; };
 		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>"; };
 		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>"; };
 		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>"; };
 		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>"; };
 		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>"; };
 		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>"; };
 		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>"; };
 		FAE010D8170DDE99006F29D0 /* ddsinfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ddsinfo.h; sourceTree = "<group>"; };
@@ -1476,16 +1474,14 @@
 			isa = PBXGroup;
 			isa = PBXGroup;
 			children = (
 			children = (
 				4941079838020ECA049B5C21 /* Color.h */,
 				4941079838020ECA049B5C21 /* Color.h */,
-				58BA2BB460AF3C591B22690E /* Drawable.cpp */,
 				5D93601669875EE06721689E /* Drawable.h */,
 				5D93601669875EE06721689E /* Drawable.h */,
-				FAC86E661724555D00EED715 /* DrawQable.h */,
 				03F17FF546D637744E263961 /* Graphics.cpp */,
 				03F17FF546D637744E263961 /* Graphics.cpp */,
 				777352284E262F48543E6E7F /* Graphics.h */,
 				777352284E262F48543E6E7F /* Graphics.h */,
-				58CC50E70A375FDF53EF01B6 /* Image.cpp */,
-				1DA41DFF0869489411A71AFC /* Image.h */,
 				75093EE94918576801F50993 /* opengl */,
 				75093EE94918576801F50993 /* opengl */,
 				FAC86E671724555D00EED715 /* Quad.cpp */,
 				FAC86E671724555D00EED715 /* Quad.cpp */,
 				FAC86E681724555D00EED715 /* Quad.h */,
 				FAC86E681724555D00EED715 /* Quad.h */,
+				58CC50E70A375FDF53EF01B6 /* Texture.cpp */,
+				1DA41DFF0869489411A71AFC /* Texture.h */,
 				4B731754147B27AF73AC5358 /* Volatile.cpp */,
 				4B731754147B27AF73AC5358 /* Volatile.cpp */,
 				0CFF64090F0F4F481BB80CF0 /* Volatile.h */,
 				0CFF64090F0F4F481BB80CF0 /* Volatile.h */,
 			);
 			);
@@ -1683,6 +1679,7 @@
 				FA577A8616C71CF000860150 /* Shader.h */,
 				FA577A8616C71CF000860150 /* Shader.h */,
 				4D700D182EAA46273D1E2CC4 /* SpriteBatch.cpp */,
 				4D700D182EAA46273D1E2CC4 /* SpriteBatch.cpp */,
 				727D23FA1CC755B902471A45 /* SpriteBatch.h */,
 				727D23FA1CC755B902471A45 /* SpriteBatch.h */,
+				FA9B49281875EFB900201DA9 /* Texture.h */,
 				426B1C4475DC54505B824B7F /* VertexBuffer.cpp */,
 				426B1C4475DC54505B824B7F /* VertexBuffer.cpp */,
 				577B66502A5360AE60733B10 /* VertexBuffer.h */,
 				577B66502A5360AE60733B10 /* VertexBuffer.h */,
 				4E3251027026699A1D4D310D /* wrap_Canvas.cpp */,
 				4E3251027026699A1D4D310D /* wrap_Canvas.cpp */,
@@ -1979,7 +1976,6 @@
 				FA636D8B171B70920065623F /* RandomGenerator.h in Headers */,
 				FA636D8B171B70920065623F /* RandomGenerator.h in Headers */,
 				FA636D8F171B72A70065623F /* wrap_RandomGenerator.h in Headers */,
 				FA636D8F171B72A70065623F /* wrap_RandomGenerator.h in Headers */,
 				FAC86E641724552C00EED715 /* wrap_Quad.h in Headers */,
 				FAC86E641724552C00EED715 /* wrap_Quad.h in Headers */,
-				FAC86E6A1724555D00EED715 /* DrawQable.h in Headers */,
 				FAC86E6C1724555D00EED715 /* Quad.h in Headers */,
 				FAC86E6C1724555D00EED715 /* Quad.h in Headers */,
 				FA03546D1731F3A700284828 /* simplexnoise1234.h in Headers */,
 				FA03546D1731F3A700284828 /* simplexnoise1234.h in Headers */,
 				FA3D9E0E16E68DE600CA6630 /* Cursor.h in Headers */,
 				FA3D9E0E16E68DE600CA6630 /* Cursor.h in Headers */,
@@ -1990,6 +1986,7 @@
 				FAF6705218184FF800DBDEEA /* wuff_internal.h in Headers */,
 				FAF6705218184FF800DBDEEA /* wuff_internal.h in Headers */,
 				FAB007941740C28900A9664D /* Joystick.h in Headers */,
 				FAB007941740C28900A9664D /* Joystick.h in Headers */,
 				FAB007981740C87D00A9664D /* wrap_Joystick.h in Headers */,
 				FAB007981740C87D00A9664D /* wrap_Joystick.h in Headers */,
+				FA9B492A1875EFB900201DA9 /* Texture.h in Headers */,
 				FAC5710117402D1100D147E4 /* BezierCurve.h in Headers */,
 				FAC5710117402D1100D147E4 /* BezierCurve.h in Headers */,
 				FAC5710317402D1100D147E4 /* wrap_BezierCurve.h in Headers */,
 				FAC5710317402D1100D147E4 /* wrap_BezierCurve.h in Headers */,
 				FA5FDC7E1788D548002F0ED2 /* callbacks.h in Headers */,
 				FA5FDC7E1788D548002F0ED2 /* callbacks.h in Headers */,
@@ -2179,9 +2176,8 @@
 				FA08F61216C753E700F007B5 /* Rasterizer.cpp in Sources */,
 				FA08F61216C753E700F007B5 /* Rasterizer.cpp in Sources */,
 				FA08F61316C753E700F007B5 /* wrap_GlyphData.cpp in Sources */,
 				FA08F61316C753E700F007B5 /* wrap_GlyphData.cpp in Sources */,
 				FA08F61416C753E700F007B5 /* wrap_Rasterizer.cpp in Sources */,
 				FA08F61416C753E700F007B5 /* wrap_Rasterizer.cpp in Sources */,
-				FA08F61516C753F600F007B5 /* Drawable.cpp in Sources */,
 				FA08F61716C753F600F007B5 /* Graphics.cpp in Sources */,
 				FA08F61716C753F600F007B5 /* Graphics.cpp in Sources */,
-				FA08F61816C753F600F007B5 /* Image.cpp in Sources */,
+				FA08F61816C753F600F007B5 /* Texture.cpp in Sources */,
 				FA08F61A16C753F600F007B5 /* Volatile.cpp in Sources */,
 				FA08F61A16C753F600F007B5 /* Volatile.cpp in Sources */,
 				FA08F61B16C7541400F007B5 /* Canvas.cpp in Sources */,
 				FA08F61B16C7541400F007B5 /* Canvas.cpp in Sources */,
 				FA08F61C16C7541400F007B5 /* Font.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
 	// Graphics
 	{"Drawable", GRAPHICS_DRAWABLE_ID},
 	{"Drawable", GRAPHICS_DRAWABLE_ID},
+	{"Texture", GRAPHICS_TEXTURE_ID},
 	{"Image", GRAPHICS_IMAGE_ID},
 	{"Image", GRAPHICS_IMAGE_ID},
 	{"Quad", GRAPHICS_QUAD_ID},
 	{"Quad", GRAPHICS_QUAD_ID},
 	{"Font", GRAPHICS_FONT_ID},
 	{"Font", GRAPHICS_FONT_ID},

+ 4 - 4
src/common/types.h

@@ -45,7 +45,7 @@ enum Type
 
 
 	// Graphics
 	// Graphics
 	GRAPHICS_DRAWABLE_ID,
 	GRAPHICS_DRAWABLE_ID,
-	GRAPHICS_DRAWQABLE_ID,
+	GRAPHICS_TEXTURE_ID,
 	GRAPHICS_IMAGE_ID,
 	GRAPHICS_IMAGE_ID,
 	GRAPHICS_QUAD_ID,
 	GRAPHICS_QUAD_ID,
 	GRAPHICS_FONT_ID,
 	GRAPHICS_FONT_ID,
@@ -130,13 +130,13 @@ const bits FONT_RASTERIZER_T = (bits(1) << FONT_RASTERIZER_ID) | OBJECT_T;
 
 
 // Graphics.
 // Graphics.
 const bits GRAPHICS_DRAWABLE_T = (bits(1) << GRAPHICS_DRAWABLE_ID) | OBJECT_T;
 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_QUAD_T = (bits(1) << GRAPHICS_QUAD_ID) | OBJECT_T;
 const bits GRAPHICS_FONT_T = (bits(1) << GRAPHICS_FONT_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_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_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_SHADER_T = (bits(1) << GRAPHICS_SHADER_ID) | OBJECT_T;
 const bits GRAPHICS_MESH_T = (bits(1) << GRAPHICS_MESH_ID) | GRAPHICS_DRAWABLE_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.
 	 * Destructor.
 	 **/
 	 **/
-	virtual ~Drawable();
+	virtual ~Drawable() {}
 
 
 	/**
 	/**
 	 * Draws the object with the specified transformation.
 	 * 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.
  * 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
 // LOVE
-#include "graphics/Volatile.h"
-#include "graphics/DrawQable.h"
 #include "common/StringMap.h"
 #include "common/StringMap.h"
+#include "common/math.h"
+#include "Drawable.h"
+#include "Quad.h"
 
 
 namespace love
 namespace love
 {
 {
 namespace graphics
 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:
 public:
 
 
 	enum WrapMode
 	enum WrapMode
 	{
 	{
-		WRAP_CLAMP = 1,
+		WRAP_CLAMP,
 		WRAP_REPEAT,
 		WRAP_REPEAT,
 		WRAP_MAX_ENUM
 		WRAP_MAX_ENUM
 	};
 	};
 
 
 	enum FilterMode
 	enum FilterMode
 	{
 	{
-		FILTER_LINEAR = 1,
-		FILTER_NEAREST,
 		FILTER_NONE,
 		FILTER_NONE,
+		FILTER_LINEAR,
+		FILTER_NEAREST,
 		FILTER_MAX_ENUM
 		FILTER_MAX_ENUM
 	};
 	};
 
 
@@ -66,17 +71,56 @@ public:
 		WrapMode t;
 		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.
 	// The default filter.
 	static void setDefaultFilter(const Filter &f);
 	static void setDefaultFilter(const Filter &f);
 	static const Filter &getDefaultFilter();
 	static const Filter &getDefaultFilter();
 
 
 	static bool getConstant(const char *in, FilterMode &out);
 	static bool getConstant(const char *in, FilterMode &out);
 	static bool getConstant(FilterMode in, const char  *&out);
 	static bool getConstant(FilterMode in, const char  *&out);
+
 	static bool getConstant(const char *in, WrapMode &out);
 	static bool getConstant(const char *in, WrapMode &out);
 	static bool getConstant(WrapMode in, const char  *&out);
 	static bool getConstant(WrapMode in, const char  *&out);
 
 
+protected:
+
+	int width;
+	int height;
+
+	Filter filter;
+	Wrap wrap;
+
+	Vertex vertices[4];
+
 private:
 private:
 
 
 	// The default texture filter.
 	// The default texture filter.
@@ -84,12 +128,13 @@ private:
 
 
 	static StringMap<FilterMode, FILTER_MAX_ENUM>::Entry filterModeEntries[];
 	static StringMap<FilterMode, FILTER_MAX_ENUM>::Entry filterModeEntries[];
 	static StringMap<FilterMode, FILTER_MAX_ENUM> filterModes;
 	static StringMap<FilterMode, FILTER_MAX_ENUM> filterModes;
+
 	static StringMap<WrapMode, WRAP_MAX_ENUM>::Entry wrapModeEntries[];
 	static StringMap<WrapMode, WRAP_MAX_ENUM>::Entry wrapModeEntries[];
 	static StringMap<WrapMode, WRAP_MAX_ENUM> wrapModes;
 	static StringMap<WrapMode, WRAP_MAX_ENUM> wrapModes;
 
 
-}; // Image
+}; // Texture
 
 
 } // graphics
 } // graphics
 } // love
 } // 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);
 		glGenTextures(1, &img);
 		gl.bindTexture(img);
 		gl.bindTexture(img);
 
 
-		gl.setTextureFilter(Image::getDefaultFilter());
+		Texture::Filter filter = Texture::getDefaultFilter();
+		gl.setTextureFilter(filter);
 
 
 		glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height,
 		glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height,
 			0, GL_RGBA, format, NULL);
 			0, GL_RGBA, format, NULL);
@@ -183,7 +184,7 @@ struct FramebufferStrategyGL3 : public FramebufferStrategy
 		for (size_t i = 0; i < canvases.size(); i++)
 		for (size_t i = 0; i < canvases.size(); i++)
 		{
 		{
 			glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1 + 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);
 			drawbuffers.push_back(GL_COLOR_ATTACHMENT1 + i);
 		}
 		}
 
 
@@ -224,7 +225,8 @@ struct FramebufferStrategyPackedEXT : public FramebufferStrategy
 		glGenTextures(1, &img);
 		glGenTextures(1, &img);
 		gl.bindTexture(img);
 		gl.bindTexture(img);
 
 
-		gl.setTextureFilter(Image::getDefaultFilter());
+		Texture::Filter filter = Texture::getDefaultFilter();
+		gl.setTextureFilter(filter);
 
 
 		glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height,
 		glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height,
 			0, GL_RGBA, format, NULL);
 			0, GL_RGBA, format, NULL);
@@ -292,7 +294,7 @@ struct FramebufferStrategyPackedEXT : public FramebufferStrategy
 		for (size_t i = 0; i < canvases.size(); i++)
 		for (size_t i = 0; i < canvases.size(); i++)
 		{
 		{
 			glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1_EXT + 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);
 			drawbuffers.push_back(GL_COLOR_ATTACHMENT1_EXT + i);
 		}
 		}
 
 
@@ -364,37 +366,36 @@ static int maxFBOColorAttachments = 0;
 static int maxDrawBuffers = 0;
 static int maxDrawBuffers = 0;
 
 
 Canvas::Canvas(int width, int height, TextureType texture_type)
 Canvas::Canvas(int width, int height, TextureType texture_type)
-	: width(width)
-	, height(height)
-	, fbo(0)
+	: fbo(0)
 	, depth_stencil(0)
 	, depth_stencil(0)
 	, img(0)
 	, img(0)
 	, texture_type(texture_type)
 	, texture_type(texture_type)
 {
 {
+	this->width = width;
+	this->height = height;
+
 	float w = static_cast<float>(width);
 	float w = static_cast<float>(width);
 	float h = static_cast<float>(height);
 	float h = static_cast<float>(height);
 
 
 	// world coordinates
 	// world coordinates
 	vertices[0].x = 0;
 	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[1].y = h;
 	vertices[2].x = w;
 	vertices[2].x = w;
-	vertices[2].y = 0;
-	vertices[3].x = 0;
+	vertices[2].y = h;
+	vertices[3].x = w;
 	vertices[3].y = 0;
 	vertices[3].y = 0;
 
 
 	// texture coordinates
 	// texture coordinates
 	vertices[0].s = 0;
 	vertices[0].s = 0;
 	vertices[0].t = 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].s = 1;
 	vertices[2].t = 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();
 	getStrategy();
 
 
@@ -410,36 +411,100 @@ Canvas::~Canvas()
 	unloadVolatile();
 	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()
 void Canvas::setupGrab()
@@ -463,7 +528,7 @@ void Canvas::setupGrab()
 	glLoadIdentity();
 	glLoadIdentity();
 
 
 	// Set up orthographic view (no depth)
 	// 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
 	// Switch back to modelview matrix
 	glMatrixMode(GL_MODELVIEW);
 	glMatrixMode(GL_MODELVIEW);
@@ -545,8 +610,10 @@ void Canvas::stopGrab()
 	glMatrixMode(GL_PROJECTION);
 	glMatrixMode(GL_PROJECTION);
 	glPopMatrix();
 	glPopMatrix();
 	glMatrixMode(GL_MODELVIEW);
 	glMatrixMode(GL_MODELVIEW);
+
+	current = nullptr;
+
 	gl.setViewport(systemViewport);
 	gl.setViewport(systemViewport);
-	current = NULL;
 }
 }
 
 
 void Canvas::clear(Color c)
 void Canvas::clear(Color c)
@@ -558,7 +625,7 @@ void Canvas::clear(Color c)
 
 
 	if (current != this)
 	if (current != this)
 	{
 	{
-		if (current != NULL)
+		if (current != nullptr)
 			previous = current->fbo;
 			previous = current->fbo;
 
 
 		strategy->bindFBO(fbo);
 		strategy->bindFBO(fbo);
@@ -601,30 +668,6 @@ void Canvas::clear(Color c)
 		strategy->bindFBO(previous);
 		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()
 bool Canvas::checkCreateStencil()
 {
 {
 	// Do nothing if we've already created the stencil buffer.
 	// 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 row = 4 * width;
 	int size = row * height;
 	int size = row * height;
 	GLubyte *pixels  = new GLubyte[size];
 	GLubyte *pixels  = new GLubyte[size];
-	GLubyte *flipped = new GLubyte[size];
 
 
 	strategy->bindFBO(fbo);
 	strategy->bindFBO(fbo);
 	glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
 	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
 	else
 		strategy->bindFBO(0);
 		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;
 	return img;
 }
 }
 
 
@@ -683,98 +718,36 @@ void Canvas::getPixel(unsigned char* pixel_rgba, int x, int y)
 		strategy->bindFBO(0);
 		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)
 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
 #ifndef LOVE_GRAPHICS_OPENGL_CANVAS_H
 #define 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 "graphics/Color.h"
 #include "image/Image.h"
 #include "image/Image.h"
 #include "image/ImageData.h"
 #include "image/ImageData.h"
-#include "common/math.h"
 #include "common/Matrix.h"
 #include "common/Matrix.h"
+#include "Texture.h"
 #include "OpenGL.h"
 #include "OpenGL.h"
 
 
 namespace love
 namespace love
@@ -38,7 +35,7 @@ namespace graphics
 namespace opengl
 namespace opengl
 {
 {
 
 
-class Canvas : public DrawQable, public Volatile
+class Canvas : public Texture
 {
 {
 public:
 public:
 
 
@@ -52,6 +49,20 @@ public:
 	Canvas(int width, int height, TextureType texture_type = TYPE_NORMAL);
 	Canvas(int width, int height, TextureType texture_type = TYPE_NORMAL);
 	virtual ~Canvas();
 	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,
 	 * @param canvases A list of other canvases to temporarily attach to this one,
 	 * to allow drawing to multiple canvases at once.
 	 * to allow drawing to multiple canvases at once.
@@ -62,13 +73,6 @@ public:
 
 
 	void clear(Color c);
 	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.
 	 * 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);
 	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
 	inline GLenum getStatus() const
 	{
 	{
@@ -99,9 +97,6 @@ public:
 		return texture_type;
 		return texture_type;
 	}
 	}
 
 
-	bool loadVolatile();
-	void unloadVolatile();
-
 	static bool isSupported();
 	static bool isSupported();
 	static bool isHDRSupported();
 	static bool isHDRSupported();
 	static bool isMultiCanvasSupported();
 	static bool isMultiCanvasSupported();
@@ -115,33 +110,16 @@ public:
 	// The viewport dimensions of the system (default) framebuffer.
 	// The viewport dimensions of the system (default) framebuffer.
 	static OpenGL::Viewport systemViewport;
 	static OpenGL::Viewport systemViewport;
 
 
-	GLuint getTextureName() const
-	{
-		return img;
-	}
-
 private:
 private:
 
 
-	friend class Shader;
-
-	GLsizei width;
-	GLsizei height;
 	GLuint fbo;
 	GLuint fbo;
 	GLuint depth_stencil;
 	GLuint depth_stencil;
 	GLuint img;
 	GLuint img;
 
 
 	TextureType texture_type;
 	TextureType texture_type;
 
 
-	Vertex vertices[4];
-
 	GLenum status;
 	GLenum status;
 
 
-	struct
-	{
-		Image::Filter filter;
-		Image::Wrap   wrap;
-	} settings;
-
 	std::vector<Canvas *> attachedCanvases;
 	std::vector<Canvas *> attachedCanvases;
 
 
 	void setupGrab();
 	void setupGrab();

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

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

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

@@ -29,7 +29,8 @@
 // LOVE
 // LOVE
 #include "common/Object.h"
 #include "common/Object.h"
 #include "font/Rasterizer.h"
 #include "font/Rasterizer.h"
-#include "graphics/Image.h"
+#include "graphics/Texture.h"
+#include "graphics/Volatile.h"
 
 
 #include "OpenGL.h"
 #include "OpenGL.h"
 
 
@@ -44,7 +45,7 @@ class Font : public Object, public Volatile
 {
 {
 public:
 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();
 	virtual ~Font();
 
 
@@ -122,8 +123,8 @@ public:
 	 **/
 	 **/
 	float getSpacing() const;
 	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.
 	// Implements Volatile.
 	bool loadVolatile();
 	bool loadVolatile();
@@ -199,7 +200,7 @@ private:
 	std::map<uint32, Glyph *> glyphs;
 	std::map<uint32, Glyph *> glyphs;
 
 
 	FontType type;
 	FontType type;
-	Image::Filter filter;
+	Texture::Filter filter;
 
 
 	static const int NUM_TEXTURE_SIZES = 7;
 	static const int NUM_TEXTURE_SIZES = 7;
 	static const int TEXTURE_WIDTHS[NUM_TEXTURE_SIZES];
 	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.
 	// Create the image.
 	Image *image = new Image(data);
 	Image *image = new Image(data);
-	bool success;
+
+	if (!isCreated())
+		return image;
+
+	bool success = false;
 	try
 	try
 	{
 	{
 		success = image->load();
 		success = image->load();
@@ -350,7 +354,11 @@ Image *Graphics::newImage(love::image::CompressedData *cdata)
 {
 {
 	// Create the image.
 	// Create the image.
 	Image *image = new Image(cdata);
 	Image *image = new Image(cdata);
-	bool success;
+
+	if (!isCreated())
+		return image;
+
+	bool success = false;
 	try
 	try
 	{
 	{
 		success = image->load();
 		success = image->load();
@@ -374,19 +382,19 @@ Quad *Graphics::newQuad(Quad::Viewport v, float sw, float sh)
 	return new Quad(v, sw, 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);
 	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)
 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");
 	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::setDefaultMipmapFilter(filter);
 	Image::setDefaultMipmapSharpness(sharpness);
 	Image::setDefaultMipmapSharpness(sharpness);
 }
 }
 
 
-void Graphics::getDefaultMipmapFilter(Image::FilterMode *filter, float *sharpness) const
+void Graphics::getDefaultMipmapFilter(Texture::FilterMode *filter, float *sharpness) const
 {
 {
 	*filter = Image::getDefaultMipmapFilter();
 	*filter = Image::getDefaultMipmapFilter();
 	*sharpness = Image::getDefaultMipmapSharpness();
 	*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)
 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);
 		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)
 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;
 		return;
 
 
 	if (wrap < 0.0f)
 	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)
 void Graphics::point(float x, float y)
 {
 {
+	gl.prepareDraw();
 	gl.bindTexture(0);
 	gl.bindTexture(0);
 	glBegin(GL_POINTS);
 	glBegin(GL_POINTS);
 	glVertex2f(x, y);
 	glVertex2f(x, y);
@@ -890,6 +899,7 @@ void Graphics::arc(DrawMode mode, float x, float y, float radius, float angle1,
 	}
 	}
 	else
 	else
 	{
 	{
+		gl.prepareDraw();
 		gl.bindTexture(0);
 		gl.bindTexture(0);
 		glEnableClientState(GL_VERTEX_ARRAY);
 		glEnableClientState(GL_VERTEX_ARRAY);
 		glVertexPointer(2, GL_FLOAT, 0, (const GLvoid *) coords);
 		glVertexPointer(2, GL_FLOAT, 0, (const GLvoid *) coords);
@@ -913,6 +923,7 @@ void Graphics::polygon(DrawMode mode, const float *coords, size_t count)
 	}
 	}
 	else
 	else
 	{
 	{
+		gl.prepareDraw();
 		gl.bindTexture(0);
 		gl.bindTexture(0);
 		glEnableClientState(GL_VERTEX_ARRAY);
 		glEnableClientState(GL_VERTEX_ARRAY);
 		glVertexPointer(2, GL_FLOAT, 0, (const GLvoid *)coords);
 		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.
 	 * 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);
 	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.
 	 * 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.
 	 * 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.
 	 * 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.
 	 * Sets the line width.

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

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

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

@@ -28,7 +28,7 @@
 #include "common/math.h"
 #include "common/math.h"
 #include "image/ImageData.h"
 #include "image/ImageData.h"
 #include "image/CompressedData.h"
 #include "image/CompressedData.h"
-#include "graphics/Image.h"
+#include "Texture.h"
 
 
 // OpenGL
 // OpenGL
 #include "OpenGL.h"
 #include "OpenGL.h"
@@ -46,7 +46,7 @@ namespace opengl
  *
  *
  * @author Anders Ruud
  * @author Anders Ruud
  **/
  **/
-class Image : public love::graphics::Image
+class Image : public Texture
 {
 {
 public:
 public:
 
 
@@ -70,11 +70,6 @@ public:
 	 **/
 	 **/
 	virtual ~Image();
 	virtual ~Image();
 
 
-	int getWidth() const;
-	int getHeight() const;
-
-	const Vertex *getVertices() const;
-
 	love::image::ImageData *getImageData() const;
 	love::image::ImageData *getImageData() const;
 	love::image::CompressedData *getCompressedData() 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;
 	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;
 	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
 	 * globally scales texture coordinates if the Image has NPOT dimensions and
 	 * NPOT isn't supported, etc.
 	 * 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);
 	void setMipmapSharpness(float sharpness);
 	float getMipmapSharpness() const;
 	float getMipmapSharpness() const;
@@ -151,12 +139,6 @@ private:
 
 
 	void drawv(const Matrix &t, const Vertex *v) const;
 	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
 	// The ImageData from which the texture is created. May be null if
 	// Compressed image data was used to create the texture.
 	// Compressed image data was used to create the texture.
 	love::image::ImageData *data;
 	love::image::ImageData *data;
@@ -165,18 +147,12 @@ private:
 	// null if raw ImageData was used to create the texture.
 	// null if raw ImageData was used to create the texture.
 	love::image::CompressedData *cdata;
 	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.
 	// Real dimensions of the texture, if it was auto-padded to POT size.
 	int paddedWidth, paddedHeight;
 	int paddedWidth, paddedHeight;
 
 
 	// OpenGL texture identifier.
 	// OpenGL texture identifier.
 	GLuint texture;
 	GLuint texture;
 
 
-	// The source vertices of the image.
-	Vertex vertices[4];
-
 	// Mipmap texture LOD bias (sharpness) value.
 	// Mipmap texture LOD bias (sharpness) value.
 	float mipmapSharpness;
 	float mipmapSharpness;
 
 
@@ -190,12 +166,6 @@ private:
 	// back to a default texture.
 	// back to a default texture.
 	bool usingDefaultTexture;
 	bool usingDefaultTexture;
 
 
-	// The image's filter mode
-	Image::Filter filter;
-
-	// The image's wrap mode
-	Image::Wrap wrap;
-
 	void preload();
 	void preload();
 
 
 	void uploadTexturePadded();
 	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)
 	, ibo(nullptr)
 	, element_count(0)
 	, element_count(0)
 	, draw_mode(mode)
 	, draw_mode(mode)
-	, image(nullptr)
+	, texture(nullptr)
 	, colors_enabled(false)
 	, colors_enabled(false)
 	, wireframe(false)
 	, wireframe(false)
 {
 {
@@ -170,27 +170,27 @@ size_t Mesh::getVertexMapCount() const
 	return element_count;
 	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)
 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)
 	if (vertex_count == 0)
 		return;
 		return;
 
 
-	if (image)
-		image->predraw();
+	if (texture)
+		texture->predraw();
 	else
 	else
 		gl.bindTexture(0);
 		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);
 	GLenum mode = getGLDrawMode(draw_mode);
 
 
+	gl.prepareDraw();
+
 	if (ibo && element_count > 0)
 	if (ibo && element_count > 0)
 	{
 	{
 		VertexBuffer::Bind ibo_bind(*ibo);
 		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();
 	glPopMatrix();
 
 
-	if (image)
-		image->postdraw();
+	if (texture)
+		texture->postdraw();
 }
 }
 
 
 GLenum Mesh::getGLDrawMode(Mesh::DrawMode mode) const
 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/math.h"
 #include "common/StringMap.h"
 #include "common/StringMap.h"
 #include "graphics/Drawable.h"
 #include "graphics/Drawable.h"
-#include "Image.h"
+#include "Texture.h"
 #include "VertexBuffer.h"
 #include "VertexBuffer.h"
 
 
 // C++
 // C++
@@ -110,20 +110,20 @@ public:
 	size_t getVertexMapCount() const;
 	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.
 	 * Sets the draw mode used when drawing the Mesh.
@@ -167,7 +167,7 @@ private:
 
 
 	DrawMode draw_mode;
 	DrawMode draw_mode;
 
 
-	Image *image;
+	Texture *texture;
 
 
 	// Whether the per-vertex colors are used when drawing.
 	// Whether the per-vertex colors are used when drawing.
 	bool colors_enabled;
 	bool colors_enabled;

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

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

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

@@ -25,9 +25,9 @@
 
 
 // LOVE
 // LOVE
 #include "graphics/Color.h"
 #include "graphics/Color.h"
-#include "graphics/Image.h"
+#include "graphics/Texture.h"
 
 
-// STL
+// C++
 #include <vector>
 #include <vector>
 
 
 // The last argument to AttribPointer takes a buffer offset casted to a pointer.
 // The last argument to AttribPointer takes a buffer offset casted to a pointer.
@@ -93,6 +93,12 @@ public:
 	 **/
 	 **/
 	void deInitContext();
 	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.
 	 * Sets the current constant color.
 	 **/
 	 **/
@@ -163,25 +169,25 @@ public:
 	void deleteTexture(GLuint texture);
 	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.
 	 * 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.
 	 * 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
 } // anonymous namespace
 
 
-ParticleSystem::ParticleSystem(Image *image, uint32 size)
+ParticleSystem::ParticleSystem(Texture *texture, uint32 size)
 	: pMem(nullptr)
 	: pMem(nullptr)
 	, pFree(nullptr)
 	, pFree(nullptr)
 	, pHead(nullptr)
 	, pHead(nullptr)
 	, pTail(nullptr)
 	, pTail(nullptr)
 	, particleVerts(nullptr)
 	, particleVerts(nullptr)
-	, image(image)
+	, texture(texture)
 	, active(true)
 	, active(true)
 	, insertMode(INSERT_MODE_TOP)
 	, insertMode(INSERT_MODE_TOP)
 	, maxParticles(0)
 	, maxParticles(0)
@@ -92,8 +92,8 @@ ParticleSystem::ParticleSystem(Image *image, uint32 size)
 	, spinStart(0)
 	, spinStart(0)
 	, spinEnd(0)
 	, spinEnd(0)
 	, spinVariation(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)
 	if (size == 0 || size > MAX_PARTICLES)
 		throw love::Exception("Invalid ParticleSystem size.");
 		throw love::Exception("Invalid ParticleSystem size.");
@@ -101,7 +101,7 @@ ParticleSystem::ParticleSystem(Image *image, uint32 size)
 	sizes.push_back(1.0f);
 	sizes.push_back(1.0f);
 	colors.push_back(Colorf(1.0f, 1.0f, 1.0f, 1.0f));
 	colors.push_back(Colorf(1.0f, 1.0f, 1.0f, 1.0f));
 	setBufferSize(size);
 	setBufferSize(size);
-	image->retain();
+	texture->retain();
 }
 }
 
 
 ParticleSystem::ParticleSystem(const ParticleSystem &p)
 ParticleSystem::ParticleSystem(const ParticleSystem &p)
@@ -110,7 +110,7 @@ ParticleSystem::ParticleSystem(const ParticleSystem &p)
 	, pHead(nullptr)
 	, pHead(nullptr)
 	, pTail(nullptr)
 	, pTail(nullptr)
 	, particleVerts(nullptr)
 	, particleVerts(nullptr)
-	, image(p.image)
+	, texture(p.texture)
 	, active(p.active)
 	, active(p.active)
 	, insertMode(p.insertMode)
 	, insertMode(p.insertMode)
 	, maxParticles(p.maxParticles)
 	, maxParticles(p.maxParticles)
@@ -147,14 +147,14 @@ ParticleSystem::ParticleSystem(const ParticleSystem &p)
 {
 {
 	setBufferSize(maxParticles);
 	setBufferSize(maxParticles);
 
 
-	if (image != nullptr)
-		image->retain();
+	if (texture != nullptr)
+		texture->retain();
 }
 }
 
 
 ParticleSystem::~ParticleSystem()
 ParticleSystem::~ParticleSystem()
 {
 {
-	if (image != nullptr)
-		image->release();
+	if (texture != nullptr)
+		texture->release();
 
 
 	deleteBuffers();
 	deleteBuffers();
 }
 }
@@ -403,17 +403,19 @@ ParticleSystem::particle *ParticleSystem::removeParticle(particle *p)
 	return pNext;
 	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)
 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
 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();
 	uint32 pCount = getCount();
-	if (pCount == 0 || image == nullptr || pMem == nullptr || particleVerts == nullptr)
+	if (pCount == 0 || texture == nullptr || pMem == nullptr || particleVerts == nullptr)
 		return;
 		return;
 
 
 	Color curcolor = gl.getColor();
 	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);
 	t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
 	glMultMatrixf((const GLfloat *)t.getElements());
 	glMultMatrixf((const GLfloat *)t.getElements());
 
 
-	const Vertex *imageVerts = image->getVertices();
+	const Vertex *textureVerts = texture->getVertices();
 	Vertex *pVerts = particleVerts;
 	Vertex *pVerts = particleVerts;
 	particle *p = pHead;
 	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
 		// 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.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
 		// set the texture coordinate and color data for particle vertices
 		for (int v = 0; v < 4; v++)
 		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)
 			// 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);
 			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;
 		p = p->next;
 	}
 	}
 
 
-	image->predraw();
+	texture->predraw();
 
 
 	glEnableClientState(GL_COLOR_ARRAY);
 	glEnableClientState(GL_COLOR_ARRAY);
 	glEnableClientState(GL_VERTEX_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);
 	glVertexPointer(2, GL_FLOAT, sizeof(Vertex), (GLvoid *) &particleVerts[0].x);
 	glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), (GLvoid *) &particleVerts[0].s);
 	glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), (GLvoid *) &particleVerts[0].s);
 
 
+	gl.prepareDraw();
 	glDrawArrays(GL_QUADS, 0, pCount * 4);
 	glDrawArrays(GL_QUADS, 0, pCount * 4);
 
 
 	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
 	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
 	glDisableClientState(GL_VERTEX_ARRAY);
 	glDisableClientState(GL_VERTEX_ARRAY);
 	glDisableClientState(GL_COLOR_ARRAY);
 	glDisableClientState(GL_COLOR_ARRAY);
 
 
-	image->postdraw();
+	texture->postdraw();
 
 
 	glPopMatrix();
 	glPopMatrix();
 
 

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

@@ -27,7 +27,7 @@
 #include "common/Vector.h"
 #include "common/Vector.h"
 #include "graphics/Drawable.h"
 #include "graphics/Drawable.h"
 #include "graphics/Color.h"
 #include "graphics/Color.h"
-#include "Image.h"
+#include "Texture.h"
 
 
 // STL
 // STL
 #include <vector>
 #include <vector>
@@ -76,9 +76,9 @@ public:
 	static const uint32 MAX_PARTICLES = LOVE_INT32_MAX / 4;
 	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);
 	ParticleSystem(const ParticleSystem &p);
 
 
 	/**
 	/**
@@ -94,15 +94,15 @@ public:
 	ParticleSystem *clone();
 	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.
 	 * 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
 	// array of transformed vertex data for all particles, for drawing
 	Vertex *particleVerts;
 	Vertex *particleVerts;
 
 
-	// The image to be drawn.
-	Image *image;
+	// The texture to be drawn.
+	Texture *texture;
 
 
 	// Whether the particle emitter is active.
 	// Whether the particle emitter is active.
 	bool active;
 	bool active;

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

@@ -327,6 +327,8 @@ Polyline::~Polyline()
 
 
 void Polyline::draw()
 void Polyline::draw()
 {
 {
+	gl.prepareDraw();
+
 	// draw the core line
 	// draw the core line
 	gl.bindTexture(0);
 	gl.bindTexture(0);
 	glEnableClientState(GL_VERTEX_ARRAY);
 	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.
  * 3. This notice may not be removed or altered from any source distribution.
  **/
  **/
 
 
+// LOVE
 #include "common/config.h"
 #include "common/config.h"
 
 
 #include "Shader.h"
 #include "Shader.h"
-#include "Graphics.h"
+#include "Canvas.h"
 
 
+// C++
 #include <algorithm>
 #include <algorithm>
 
 
 namespace love
 namespace love
@@ -47,7 +49,7 @@ namespace
 
 
 		~TemporaryAttacher()
 		~TemporaryAttacher()
 		{
 		{
-			if (prevShader != NULL)
+			if (prevShader != nullptr)
 				prevShader->attach();
 				prevShader->attach();
 			else
 			else
 				curShader->detach();
 				curShader->detach();
@@ -59,28 +61,30 @@ namespace
 } // anonymous namespace
 } // anonymous namespace
 
 
 
 
-Shader *Shader::current = NULL;
+Shader *Shader::current = nullptr;
 
 
-GLint Shader::maxTextureUnits = 0;
+GLint Shader::maxTexUnits = 0;
 std::vector<int> Shader::textureCounters;
 std::vector<int> Shader::textureCounters;
 
 
 Shader::Shader(const ShaderSources &sources)
 Shader::Shader(const ShaderSources &sources)
 	: shaderSources(sources)
 	: shaderSources(sources)
 	, program(0)
 	, program(0)
+	, builtinUniforms()
+	, lastCanvas((Canvas *) -1)
 {
 {
 	if (shaderSources.empty())
 	if (shaderSources.empty())
 		throw love::Exception("Cannot create shader: no source code!");
 		throw love::Exception("Cannot create shader: no source code!");
 
 
-	if (maxTextureUnits <= 0)
+	if (maxTexUnits <= 0)
 	{
 	{
 		GLint maxtexunits;
 		GLint maxtexunits;
 		glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &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
 	// 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
 	// load shader source and create program object
 	loadVolatile();
 	loadVolatile();
@@ -147,7 +151,7 @@ GLuint Shader::compileCode(ShaderType type, const std::string &code)
 	glGetShaderiv(shaderid, GL_INFO_LOG_LENGTH, &infologlen);
 	glGetShaderiv(shaderid, GL_INFO_LOG_LENGTH, &infologlen);
 
 
 	GLchar *infolog = new GLchar[infologlen + 1];
 	GLchar *infolog = new GLchar[infologlen + 1];
-	glGetShaderInfoLog(shaderid, infologlen, NULL, infolog);
+	glGetShaderInfoLog(shaderid, infologlen, nullptr, infolog);
 
 
 	// Save any warnings for later querying.
 	// Save any warnings for later querying.
 	if (infologlen > 0)
 	if (infologlen > 0)
@@ -232,6 +236,11 @@ void Shader::mapActiveUniforms()
 				u.name.erase(u.name.length() - 3);
 				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)
 		if (u.location != -1)
 			uniforms[u.name] = u;
 			uniforms[u.name] = u;
 	}
 	}
@@ -240,8 +249,12 @@ void Shader::mapActiveUniforms()
 bool Shader::loadVolatile()
 bool Shader::loadVolatile()
 {
 {
 	// zero out active texture list
 	// 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;
 	std::vector<GLuint> shaderids;
 
 
@@ -263,7 +276,7 @@ bool Shader::loadVolatile()
 	if (current == this)
 	if (current == this)
 	{
 	{
 		// make sure glUseProgram gets called.
 		// make sure glUseProgram gets called.
-		current = NULL;
+		current = nullptr;
 		attach();
 		attach();
 	}
 	}
 
 
@@ -282,19 +295,23 @@ void Shader::unloadVolatile()
 	}
 	}
 
 
 	// decrement global texture id counters for texture units which had textures bound from this shader
 	// 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);
 			textureCounters[i] = std::max(textureCounters[i] - 1, 0);
 	}
 	}
 
 
 	// active texture list is probably invalid, clear it
 	// 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
 	// same with uniform location list
 	uniforms.clear();
 	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();
 	shaderWarnings.clear();
 }
 }
 
 
@@ -337,7 +354,7 @@ void Shader::attach(bool temporary)
 {
 {
 	if (current != this)
 	if (current != this)
 	{
 	{
-		if (current != NULL)
+		if (current != nullptr)
 			current->release();
 			current->release();
 
 
 		glUseProgram(program);
 		glUseProgram(program);
@@ -350,10 +367,10 @@ void Shader::attach(bool temporary)
 	{
 	{
 		// make sure all sent textures are properly bound to their respective texture units
 		// make sure all sent textures are properly bound to their respective texture units
 		// note: list potentially contains texture ids of deleted/invalid textures!
 		// 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.
 		// We always want to use texture unit 0 for everyhing else.
@@ -363,10 +380,10 @@ void Shader::attach(bool temporary)
 
 
 void Shader::detach()
 void Shader::detach()
 {
 {
-	if (current != NULL)
+	if (current != nullptr)
 		glUseProgram(0);
 		glUseProgram(0);
 
 
-	current = NULL;
+	current = nullptr;
 }
 }
 
 
 const Shader::Uniform &Shader::getUniform(const std::string &name) const
 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);
 	TemporaryAttacher attacher(this);
 
 
-	int textureunit = getTextureUnit(name);
+	int texunit = getTextureUnit(name);
 
 
 	const Uniform &u = getUniform(name);
 	const Uniform &u = getUniform(name);
 	checkSetUniformError(u, 1, 1, UNIFORM_SAMPLER);
 	checkSetUniformError(u, 1, 1, UNIFORM_SAMPLER);
 
 
 	// bind texture to assigned texture unit and send uniform to shader program
 	// 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
 	// reset texture unit
 	gl.setTextureUnit(0);
 	gl.setTextureUnit(0);
 
 
 	// increment global shader texture id counter for this texture unit, if we haven't already
 	// 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
 	// 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);
 	auto it = boundRetainables.find(name);
 	if (it != boundRetainables.end())
 	if (it != boundRetainables.end())
 		it->second->release();
 		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)
 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;
 		return it->second;
 
 
-	int textureunit = 1;
+	int texunit = 1;
 
 
 	// prefer texture units which are unused by all other shaders
 	// prefer texture units which are unused by all other shaders
 	auto freeunit_it = std::find(textureCounters.begin(), textureCounters.end(), 0);
 	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())
 	if (freeunit_it != textureCounters.end())
 	{
 	{
 		// we don't want to use unit 0
 		// 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
 	else
 	{
 	{
 		// no completely unused texture units exist, try to use next free slot in our own list
 		// 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.");
 			throw love::Exception("No more texture units available for shader.");
 
 
 		// we don't want to use unit 0
 		// 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()
 std::string Shader::getGLSLVersion()
 {
 {
-	const char *tmp = 0;
+	const char *tmp = nullptr;
 
 
 	// GL_SHADING_LANGUAGE_VERSION isn't available in OpenGL < 2.0.
 	// GL_SHADING_LANGUAGE_VERSION isn't available in OpenGL < 2.0.
 	if (GLEE_VERSION_2_0 || GLEE_ARB_shading_language_100)
 	if (GLEE_VERSION_2_0 || GLEE_ARB_shading_language_100)
 		tmp = (const char *) glGetString(GL_SHADING_LANGUAGE_VERSION);
 		tmp = (const char *) glGetString(GL_SHADING_LANGUAGE_VERSION);
 
 
-	if (tmp == 0)
+	if (tmp == nullptr)
 		return "0.0";
 		return "0.0";
 
 
 	// the version string always begins with a version number of the format
 	// 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::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
 } // opengl
 } // graphics
 } // graphics
 } // love
 } // love

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

@@ -25,8 +25,7 @@
 #include "common/Object.h"
 #include "common/Object.h"
 #include "common/StringMap.h"
 #include "common/StringMap.h"
 #include "OpenGL.h"
 #include "OpenGL.h"
-#include "Image.h"
-#include "Canvas.h"
+#include "Texture.h"
 
 
 // STL
 // STL
 #include <string>
 #include <string>
@@ -39,6 +38,9 @@ namespace graphics
 {
 {
 namespace opengl
 namespace opengl
 {
 {
+
+class Canvas;
+
 // A GLSL shader
 // A GLSL shader
 class Shader : public Object, public Volatile
 class Shader : public Object, public Volatile
 {
 {
@@ -54,6 +56,13 @@ public:
 		TYPE_MAX_ENUM
 		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
 	// Type for a list of shader source codes in the form of sources[shadertype] = code
 	typedef std::map<ShaderType, std::string> ShaderSources;
 	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);
 	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.
 	 * @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 std::string getGLSLVersion();
 	static bool isSupported();
 	static bool isSupported();
@@ -172,8 +181,7 @@ private:
 
 
 	int getTextureUnit(const std::string &name);
 	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.
 	// Get any warnings or errors generated only by the shader program object.
 	std::string getProgramWarnings() const;
 	std::string getProgramWarnings() const;
@@ -187,24 +195,34 @@ private:
 	// volatile
 	// volatile
 	GLuint program;
 	GLuint program;
 
 
+	// Location values for any built-in uniform variables.
+	GLint builtinUniforms[BUILTIN_MAX_ENUM];
+
 	// Uniform location buffer map
 	// Uniform location buffer map
 	std::map<std::string, Uniform> uniforms;
 	std::map<std::string, Uniform> uniforms;
 
 
 	// Texture unit pool for setting images
 	// 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
 	// Uniform name to retainable objects
 	std::map<std::string, Object*> boundRetainables;
 	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
 	// 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
 	// Counts total number of textures bound to each texture unit in all shaders
 	static std::vector<int> textureCounters;
 	static std::vector<int> textureCounters;
 
 
 	static StringMap<ShaderType, TYPE_MAX_ENUM>::Entry typeNameEntries[];
 	static StringMap<ShaderType, TYPE_MAX_ENUM>::Entry typeNameEntries[];
 	static StringMap<ShaderType, TYPE_MAX_ENUM> typeNames;
 	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
 } // opengl

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

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

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

@@ -42,7 +42,7 @@ namespace opengl
 {
 {
 
 
 // Forward declarations.
 // Forward declarations.
-class Image;
+class Texture;
 class VertexBuffer;
 class VertexBuffer;
 class VertexIndex;
 class VertexIndex;
 
 
@@ -58,7 +58,7 @@ public:
 		USAGE_MAX_ENUM
 		USAGE_MAX_ENUM
 	};
 	};
 
 
-	SpriteBatch(Image *image, int size, int usage);
+	SpriteBatch(Texture *texture, int size, int usage);
 	virtual ~SpriteBatch();
 	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);
 	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 *lock();
 	void unlock();
 	void unlock();
 
 
-	void setImage(Image *newimage);
-	Image *getImage();
+	void setTexture(Texture *newtexture);
+	Texture *getTexture();
 
 
 	/**
 	/**
 	 * Set the current color for this SpriteBatch. The sprites added
 	 * Set the current color for this SpriteBatch. The sprites added
@@ -127,7 +127,7 @@ private:
 	 */
 	 */
 	void setColorv(Vertex *v, const Color &color);
 	void setColorv(Vertex *v, const Color &color);
 
 
-	Image *image;
+	Texture *texture;
 
 
 	// Max number of sprites in the batch.
 	// Max number of sprites in the batch.
 	int size;
 	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.
  * 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 love
 {
 {
 namespace graphics
 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
 } // graphics
 } // love
 } // 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.
  * 3. This notice may not be removed or altered from any source distribution.
  **/
  **/
 
 
-#include "Graphics.h"
 #include "wrap_Canvas.h"
 #include "wrap_Canvas.h"
 
 
 namespace love
 namespace love
@@ -84,14 +83,14 @@ int w_Canvas_setFilter(lua_State *L)
 {
 {
 	Canvas *canvas = luax_checkcanvas(L, 1);
 	Canvas *canvas = luax_checkcanvas(L, 1);
 
 
-	Image::Filter f;
+	Texture::Filter f;
 
 
 	const char *minstr = luaL_checkstring(L, 2);
 	const char *minstr = luaL_checkstring(L, 2);
 	const char *magstr = luaL_optstring(L, 3, minstr);
 	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);
 		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);
 		return luaL_error(L, "Invalid filter mode: %s", magstr);
 
 
 	f.anisotropy = (float) luaL_optnumber(L, 4, 1.0);
 	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)
 int w_Canvas_getFilter(lua_State *L)
 {
 {
 	Canvas *canvas = luax_checkcanvas(L, 1);
 	Canvas *canvas = luax_checkcanvas(L, 1);
-	const Image::Filter f = canvas->getFilter();
+	const Texture::Filter f = canvas->getFilter();
 
 
 	const char *minstr;
 	const char *minstr;
 	const char *magstr;
 	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, minstr);
 	lua_pushstring(L, magstr);
 	lua_pushstring(L, magstr);
@@ -123,14 +122,14 @@ int w_Canvas_setWrap(lua_State *L)
 {
 {
 	Canvas *canvas = luax_checkcanvas(L, 1);
 	Canvas *canvas = luax_checkcanvas(L, 1);
 
 
-	Image::Wrap w;
+	Texture::Wrap w;
 
 
 	const char *sstr = luaL_checkstring(L, 2);
 	const char *sstr = luaL_checkstring(L, 2);
 	const char *tstr = luaL_optstring(L, 3, sstr);
 	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);
 		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);
 		return luaL_error(L, "Invalid wrap mode, %s", tstr);
 
 
 	canvas->setWrap(w);
 	canvas->setWrap(w);
@@ -141,12 +140,12 @@ int w_Canvas_setWrap(lua_State *L)
 int w_Canvas_getWrap(lua_State *L)
 int w_Canvas_getWrap(lua_State *L)
 {
 {
 	Canvas *canvas = luax_checkcanvas(L, 1);
 	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_s;
 	const char *wrap_t;
 	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_s);
 	lua_pushstring(L, wrap_t);
 	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)
 int w_Font_setFilter(lua_State *L)
 {
 {
 	Font *t = luax_checkfont(L, 1);
 	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 *minstr = luaL_checkstring(L, 2);
 	const char *magstr = luaL_optstring(L, 3, minstr);
 	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);
 		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);
 		return luaL_error(L, "Invalid filter mode: %s", magstr);
 
 
 	f.anisotropy = (float) luaL_optnumber(L, 4, 1.0);
 	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)
 int w_Font_getFilter(lua_State *L)
 {
 {
 	Font *t = luax_checkfont(L, 1);
 	Font *t = luax_checkfont(L, 1);
-	const Image::Filter f = t->getFilter();
+	const Texture::Filter f = t->getFilter();
 	const char *minstr;
 	const char *minstr;
 	const char *magstr;
 	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, minstr);
 	lua_pushstring(L, magstr);
 	lua_pushstring(L, magstr);
 	lua_pushnumber(L, f.anisotropy);
 	lua_pushnumber(L, f.anisotropy);

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

@@ -20,7 +20,7 @@
 
 
 #include "wrap_Graphics.h"
 #include "wrap_Graphics.h"
 #include "OpenGL.h"
 #include "OpenGL.h"
-#include "graphics/DrawQable.h"
+#include "graphics/Texture.h"
 #include "image/ImageData.h"
 #include "image/ImageData.h"
 #include "font/Rasterizer.h"
 #include "font/Rasterizer.h"
 
 
@@ -34,7 +34,7 @@ namespace graphics
 namespace opengl
 namespace opengl
 {
 {
 
 
-static Graphics *instance = 0;
+static Graphics *instance = nullptr;
 
 
 int w_reset(lua_State *)
 int w_reset(lua_State *)
 {
 {
@@ -237,7 +237,7 @@ int w_newFont(lua_State *L)
 int w_newImageFont(lua_State *L)
 int w_newImageFont(lua_State *L)
 {
 {
 	// filter for glyphs
 	// filter for glyphs
-	Image::Filter filter = instance->getDefaultFilter();
+	Texture::Filter filter = instance->getDefaultFilter();
 
 
 	// Convert to ImageData if necessary.
 	// 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))
 	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)
 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);
 	int size = luaL_optint(L, 2, 1000);
 	SpriteBatch::UsageHint usage = SpriteBatch::USAGE_DYNAMIC;
 	SpriteBatch::UsageHint usage = SpriteBatch::USAGE_DYNAMIC;
 	if (lua_gettop(L) > 2)
 	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);
 			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);
 	luax_pushtype(L, "SpriteBatch", GRAPHICS_SPRITE_BATCH_T, t);
 	return 1;
 	return 1;
@@ -296,13 +296,13 @@ int w_newSpriteBatch(lua_State *L)
 
 
 int w_newParticleSystem(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);
 	lua_Number size = luaL_optnumber(L, 2, 1000);
 	ParticleSystem *t = 0;
 	ParticleSystem *t = 0;
 	if (size < 1.0 || size > ParticleSystem::MAX_PARTICLES)
 	if (size < 1.0 || size > ParticleSystem::MAX_PARTICLES)
 		return luaL_error(L, "Invalid ParticleSystem size");	
 		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);
 	luax_pushtype(L, "ParticleSystem", GRAPHICS_PARTICLE_SYSTEM_T, t);
 	return 1;
 	return 1;
@@ -434,10 +434,10 @@ int w_newMesh(lua_State *L)
 	// Check first argument: mandatory table of vertices.
 	// Check first argument: mandatory table of vertices.
 	luaL_checktype(L, 1, LUA_TTABLE);
 	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))
 	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.
 	// Third argument: optional draw mode.
 	const char *str = 0;
 	const char *str = 0;
@@ -485,11 +485,11 @@ int w_newMesh(lua_State *L)
 		vertices.push_back(v);
 		vertices.push_back(v);
 	}
 	}
 
 
-	Mesh *t = 0;
+	Mesh *t = nullptr;
 	EXCEPT_GUARD(t = instance->newMesh(vertices, mode);)
 	EXCEPT_GUARD(t = instance->newMesh(vertices, mode);)
 
 
-	if (img)
-		t->setImage(img);
+	if (tex)
+		t->setTexture(tex);
 
 
 	t->setVertexColors(use_colors);
 	t->setVertexColors(use_colors);
 
 
@@ -646,20 +646,20 @@ int w_getBlendMode(lua_State *L)
 
 
 int w_setDefaultFilter(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 *minstr = luaL_checkstring(L, 1);
 	const char *magstr = luaL_optstring(L, 2, minstr);
 	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);
 		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);
 		return luaL_error(L, "Invalid filter mode: %s", magstr);
 
 
 	float anisotropy = (float) luaL_optnumber(L, 3, 1.0);
 	float anisotropy = (float) luaL_optnumber(L, 3, 1.0);
 
 
-	Image::Filter f;
+	Texture::Filter f;
 	f.min = min;
 	f.min = min;
 	f.mag = mag;
 	f.mag = mag;
 	f.anisotropy = anisotropy;
 	f.anisotropy = anisotropy;
@@ -671,12 +671,12 @@ int w_setDefaultFilter(lua_State *L)
 
 
 int w_getDefaultFilter(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 *minstr;
 	const char *magstr;
 	const char *magstr;
-	if (!Image::getConstant(f.min, minstr))
+	if (!Texture::getConstant(f.min, minstr))
 		return luaL_error(L, "Unknown minification filter mode");
 		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");
 		return luaL_error(L, "Unknown magnification filter mode");
 	lua_pushstring(L, minstr);
 	lua_pushstring(L, minstr);
 	lua_pushstring(L, magstr);
 	lua_pushstring(L, magstr);
@@ -686,11 +686,11 @@ int w_getDefaultFilter(lua_State *L)
 
 
 int w_setDefaultMipmapFilter(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))
 	if (!lua_isnoneornil(L, 1))
 	{
 	{
 		const char *str = luaL_checkstring(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);
 			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)
 int w_getDefaultMipmapFilter(lua_State *L)
 {
 {
-	Image::FilterMode filter;
+	Texture::FilterMode filter;
 	float sharpness;
 	float sharpness;
 
 
 	instance->getDefaultMipmapFilter(&filter, &sharpness);
 	instance->getDefaultMipmapFilter(&filter, &sharpness);
 
 
 	const char *str;
 	const char *str;
-	if (Image::getConstant(filter, str))
+	if (Texture::getConstant(filter, str))
 		lua_pushstring(L, str);
 		lua_pushstring(L, str);
 	else
 	else
 		lua_pushnil(L);
 		lua_pushnil(L);
@@ -1004,14 +1004,14 @@ int w_getRendererInfo(lua_State *L)
 
 
 int w_draw(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;
 	int startidx = 2;
 
 
 	if (luax_istype(L, 2, GRAPHICS_QUAD_T))
 	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);
 		quad = luax_totype<Quad>(L, 2, "Quad", GRAPHICS_QUAD_T);
 		startidx = 3;
 		startidx = 3;
 	}
 	}
@@ -1035,8 +1035,8 @@ int w_draw(lua_State *L)
 	float kx = (float) luaL_optnumber(L, startidx + 7, 0.0);
 	float kx = (float) luaL_optnumber(L, startidx + 7, 0.0);
 	float ky = (float) luaL_optnumber(L, startidx + 8, 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)
 	else if (drawable)
 		drawable->draw(x, y, a, sx, sy, ox, oy, kx, ky);
 		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)
 int w_Image_setFilter(lua_State *L)
 {
 {
 	Image *t = luax_checkimage(L, 1);
 	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 *minstr = luaL_checkstring(L, 2);
 	const char *magstr = luaL_optstring(L, 3, minstr);
 	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);
 		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);
 		return luaL_error(L, "Invalid filter mode: %s", magstr);
 
 
 	f.anisotropy = (float) luaL_optnumber(L, 4, 1.0);
 	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)
 int w_Image_getFilter(lua_State *L)
 {
 {
 	Image *t = luax_checkimage(L, 1);
 	Image *t = luax_checkimage(L, 1);
-	const Image::Filter f = t->getFilter();
+	const Texture::Filter f = t->getFilter();
 	const char *minstr;
 	const char *minstr;
 	const char *magstr;
 	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, minstr);
 	lua_pushstring(L, magstr);
 	lua_pushstring(L, magstr);
 	lua_pushnumber(L, f.anisotropy);
 	lua_pushnumber(L, f.anisotropy);
@@ -91,14 +91,14 @@ int w_Image_getFilter(lua_State *L)
 int w_Image_setMipmapFilter(lua_State *L)
 int w_Image_setMipmapFilter(lua_State *L)
 {
 {
 	Image *t = luax_checkimage(L, 1);
 	Image *t = luax_checkimage(L, 1);
-	Image::Filter f = t->getFilter();
+	Texture::Filter f = t->getFilter();
 
 
 	if (lua_isnoneornil(L, 2))
 	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
 	else
 	{
 	{
 		const char *mipmapstr = luaL_checkstring(L, 2);
 		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);
 			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);
 	Image *t = luax_checkimage(L, 1);
 
 
-	const Image::Filter &f = t->getFilter();
+	const Texture::Filter &f = t->getFilter();
 
 
 	const char *mipmapstr;
 	const char *mipmapstr;
-	if (Image::getConstant(f.mipmap, mipmapstr))
+	if (Texture::getConstant(f.mipmap, mipmapstr))
 		lua_pushstring(L, mipmapstr);
 		lua_pushstring(L, mipmapstr);
 	else
 	else
 		lua_pushnil(L); // only return a mipmap filter if mipmapping is enabled
 		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 *i = luax_checkimage(L, 1);
 
 
-	Image::Wrap w;
+	Texture::Wrap w;
 
 
 	const char *sstr = luaL_checkstring(L, 2);
 	const char *sstr = luaL_checkstring(L, 2);
 	const char *tstr = luaL_optstring(L, 3, sstr);
 	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);
 		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);
 		return luaL_error(L, "Invalid wrap mode, %s", tstr);
 
 
 	i->setWrap(w);
 	i->setWrap(w);
-
 	return 0;
 	return 0;
 }
 }
 
 
 int w_Image_getWrap(lua_State *L)
 int w_Image_getWrap(lua_State *L)
 {
 {
 	Image *i = luax_checkimage(L, 1);
 	Image *i = luax_checkimage(L, 1);
-	const Image::Wrap w = i->getWrap();
+	const Texture::Wrap w = i->getWrap();
 	const char *sstr;
 	const char *sstr;
 	const char *tstr;
 	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, sstr);
 	lua_pushstring(L, tstr);
 	lua_pushstring(L, tstr);
 	return 2;
 	return 2;

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

@@ -20,7 +20,11 @@
 
 
 // LOVE
 // LOVE
 #include "wrap_Mesh.h"
 #include "wrap_Mesh.h"
-#include "wrap_Image.h"
+#include "Image.h"
+#include "Canvas.h"
+
+// C++
+#include <typeinfo>
 
 
 namespace love
 namespace love
 {
 {
@@ -231,31 +235,42 @@ int w_Mesh_getVertexMap(lua_State *L)
 	return 1;
 	return 1;
 }
 }
 
 
-int w_Mesh_setImage(lua_State *L)
+int w_Mesh_setTexture(lua_State *L)
 {
 {
 	Mesh *t = luax_checkmesh(L, 1);
 	Mesh *t = luax_checkmesh(L, 1);
 
 
 	if (lua_isnoneornil(L, 2))
 	if (lua_isnoneornil(L, 2))
-		t->setImage();
+		t->setTexture();
 	else
 	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;
 	return 0;
 }
 }
 
 
-int w_Mesh_getImage(lua_State *L)
+int w_Mesh_getTexture(lua_State *L)
 {
 {
 	Mesh *t = luax_checkmesh(L, 1);
 	Mesh *t = luax_checkmesh(L, 1);
-	Image *img = t->getImage();
+	Texture *tex = t->getTexture();
 
 
-	if (img == NULL)
+	if (tex == nullptr)
 		return 0;
 		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;
 	return 1;
 }
 }
 
 
@@ -322,14 +337,19 @@ static const luaL_Reg functions[] =
 	{ "getVertexCount", w_Mesh_getVertexCount },
 	{ "getVertexCount", w_Mesh_getVertexCount },
 	{ "setVertexMap", w_Mesh_setVertexMap },
 	{ "setVertexMap", w_Mesh_setVertexMap },
 	{ "getVertexMap", w_Mesh_getVertexMap },
 	{ "getVertexMap", w_Mesh_getVertexMap },
-	{ "setImage", w_Mesh_setImage },
-	{ "getImage", w_Mesh_getImage },
+	{ "setTexture", w_Mesh_setTexture },
+	{ "getTexture", w_Mesh_getTexture },
 	{ "setDrawMode", w_Mesh_setDrawMode },
 	{ "setDrawMode", w_Mesh_setDrawMode },
 	{ "getDrawMode", w_Mesh_getDrawMode },
 	{ "getDrawMode", w_Mesh_getDrawMode },
 	{ "setVertexColors", w_Mesh_setVertexColors },
 	{ "setVertexColors", w_Mesh_setVertexColors },
 	{ "hasVertexColors", w_Mesh_hasVertexColors },
 	{ "hasVertexColors", w_Mesh_hasVertexColors },
 	{ "setWireframe", w_Mesh_setWireframe },
 	{ "setWireframe", w_Mesh_setWireframe },
 	{ "isWireframe", w_Mesh_isWireframe },
 	{ "isWireframe", w_Mesh_isWireframe },
+
+	// Deprecated since 0.9.1.
+	{ "setImage", w_Mesh_setTexture },
+	{ "getImage", w_Mesh_getTexture },
+
 	{ 0, 0 }
 	{ 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_getVertexCount(lua_State *L);
 int w_Mesh_setVertexMap(lua_State *L);
 int w_Mesh_setVertexMap(lua_State *L);
 int w_Mesh_getVertexMap(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_setDrawMode(lua_State *L);
 int w_Mesh_getDrawMode(lua_State *L);
 int w_Mesh_getDrawMode(lua_State *L);
 int w_Mesh_setVertexColors(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.
  * 3. This notice may not be removed or altered from any source distribution.
  **/
  **/
 
 
+// LOVE
 #include "wrap_ParticleSystem.h"
 #include "wrap_ParticleSystem.h"
-
 #include "common/Vector.h"
 #include "common/Vector.h"
 
 
+#include "Image.h"
+#include "Canvas.h"
+
+// C
 #include <cstring>
 #include <cstring>
 
 
+// C++
+#include <typeinfo>
+
 namespace love
 namespace love
 {
 {
 namespace graphics
 namespace graphics
@@ -47,20 +54,31 @@ int w_ParticleSystem_clone(lua_State *L)
 	return 1;
 	return 1;
 }
 }
 
 
-int w_ParticleSystem_setImage(lua_State *L)
+int w_ParticleSystem_setTexture(lua_State *L)
 {
 {
 	ParticleSystem *t = luax_checkparticlesystem(L, 1);
 	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;
 	return 0;
 }
 }
 
 
-int w_ParticleSystem_getImage(lua_State *L)
+int w_ParticleSystem_getTexture(lua_State *L)
 {
 {
 	ParticleSystem *t = luax_checkparticlesystem(L, 1);
 	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;
 	return 1;
 }
 }
 
 
@@ -616,8 +634,8 @@ int w_ParticleSystem_update(lua_State *L)
 static const luaL_Reg functions[] =
 static const luaL_Reg functions[] =
 {
 {
 	{ "clone", w_ParticleSystem_clone },
 	{ "clone", w_ParticleSystem_clone },
-	{ "setImage", w_ParticleSystem_setImage },
-	{ "getImage", w_ParticleSystem_getImage },
+	{ "setTexture", w_ParticleSystem_setTexture },
+	{ "getTexture", w_ParticleSystem_getTexture },
 	{ "setBufferSize", w_ParticleSystem_setBufferSize },
 	{ "setBufferSize", w_ParticleSystem_setBufferSize },
 	{ "getBufferSize", w_ParticleSystem_getBufferSize },
 	{ "getBufferSize", w_ParticleSystem_getBufferSize },
 	{ "setInsertMode", w_ParticleSystem_setInsertMode },
 	{ "setInsertMode", w_ParticleSystem_setInsertMode },
@@ -668,6 +686,11 @@ static const luaL_Reg functions[] =
 	{ "isPaused", w_ParticleSystem_isPaused },
 	{ "isPaused", w_ParticleSystem_isPaused },
 	{ "isStopped", w_ParticleSystem_isStopped },
 	{ "isStopped", w_ParticleSystem_isStopped },
 	{ "update", w_ParticleSystem_update },
 	{ "update", w_ParticleSystem_update },
+
+	// Deprecated since 0.9.1.
+	{ "setImage", w_ParticleSystem_setTexture },
+	{ "getImage", w_ParticleSystem_getTexture },
+
 	{ 0, 0 }
 	{ 0, 0 }
 };
 };
 
 

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

@@ -23,7 +23,6 @@
 
 
 // LOVE
 // LOVE
 #include "common/runtime.h"
 #include "common/runtime.h"
-#include "wrap_Image.h"
 #include "ParticleSystem.h"
 #include "ParticleSystem.h"
 
 
 namespace love
 namespace love
@@ -35,8 +34,8 @@ namespace opengl
 
 
 ParticleSystem *luax_checkparticlesystem(lua_State *L, int idx);
 ParticleSystem *luax_checkparticlesystem(lua_State *L, int idx);
 int w_ParticleSystem_clone(lua_State *L);
 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_setBufferSize(lua_State *L);
 int w_ParticleSystem_getBufferSize(lua_State *L);
 int w_ParticleSystem_getBufferSize(lua_State *L);
 int w_ParticleSystem_setInsertMode(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;
 	return 0;
 }
 }
 
 
-int w_Shader_sendImage(lua_State *L)
+int w_Shader_sendTexture(lua_State *L)
 {
 {
 	Shader *shader = luax_checkshader(L, 1);
 	Shader *shader = luax_checkshader(L, 1);
 	const char *name = luaL_checkstring(L, 2);
 	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;
 	return 0;
 }
 }
 
 
@@ -318,7 +308,7 @@ static void w_convertMatrices(lua_State *L, int idx)
 int w_Shader_send(lua_State *L)
 int w_Shader_send(lua_State *L)
 {
 {
 	int ttype = lua_type(L, 3);
 	int ttype = lua_type(L, 3);
-	Proxy *p = 0;
+	Proxy *p = nullptr;
 
 
 	switch (ttype)
 	switch (ttype)
 	{
 	{
@@ -328,13 +318,11 @@ int w_Shader_send(lua_State *L)
 		return w_Shader_sendFloat(L);
 		return w_Shader_sendFloat(L);
 		break;
 		break;
 	case LUA_TUSERDATA:
 	case LUA_TUSERDATA:
-		// Image or Canvas.
+		// Texture (Image or Canvas).
 		p = (Proxy *) lua_touserdata(L, 3);
 		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;
 		break;
 	case LUA_TTABLE:
 	case LUA_TTABLE:
@@ -366,9 +354,13 @@ static const luaL_Reg functions[] =
 	{ "sendBoolean", w_Shader_sendInt },
 	{ "sendBoolean", w_Shader_sendInt },
 	{ "sendFloat",   w_Shader_sendFloat },
 	{ "sendFloat",   w_Shader_sendFloat },
 	{ "sendMatrix",  w_Shader_sendMatrix },
 	{ "sendMatrix",  w_Shader_sendMatrix },
-	{ "sendImage",   w_Shader_sendImage },
-	{ "sendCanvas",  w_Shader_sendCanvas },
+	{ "sendTexture", w_Shader_sendTexture },
 	{ "send",        w_Shader_send },
 	{ "send",        w_Shader_send },
+
+	// Deprecated since 0.9.1.
+	{ "sendImage",   w_Shader_sendTexture },
+	{ "sendCanvas",  w_Shader_sendTexture },
+
 	{ 0, 0 }
 	{ 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_sendInt(lua_State *L);
 int w_Shader_sendFloat(lua_State *L);
 int w_Shader_sendFloat(lua_State *L);
 int w_Shader_sendMatrix(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);
 int w_Shader_send(lua_State *L);
 extern "C" int luaopen_shader(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.
  * 3. This notice may not be removed or altered from any source distribution.
  **/
  **/
 
 
-#include "Image.h"
+// LOVE
 #include "wrap_SpriteBatch.h"
 #include "wrap_SpriteBatch.h"
+#include "Image.h"
+#include "Canvas.h"
+
+// C++
+#include <typeinfo>
 
 
 namespace love
 namespace love
 {
 {
@@ -36,7 +41,7 @@ SpriteBatch *luax_checkspritebatch(lua_State *L, int idx)
 int w_SpriteBatch_add(lua_State *L)
 int w_SpriteBatch_add(lua_State *L)
 {
 {
 	SpriteBatch *t = luax_checkspritebatch(L, 1);
 	SpriteBatch *t = luax_checkspritebatch(L, 1);
-	Quad *quad = 0;
+	Quad *quad = nullptr;
 	int startidx = 2;
 	int startidx = 2;
 
 
 	if (luax_istype(L, 2, GRAPHICS_QUAD_T))
 	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);
 	SpriteBatch *t = luax_checkspritebatch(L, 1);
 	int id = luaL_checkinteger(L, 2);
 	int id = luaL_checkinteger(L, 2);
 
 
-	Quad *quad = 0;
+	Quad *quad = nullptr;
 	int startidx = 3;
 	int startidx = 3;
 
 
 	if (luax_istype(L, 3, GRAPHICS_QUAD_T))
 	if (luax_istype(L, 3, GRAPHICS_QUAD_T))
@@ -126,20 +131,31 @@ int w_SpriteBatch_unbind(lua_State *L)
 	return 0;
 	return 0;
 }
 }
 
 
-int w_SpriteBatch_setImage(lua_State *L)
+int w_SpriteBatch_setTexture(lua_State *L)
 {
 {
 	SpriteBatch *t = luax_checkspritebatch(L, 1);
 	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;
 	return 0;
 }
 }
 
 
-int w_SpriteBatch_getImage(lua_State *L)
+int w_SpriteBatch_getTexture(lua_State *L)
 {
 {
 	SpriteBatch *t = luax_checkspritebatch(L, 1);
 	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;
 	return 1;
 }
 }
 
 
@@ -224,13 +240,17 @@ static const luaL_Reg functions[] =
 	{ "clear", w_SpriteBatch_clear },
 	{ "clear", w_SpriteBatch_clear },
 	{ "bind", w_SpriteBatch_bind },
 	{ "bind", w_SpriteBatch_bind },
 	{ "unbind", w_SpriteBatch_unbind },
 	{ "unbind", w_SpriteBatch_unbind },
-	{ "setImage", w_SpriteBatch_setImage },
-	{ "getImage", w_SpriteBatch_getImage },
+	{ "setTexture", w_SpriteBatch_setTexture },
+	{ "getTexture", w_SpriteBatch_getTexture },
 	{ "setColor", w_SpriteBatch_setColor },
 	{ "setColor", w_SpriteBatch_setColor },
 	{ "getColor", w_SpriteBatch_getColor },
 	{ "getColor", w_SpriteBatch_getColor },
 	{ "getCount", w_SpriteBatch_getCount },
 	{ "getCount", w_SpriteBatch_getCount },
 	{ "setBufferSize", w_SpriteBatch_setBufferSize },
 	{ "setBufferSize", w_SpriteBatch_setBufferSize },
 	{ "getBufferSize", w_SpriteBatch_getBufferSize },
 	{ "getBufferSize", w_SpriteBatch_getBufferSize },
+
+	// Deprecated since 0.9.1.
+	{ "setImage", w_SpriteBatch_setTexture },
+	{ "getImage", w_SpriteBatch_getTexture },
 	{ 0, 0 }
 	{ 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_clear(lua_State *L);
 int w_SpriteBatch_bind(lua_State *L);
 int w_SpriteBatch_bind(lua_State *L);
 int w_SpriteBatch_unbind(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_setColor(lua_State *L);
 int w_SpriteBatch_getColor(lua_State *L);
 int w_SpriteBatch_getColor(lua_State *L);
 int w_SpriteBatch_getCount(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 ProjectionMatrix gl_ProjectionMatrix
 #define TransformProjectionMatrix gl_ModelViewProjectionMatrix
 #define TransformProjectionMatrix gl_ModelViewProjectionMatrix
 #define NormalMatrix gl_NormalMatrix
 #define NormalMatrix gl_NormalMatrix
-uniform sampler2D _tex0_;]]
+uniform sampler2D _tex0_;
+uniform vec2 love_ScreenParams;]]
 
 
 	local GLSL_VERTEX = {
 	local GLSL_VERTEX = {
 		HEADER = [[
 		HEADER = [[
@@ -1335,14 +1336,22 @@ void main() {
 void main() {
 void main() {
 	// fix crashing issue in OSX when _tex0_ is unused within effect()
 	// fix crashing issue in OSX when _tex0_ is unused within effect()
 	float dummy = texture2D(_tex0_, vec2(.5)).r;
 	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 = [[
 		FOOTER_MULTI_CANVAS = [[
 void main() {
 void main() {
 	// fix crashing issue in OSX when _tex0_ is unused within effect()
 	// fix crashing issue in OSX when _tex0_ is unused within effect()
 	float dummy = texture2D(_tex0_, vec2(.5)).r;
 	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, 
 	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,
 	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, 
 	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, 
 	0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x47, 0x4c, 0x53, 0x4c, 0x5f, 0x56, 0x45, 0x52, 0x54, 0x45, 0x58, 
 	0x20, 0x3d, 0x20, 0x7b, 0x0a,
 	0x20, 0x3d, 0x20, 0x7b, 0x0a,
 	0x09, 0x09, 0x48, 0x45, 0x41, 0x44, 0x45, 0x52, 0x20, 0x3d, 0x20, 0x5b, 0x5b, 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, 
 	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, 
 	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,
 	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, 
 	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, 
 	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, 
 	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,
 	0x7d, 0x5d, 0x5d, 0x2c, 0x0a,
 	0x09, 0x09, 0x46, 0x4f, 0x4f, 0x54, 0x45, 0x52, 0x5f, 0x4d, 0x55, 0x4c, 0x54, 0x49, 0x5f, 0x43, 0x41, 0x4e, 
 	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,
 	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, 
 	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, 
 	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,
 	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, 
 	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, 
 	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,
 	0x7d, 0x5d, 0x5d, 0x2c, 0x0a,
 	0x09, 0x7d, 0x0a,
 	0x09, 0x7d, 0x0a,
 	0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x72, 
 	0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x72,