Browse Source

Manage our own ImageData, thereby bypassing DevILs thread-safety issues (bug #200)

Bart van Strien 14 years ago
parent
commit
176fb7d2f5

+ 2 - 2
src/modules/image/ImageData.h

@@ -107,7 +107,7 @@ namespace image
 		* @param y The location along the y-axis.
 		* @return The color for the given location.
 		**/
-		virtual pixel getPixel(int x, int y) const = 0;
+		virtual pixel getPixel(int x, int y) = 0;
 		
 		/**
 		 * Encodes raw pixel data into a given format.
@@ -125,4 +125,4 @@ namespace image
 } // image
 } // love
 
-#endif // LOVE_IMAGE_IMAGE_DATA_H
+#endif // LOVE_IMAGE_IMAGE_DATA_H

+ 96 - 10
src/modules/image/devil/ImageData.cpp

@@ -29,6 +29,10 @@
 #include <common/math.h>
 #include <filesystem/File.h>
 
+using love::thread::Lock;
+
+static Mutex devilMutex;
+
 namespace love
 {
 namespace image
@@ -37,6 +41,9 @@ namespace devil
 {
 	void ImageData::create(int width, int height, void * data)
 	{
+		Lock lock(devilMutex); //automatically lock and unlock
+		ILuint image;
+
 		//create the image
 		ilGenImages(1, &image);
 
@@ -66,10 +73,24 @@ namespace devil
 			}
 			throw love::Exception("Could not decode image data.");
 		}
+
+		try {
+                        this->data = new unsigned char[width*height*bpp];
+                } catch(std::bad_alloc) {
+                        ilDeleteImages(1, &image);
+                        throw love::Exception("Out of memory");
+                }
+ 
+                memcpy(this->data, ilGetData(), width*height*bpp);
+ 
+                ilDeleteImages(1, &image);
 	}
 
 	void ImageData::load(Data * data)
 	{
+		Lock lock(devilMutex);
+		ILuint image;
+
 		// Generate DevIL image.
 		ilGenImages(1, &image);
 
@@ -101,6 +122,20 @@ namespace devil
 			std::cerr << "Bits per pixel != 4" << std::endl;
 			return;
 		}
+
+		try
+		{
+                        this->data = new unsigned char[width*height*bpp];
+                }
+		catch(std::bad_alloc)
+		{
+                        ilDeleteImages(1, &image);
+                        throw love::Exception("Out of memory");
+                }
+ 
+                memcpy(this->data, ilGetData(), width*height*bpp);
+ 
+                ilDeleteImages(1, &image);
 	}
 
 	ImageData::ImageData(Data * data)
@@ -121,7 +156,7 @@ namespace devil
 		create(width, height);
 
 		// Set to black.
-		memset((void*)ilGetData(), 0, width*height*4);
+		memset(data, 0, width*height*4);
 	}
 
 	ImageData::ImageData(int width, int height, void *data)
@@ -132,7 +167,7 @@ namespace devil
 
 	ImageData::~ImageData()
 	{
-		ilDeleteImages(1, &image);
+		delete[] data;
 	}
 
 	int ImageData::getWidth() const
@@ -147,8 +182,7 @@ namespace devil
 
 	void * ImageData::getData() const
 	{
-		ilBindImage(image);
-		return ilGetData();
+		return data;
 	}
 
 	int ImageData::getSize() const
@@ -158,6 +192,7 @@ namespace devil
 
 	void ImageData::setPixel(int x, int y, pixel c)
 	{
+		Lock lock(mutex);
 		//int tx = x > width-1 ? width-1 : x;
 		//int ty = y > height-1 ? height-1 : y; // not using these seems to not break anything
 		if (x > width-1 || y > height-1 || x < 0 || y < 0) throw love::Exception("Attempt to set out-of-range pixel!");
@@ -165,8 +200,9 @@ namespace devil
 		pixels[y*width+x] = c;
 	}
 
-	pixel ImageData::getPixel(int x, int y) const
+	pixel ImageData::getPixel(int x, int y)
 	{
+		Lock lock(mutex);
 		//int tx = x > width-1 ? width-1 : x;
 		//int ty = y > height-1 ? height-1 : y; // not using these seems to not break anything
 		if (x > width-1 || y > height-1 || x < 0 || y < 0) throw love::Exception("Attempt to get out-of-range pixel!");
@@ -175,7 +211,41 @@ namespace devil
 	}
 
 	void ImageData::encode(love::filesystem::File * f, ImageData::Format format) {
-		ilBindImage(image);
+		Lock lock(devilMutex);
+		Lock lock2(mutex);
+
+		ILuint tempimage;
+		ilGenImages(1, &tempimage);
+		ilBindImage(tempimage);
+
+		while(ilGetError() != IL_NO_ERROR);
+
+		bool success = ilTexImage(width, height, 1, bpp, IL_RGBA, IL_UNSIGNED_BYTE, this->data) == IL_TRUE;
+
+		ILenum err = ilGetError();
+		while(ilGetError() != IL_NO_ERROR);
+
+		if(!success) {
+			ilDeleteImages(1, &tempimage);
+
+			if(err != IL_NO_ERROR) {
+				switch (err) {
+					case IL_ILLEGAL_OPERATION:
+						throw love::Exception("Illegal operation");
+					case IL_INVALID_PARAM:
+						throw love::Exception("Invalid parameters");
+					case IL_OUT_OF_MEMORY:
+						throw love::Exception("Out of memory");
+					default:
+						throw love::Exception("Unknown error (%d)", (int) err);
+				}
+			}
+
+			throw love::Exception("Could not create image for the encoding!");
+		}
+
+		ilRegisterOrigin(IL_ORIGIN_UPPER_LEFT);
+
 		ILuint ilFormat;
 		switch (format) {
 			case ImageData::FORMAT_BMP:
@@ -195,13 +265,29 @@ namespace devil
 				ilFormat = IL_PNG;
 				break;
 		}
+
 		ILuint size = ilSaveL(ilFormat, NULL, 0);
-		ILubyte * data = new ILubyte[size];
-		ilSaveL(ilFormat, data, size);
+		if(!size) {
+			ilDeleteImages(1, &tempimage);
+			throw love::Exception("Could not encode image!");
+		}
+
+		ILubyte * encoded_data;
+		try {
+			encoded_data = new ILubyte[size];
+		} catch(std::bad_alloc) {
+			ilDeleteImages(1, &tempimage);
+			throw love::Exception("Out of memory");
+		}
+
+		ilSaveL(ilFormat, encoded_data, size);
+		ilDeleteImages(1, &tempimage);
+
 		f->open(love::filesystem::File::WRITE);
-		f->write(data, size);
+		f->write(encoded_data, size);
 		f->close();
-		delete[] data;
+
+		delete[] encoded_data;
 	}
 
 } // devil

+ 11 - 3
src/modules/image/devil/ImageData.h

@@ -24,10 +24,13 @@
 // LOVE
 #include <filesystem/File.h>
 #include <image/ImageData.h>
+#include <thread/threads.h>
 
 // DevIL
 #include <IL/il.h>
 
+using love::thread::Mutex;
+
 namespace love
 {
 namespace image
@@ -50,14 +53,19 @@ namespace devil
 		// The bits per pixel.
 		int bpp;
 
-		// DevIL image identifier.
-		ILuint image;
+		// The actual data
+		unsigned char *data;
 
 		// Create imagedata.
 		void create(int width, int height, void * data = 0);
 
 		void load(Data * data);
 
+		// We need to be thread-safe
+		// so we lock when we're accessing our
+		// data
+		Mutex mutex;
+
 	public:
 
 		ImageData(Data * data);
@@ -74,7 +82,7 @@ namespace devil
 		int getWidth() const ;
 		int getHeight() const ;
 		void setPixel(int x, int y, pixel c);
-		pixel getPixel(int x, int y) const;
+		pixel getPixel(int x, int y);
 		void encode(love::filesystem::File * f, Format format);
 
 	}; // ImageData