Browse Source

Added optional xoffset/yoffset/width/height arguments to Image:refresh, to specify a sub-rectangle. Specifying a small sub-rectangle can improve the performance of Image:refresh.

--HG--
branch : minor
Alex Szpakowski 11 years ago
parent
commit
8a7481eaa3

+ 72 - 55
src/modules/graphics/opengl/Image.cpp

@@ -20,6 +20,8 @@
 
 #include "Image.h"
 
+#include "common/int.h"
+
 // STD
 #include <cstring> // For memcpy
 #include <algorithm> // for min/max
@@ -218,7 +220,24 @@ void Image::unload()
 	return unloadVolatile();
 }
 
-void Image::uploadCompressedData()
+void Image::generateMipmaps()
+{
+	// The GL_GENERATE_MIPMAP texparameter is set in loadVolatile if we don't
+	// have support for glGenerateMipmap.
+	if (flags.mipmaps && !isCompressed() &&
+		(GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object))
+	{
+		// Driver bug: http://www.opengl.org/wiki/Common_Mistakes#Automatic_mipmap_generation
+#if defined(LOVE_WINDOWS) || defined(LOVE_LINUX)
+		if (gl.getVendor() == OpenGL::VENDOR_ATI_AMD)
+			glEnable(GL_TEXTURE_2D);
+#endif
+
+		glGenerateMipmap(GL_TEXTURE_2D);
+	}
+}
+
+void Image::loadTextureFromCompressedData()
 {
 	GLenum iformat = getCompressedFormat(cdata->getFormat());
 	int count = flags.mipmaps ? cdata->getMipmapCount() : 1;
@@ -228,62 +247,23 @@ void Image::uploadCompressedData()
 
 	for (int i = 0; i < count; i++)
 	{
-		glCompressedTexImage2D(GL_TEXTURE_2D,
-		                       i,
-		                       iformat,
-		                       cdata->getWidth(i),
-		                       cdata->getHeight(i),
-		                       0,
-		                       (GLsizei) cdata->getSize(i),
-		                       cdata->getData(i));
+		glCompressedTexImage2D(GL_TEXTURE_2D, i, iformat,
+		                       cdata->getWidth(i), cdata->getHeight(i), 0,
+		                       (GLsizei) cdata->getSize(i), cdata->getData(i));
 	}
 }
 
-void Image::uploadImageData()
+void Image::loadTextureFromImageData()
 {
 	GLenum iformat = flags.sRGB ? GL_SRGB8_ALPHA8 : GL_RGBA8;
 
-	if (flags.mipmaps && !(GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object))
-		glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
-
 	{
 		love::thread::Lock lock(data->getMutex());
-		glTexImage2D(GL_TEXTURE_2D,
-		             0,
-		             iformat,
-		             width,
-		             height,
-		             0,
-		             GL_RGBA,
-		             GL_UNSIGNED_BYTE,
-		             data->getData());
-	}
-
-	if (flags.mipmaps)
-	{
-		if (GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object)
-		{
-			// Driver bug: http://www.opengl.org/wiki/Common_Mistakes#Automatic_mipmap_generation
-#if defined(LOVE_WINDOWS) || defined(LOVE_LINUX)
-			if (gl.getVendor() == OpenGL::VENDOR_ATI_AMD)
-				glEnable(GL_TEXTURE_2D);
-#endif
-
-			glGenerateMipmap(GL_TEXTURE_2D);
-		}
-		else
-			glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_FALSE);
+		glTexImage2D(GL_TEXTURE_2D, 0, iformat, width, height, 0, GL_RGBA,
+		             GL_UNSIGNED_BYTE, data->getData());
 	}
-}
-
-void Image::uploadTexture()
-{
-	bind();
 
-	if (isCompressed() && cdata.get())
-		uploadCompressedData();
-	else if (data.get())
-		uploadImageData();
+	generateMipmaps();
 }
 
 bool Image::loadVolatile()
@@ -313,21 +293,32 @@ bool Image::loadVolatile()
 	gl.setTextureWrap(wrap);
 	setMipmapSharpness(mipmapSharpness);
 
-	if (!flags.mipmaps)
-		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
-
 	// Use a default texture if the size is too big for the system.
 	if (width > gl.getMaxTextureSize() || height > gl.getMaxTextureSize())
 	{
-		uploadDefaultTexture();
+		loadDefaultTexture();
 		return true;
 	}
 
+	if (!flags.mipmaps)
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
+
+	if (flags.mipmaps && !isCompressed() &&
+		!(GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object))
+	{
+		// Auto-generate mipmaps every time the texture is modified, if
+		// glGenerateMipmap isn't supported.
+		glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
+	}
+
 	while (glGetError() != GL_NO_ERROR); // Clear errors.
 
 	try
 	{
-		uploadTexture();
+		if (isCompressed())
+			loadTextureFromCompressedData();
+		else
+			loadTextureFromImageData();
 
 		GLenum glerr = glGetError();
 		if (glerr != GL_NO_ERROR)
@@ -374,13 +365,39 @@ void Image::unloadVolatile()
 	}
 }
 
-bool Image::refresh()
+void Image::uploadImageData(int xoffset, int yoffset, int w, int h)
+{
+	const image::pixel *pdata = (const image::pixel *) data->getData();
+	pdata += yoffset * data->getWidth() + xoffset;
+
+	{
+		thread::Lock lock(data->getMutex());
+		glTexSubImage2D(GL_TEXTURE_2D, 0, xoffset, yoffset, w, h, GL_RGBA,
+		                GL_UNSIGNED_BYTE, pdata);
+	}
+
+	generateMipmaps();
+}
+
+bool Image::refresh(int xoffset, int yoffset, int w, int h)
 {
 	// No effect if the texture hasn't been created yet.
 	if (texture == 0 || usingDefaultTexture)
 		return false;
 
-	uploadTexture();
+	if (xoffset < 0 || yoffset < 0 || w <= 0 || h <= 0 ||
+		(xoffset + w) > width || (yoffset + h) > height)
+	{
+		throw love::Exception("Invalid rectangle dimensions.");
+	}
+
+	bind();
+
+	if (isCompressed())
+		loadTextureFromCompressedData();
+	else
+		uploadImageData(xoffset, yoffset, w, h);
+
 	return true;
 }
 
@@ -389,7 +406,7 @@ const Image::Flags &Image::getFlags() const
 	return flags;
 }
 
-void Image::uploadDefaultTexture()
+void Image::loadDefaultTexture()
 {
 	usingDefaultTexture = true;
 

+ 11 - 11
src/modules/graphics/opengl/Image.h

@@ -133,7 +133,7 @@ public:
 	 * Re-uploads the ImageData or CompressedData associated with this Image to
 	 * the GPU.
 	 **/
-	bool refresh();
+	bool refresh(int xoffset, int yoffset, int w, int h);
 
 	const Flags &getFlags() const;
 
@@ -153,10 +153,18 @@ public:
 
 private:
 
-	void uploadDefaultTexture();
-
 	void drawv(const Matrix &t, const Vertex *v);
 
+	void preload();
+
+	void generateMipmaps();
+	void loadDefaultTexture();
+	void loadTextureFromCompressedData();
+	void loadTextureFromImageData();
+	void uploadImageData(int xoffset, int yoffset, int w, int h);
+
+	GLenum getCompressedFormat(image::CompressedData::Format cformat) const;
+
 	// The ImageData from which the texture is created. May be null if
 	// Compressed image data was used to create the texture.
 	Object::StrongRef<love::image::ImageData> data;
@@ -183,19 +191,11 @@ private:
 
 	size_t textureMemorySize;
 
-	void preload();
-
-	void uploadCompressedData();
-	void uploadImageData();
-	void uploadTexture();
-
 	static float maxMipmapSharpness;
 
 	static FilterMode defaultMipmapFilter;
 	static float defaultMipmapSharpness;
 
-	GLenum getCompressedFormat(image::CompressedData::Format cformat) const;
-
 	static StringMap<FlagType, FLAG_TYPE_MAX_ENUM>::Entry flagNameEntries[];
 	static StringMap<FlagType, FLAG_TYPE_MAX_ENUM> flagNames;
 

+ 7 - 1
src/modules/graphics/opengl/wrap_Image.cpp

@@ -79,7 +79,13 @@ int w_Image_isCompressed(lua_State *L)
 int w_Image_refresh(lua_State *L)
 {
 	Image *i = luax_checkimage(L, 1);
-	luax_catchexcept(L, [&](){ i->refresh(); });
+
+	int xoffset = luaL_optint(L, 2, 0);
+	int yoffset = luaL_optint(L, 3, 0);
+	int w = luaL_optint(L, 4, i->getWidth());
+	int h = luaL_optint(L, 5, i->getHeight());
+
+	luax_catchexcept(L, [&](){ i->refresh(xoffset, yoffset, w, h); });
 	return 0;
 }