Example #1
0
void Texture :: configure(AlloArrayHeader& header) {
	switch (header.dimcount) {
		case 1: target(TEXTURE_1D); break;
		case 2: target(TEXTURE_2D); break;
		case 3: target(TEXTURE_3D); break;
		default:
			AL_WARN("invalid array dimensions for texture");
			return;
	}
	
	switch (header.dimcount) {
		case 3:	depth(header.dim[2]);
		case 2:	height(header.dim[1]);
		case 1:	width(header.dim[0]); break;
	}

	switch (header.components) {
		case 1:	format(Graphics::LUMINANCE); break; // alpha or luminance?
		case 2:	format(Graphics::LUMINANCE_ALPHA); break;
		case 3:	format(Graphics::RGB); break;
		case 4:	format(Graphics::RGBA); break;
		default:
			AL_WARN("invalid array component count for texture");
			return;
	}
	
	switch (header.type) {
		case AlloUInt8Ty:	type(Graphics::UBYTE); break; 
		case AlloSInt8Ty:	type(Graphics::BYTE); break; 
		case AlloUInt16Ty:	type(Graphics::SHORT); break; 
		case AlloSInt16Ty:	type(Graphics::USHORT); break; 
		case AlloUInt32Ty:	type(Graphics::INT); break; 
		case AlloSInt32Ty:	type(Graphics::UINT); break; 
		case AlloFloat32Ty:	type(Graphics::FLOAT); break; 
		case AlloFloat64Ty:	type(Graphics::DOUBLE); break; 
		default:
			AL_WARN("invalid array type for texture");
			return;
	}
	
	// reconfigure internal array to match:
	mArray.configure(header);
	
	mParamsUpdated = true; 
}
Example #2
0
ProtoApp& ProtoApp::resourceDir(const std::string& dir, bool searchBack){

	std::string modDir = dir;

	if(searchBack){
		if(!al::File::searchBack(modDir)){
			AL_WARN("Could not find %s", modDir.c_str());
			exit(0);
		}
	}
	mResourceDir = modDir;
	
	if(mResourceDir[mResourceDir.size()-1] != AL_FILE_DELIMITER){
		mResourceDir += AL_FILE_DELIMITER_STR;
	}
	
	return *this;
}
	int glob(const std::string& regex, FileList& result, bool recursive=true) {
		std::regex e(regex);
		if (dir && APR_SUCCESS == check_apr(apr_dir_open(&dir, dirname.c_str(), mPool))) {
			// iterate over directory:
			while (APR_SUCCESS == (apr_dir_read(&dirent, APR_FINFO_TYPE|APR_FINFO_NAME, dir))) {
				//printf("test %s %s\n", dirname.c_str(), dirent.name);
				if (dirent.filetype == APR_REG && dirent.name && std::regex_match(dirname+dirent.name,e) ) {
					FilePath res;
					res.file(dirent.name);
					res.path(dirname);
					result.add(res);
				} else if (recursive && dirent.filetype == APR_DIR && dirent.name && dirent.name[0] != '.') {
					Path path(dirname + dirent.name + AL_FILE_DELIMITER);
					path.glob(regex, result, true);
				}
			}
		} else {
			AL_WARN("couldn't open directory %s", dirname.c_str());
		}
		return result.count();
	}
	bool find(const std::string& name, FilePath& result, bool recursive=true) {
		bool found = false;
		if (dir && APR_SUCCESS == check_apr(apr_dir_open(&dir, dirname.c_str(), mPool))) {
			// iterate over directory:
			while ((!found) && APR_SUCCESS == (apr_dir_read(&dirent, APR_FINFO_TYPE|APR_FINFO_NAME, dir))) {
				//printf("test %s %s\n", dirname.c_str(), dirent.name);
				if (dirent.filetype == APR_REG && dirent.name && std::string(dirent.name) == name) {
					result.file(dirent.name);
					result.path(dirname);
					found = true;
					break;
				} else if (recursive && dirent.filetype == APR_DIR && dirent.name && dirent.name[0] != '.') {
					Path path(dirname + dirent.name + AL_FILE_DELIMITER);
					found = path.find(name, result, true);
				}
			}
		} else {
			AL_WARN("couldn't open directory %s", dirname.c_str());
		}
		return found;
	}
	virtual bool load(const std::string& filename, Array &lat) {
		FREE_IMAGE_FORMAT type = FreeImage_GetFIFFromFilename(filename.c_str());
		if(type == FIF_UNKNOWN) {
			AL_WARN("image format not recognized: %s", filename.c_str());
			return false;
		}
		if(!FreeImage_FIFSupportsReading(type)) {
			AL_WARN("image format not supported: %s", filename.c_str());
			return false;
		}
		
		destroy();
		mImage = FreeImage_Load(type, filename.c_str(), 0);
		if (mImage == NULL) {
			AL_WARN("image failed to load: %s", filename.c_str());
			return false;
		}
		
		FREE_IMAGE_COLOR_TYPE colorType = FreeImage_GetColorType(mImage);
		switch(colorType) {
			case FIC_MINISBLACK:
			case FIC_MINISWHITE: {
					FIBITMAP *res = FreeImage_ConvertToGreyscale(mImage);
					FreeImage_Unload(mImage);
					mImage = res;
				}
				break;

			case FIC_PALETTE: {
					if(FreeImage_IsTransparent(mImage)) {
						FIBITMAP *res = FreeImage_ConvertTo32Bits(mImage);
						FreeImage_Unload(mImage);
						mImage = res;
					}
					else {
						FIBITMAP *res = FreeImage_ConvertTo24Bits(mImage);
						FreeImage_Unload(mImage);
						mImage = res;
					}
				}
				break;

			case FIC_CMYK: {
					AL_WARN("CMYK images currently not supported");
					return false;
				}
				break;

			default:
				break;
		}

		// flip vertical for OpenGL:
		//FreeImage_FlipVertical(mImage);
		
		//Freeimage is not tightly packed, so we use
		//a custom method instead of one of the Matrix
		//utility methods
		int planes = getPlanes();
		AlloTy ty = getDataType();
		int w, h;
		getDim(w, h);
		lat.format(planes, ty, w, h);
		
		Image::Format format = Image::getFormat(planes);
		switch(format) {
			case Image::LUMINANCE: {
				char *o_pix = (char *)(lat.data.ptr);
				int rowstride = lat.header.stride[1];
				for(unsigned j = 0; j < lat.header.dim[1]; ++j) {
					char *pix = (char *)FreeImage_GetScanLine(mImage, j);
					memcpy(o_pix, pix, rowstride);
					o_pix += rowstride;
				}
			}
			break;
			
			case Image::RGB: {
				switch(lat.header.type) {
					case AlloUInt8Ty: {
						char *bp = (char *)(lat.data.ptr);
						int rowstride = lat.header.stride[1];

						for(unsigned j = 0; j < lat.header.dim[1]; ++j) {
							RGBTRIPLE * pix = (RGBTRIPLE *)FreeImage_GetScanLine(mImage, j);
							Image::RGBPix<uint8_t> *o_pix = (Image::RGBPix<uint8_t> *)(bp + j*rowstride);
							for(unsigned i=0; i < lat.header.dim[0]; ++i) {
								o_pix->r = pix->rgbtRed;
								o_pix->g = pix->rgbtGreen;
								o_pix->b = pix->rgbtBlue;
								++pix;
								++o_pix;
							}
						}
					}
					break;

					case AlloFloat32Ty: {
						char *o_pix = (char *)(lat.data.ptr);
						int rowstride = lat.header.stride[1];

						for(unsigned j = 0; j < lat.header.dim[1]; ++j) {
							char *pix = (char *)FreeImage_GetScanLine(mImage, j);
							memcpy(o_pix, pix, rowstride);
							o_pix += rowstride;
						}
					}
					break;

					default: 
					break;

				}
			}
			break;
			
			case Image::RGBA: {
				switch(lat.header.type) {
					case AlloUInt8Ty: {
						char *bp = (char *)(lat.data.ptr);
						int rowstride = lat.header.stride[1];
						for(unsigned j = 0; j < lat.header.dim[1]; ++j) {
							RGBQUAD *pix = (RGBQUAD *)FreeImage_GetScanLine(mImage, j);
							Image::RGBAPix<uint8_t> *o_pix = (Image::RGBAPix<uint8_t> *)(bp + j*rowstride);
							for(unsigned i=0; i < lat.header.dim[0]; ++i) {
								o_pix->r = pix->rgbRed;
								o_pix->g = pix->rgbGreen;
								o_pix->b = pix->rgbBlue;
								o_pix->a = pix->rgbReserved;
								++pix;
								++o_pix;
							}
						}
					}
					break;

					case AlloFloat32Ty: {
						char *o_pix = (char *)(lat.data.ptr);
						int rowstride = lat.header.stride[1];
						for(unsigned j = 0; j < lat.header.dim[1]; ++j) {
							char *pix = (char *)FreeImage_GetScanLine(mImage, j);
							memcpy(o_pix, pix, rowstride);
							o_pix += rowstride;
						}
					}
					break;

					default: break;
				}
			}
			break;
			
			default: 
				AL_WARN("image data not understood");
				destroy();
				return false;
		}
		return true;
	}
	virtual bool save(const std::string& filename, const Array& lat) {
	
		// check existing image type
		FREE_IMAGE_FORMAT type = FreeImage_GetFIFFromFilename(filename.c_str());
		
		if(type == FIF_UNKNOWN) {
			AL_WARN("image format not recognized: %s", filename.c_str());
			return false;
		}
		if(!FreeImage_FIFSupportsWriting(type)) {
			AL_WARN("image format not supported: %s", filename.c_str());
			return false;
		}
		
		destroy();
		
		
		const AlloArrayHeader& header = lat.header;
		int w = header.dim[0];
		int h = (header.dimcount > 1) ? header.dim[1] : 1;
		Image::Format format = Image::getFormat(header.components);

		switch(format) {
			case Image::RGB: {
				switch(header.type) {

					case AlloUInt8Ty: {
						int bpp = header.stride[0]; 
						mImage = FreeImage_AllocateT(FIT_BITMAP, w, h, bpp*8);
						char *bp = (char *)(lat.data.ptr);
						int rowstride = header.stride[1]; 

						for(unsigned j = 0; j < header.dim[1]; ++j) {
							
							// copy Array row to image buffer
							/*memcpy(
								FreeImage_GetScanLine(mImage, j),
								bp + j*rowstride,
								w*3
							);*/

							RGBTRIPLE *pix = (RGBTRIPLE *)FreeImage_GetScanLine(mImage, j);
							Image::RGBPix<uint8_t> *o_pix = (Image::RGBPix<uint8_t> *)(bp + j*rowstride);
							for(unsigned i=0; i < header.dim[0]; ++i) {
								pix->rgbtRed = o_pix->r;
								pix->rgbtGreen = o_pix->g;
								pix->rgbtBlue = o_pix->b;
								++pix;
								++o_pix;
							}
						}
					}
					break;

					default:
						AL_WARN("input matrix type not supported");
						break;
				}
			}
			break;

			case Image::RGBA: {
				
				switch(header.type) {

					case AlloUInt8Ty: {
	
						int bpp = header.stride[0]; 
						mImage = FreeImage_AllocateT(FIT_BITMAP, w, h, bpp*8);
						
						char *bp = (char *)(lat.data.ptr);
						int rowstride = header.stride[1]; 
						
						for(unsigned j = 0; j < header.dim[1]; ++j) {

							// copy Array row to image buffer
							/*memcpy(
								FreeImage_GetScanLine(mImage, j),
								bp + j*rowstride,
								w*4
							);*/
							
							RGBQUAD *pix = (RGBQUAD *)FreeImage_GetScanLine(mImage, j);
							Image::RGBAPix<uint8_t> *o_pix = (Image::RGBAPix<uint8_t> *)(bp + j*rowstride);
							for(unsigned i=0; i < header.dim[0]; ++i) {
								pix->rgbRed = o_pix->r;
								pix->rgbGreen = o_pix->g;
								pix->rgbBlue = o_pix->b;
								pix->rgbReserved = o_pix->a;
								++pix;
								++o_pix;
							}
						}
					}
					break;

					default:
						AL_WARN("input matrix type not supported");
						return false;
				}
			}
			break;

			default: {
				AL_WARN("input matrix format not supported");
				return false;
			}
		}
		
		
		if (mImage == NULL) {
			AL_WARN("image could not be understood");
			return false;
		}
		
		
		return FreeImage_Save(type, mImage, filename.c_str(), 0);
	}
Example #7
0
void Texture :: submit(const void * pixels, uint32_t align) {
	Graphics::error(id(), "Texture::submit (initial)");
	validate();
	
	determineTarget();	// is this necessary? surely the target is already set!
	
	sendParams(false);
	
	glActiveTexture(GL_TEXTURE0);
	Graphics::error(id(), "Texture::submit (glActiveTexture)");
	glEnable(target());
	Graphics::error(id(), "Texture::submit (glEnable(texture target))");
	glBindTexture(target(), id());
	Graphics::error(id(), "Texture::submit (glBindTexture)");
	
	// set glPixelStore according to layout:
	glPixelStorei(GL_UNPACK_ALIGNMENT, mUnpack);
	Graphics::error(id(), "Texture::submit (glPixelStorei set)");
	
	// void glTexImage3D(
	//		GLenum target, GLint level, GLenum internalformat,
	//		GLsizei width, GLsizei height, GLsizei depth, 
	//		GLint border, GLenum format, GLenum type, const GLvoid *pixels
	// );
	
//	// internal format is important
//	// TODO: complete the derivation, probably do it elsewhere...
//	if(type() == Graphics::FLOAT || type() == Graphics::DOUBLE){
//		switch(numComponents()){
//			case 1: intFmt = GL_LUMINANCE32F_ARB; break;
//			case 2: intFmt = GL_LUMINANCE_ALPHA32F_ARB; break;
//			case 3: intFmt = GL_RGB32F_ARB; break;
//			case 4: intFmt = GL_RGBA32F_ARB; break;
//			default:;
//		}
//	} else {
//		// the old way - let the GPU decide:
//		intFmt = numComponents();
//	}

	int intFmt;

	// Use specified texel format, if defined
	if(mTexelFormat){
		intFmt = mTexelFormat;
	}
	
	// Derive internal texel format from texture data format.
	// By default, we can just use the texture data format. In cases where
	// there is no corresponding texel format, just hand in the number of
	// components.
	else{
		if(	format() == Graphics::RED ||
			format() == Graphics::GREEN ||
			format() == Graphics::BLUE
		){
			intFmt = 1;
		}
		else if(format() == Graphics::BGRA){
			intFmt = 4;
		}
		else{
			intFmt = format();
		}	
	}

	switch(mTarget){
		case GL_TEXTURE_1D:
			glTexImage1D(mTarget, 0, intFmt, width(), 0, format(), type(), pixels);
			break;
		case GL_TEXTURE_2D:
			glTexImage2D(mTarget, 0, intFmt, width(), height(), 0, format(), type(), pixels);
			break;
		case GL_TEXTURE_3D:
			glTexImage3D(mTarget, 0, intFmt, width(), height(), depth(), 0, format(), type(), pixels);
			break;
		default:
			AL_WARN("invalid texture target %d", mTarget);
	}
	Graphics::error(id(), "Texture::submit (glTexImage)");
	
	// set alignment back to default
	glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
	Graphics::error(id(), "Texture::submit (glPixelStorei unset)");
	
//		// OpenGL may have changed the internal format to one it supports:
//		GLint format;
//		glGetTexLevelParameteriv(mTarget, 0, GL_TEXTURE_INTERNAL_FORMAT, &format);
//		if (format != mInternalFormat) {
//			printf("converted from %X to %X format\n", mInternalFormat, format);
//			mInternalFormat = format;
//		}

	//printf("submitted texture data %p\n", pixels);
	
	glDisable(target());
	glBindTexture(target(), 0);
	Graphics::error(id(), "Texture::submit (glBindTexture 0)");
}
Example #8
0
void Texture :: submit(const Array& src, bool reconfigure) {	
//	if (src.type() != AlloUInt8Ty) {
//		printf("submit failed: only uint8_t arrays are supported\n");
//		return;
//	} 
	
	if (reconfigure) {
		// reconfigure texture from array
		switch (src.dimcount()) {
			case 1: target(TEXTURE_1D); break;
			case 2: target(TEXTURE_2D); break;
			case 3: target(TEXTURE_3D); break;
			default:
				AL_WARN("invalid array dimensions for texture");
				return;
		}
		
		switch (src.dimcount()) {
			case 3:	depth(src.depth());
			case 2:	height(src.height());
			case 1:	width(src.width()); break;
		}

		switch (src.components()) {
			case 1:	format(Graphics::LUMINANCE); break; // alpha or luminance?
			case 2:	format(Graphics::LUMINANCE_ALPHA); break;
			case 3:	format(Graphics::RGB); break;
			case 4:	format(Graphics::RGBA); break;
			default:
				AL_WARN("invalid array component count for texture");
				return;
		}
		
		switch (src.type()) {
			case AlloUInt8Ty:	type(Graphics::UBYTE); break; 
			case AlloSInt8Ty:	type(Graphics::BYTE); break; 
			case AlloUInt16Ty:	type(Graphics::SHORT); break; 
			case AlloSInt16Ty:	type(Graphics::USHORT); break; 
			case AlloUInt32Ty:	type(Graphics::INT); break; 
			case AlloSInt32Ty:	type(Graphics::UINT); break; 
			case AlloFloat32Ty:	type(Graphics::FLOAT); break; 
			case AlloFloat64Ty:	type(Graphics::DOUBLE); break; 
			default:
				AL_WARN("invalid array type for texture");
				return;
		}
		
		// reformat internal array to match:
		if (!mArray.isFormat(src)) mArray.format(src);
		
		//printf("configured to target=%X(%dD), type=%X(%X), format=%X, align=(%d)\n", mTarget, src.dimcount(), type(), src.type(), mFormat, src.alignment());
	} 
	else {
		
		if (src.width() != width()) {
			AL_WARN("submit failed: source array width does not match");
			return;
		}
		if (target() != TEXTURE_1D) {
			if (height() && src.height() != height()) {
				AL_WARN("submit failed: source array height does not match");
				return;
			}
			if (target() == TEXTURE_3D) {
				if (depth() && src.depth() != depth()) {
					AL_WARN("submit failed: source array depth does not match");
					return;
				}
			}
		}
		
		if (Graphics::toDataType(src.type()) != type()) {
			AL_WARN("submit failed: source array type does not match texture");
			return;
		}
	
		switch (format()) {
			case Graphics::ALPHA:
			case Graphics::LUMINANCE:
				if (src.components() != 1) {
					AL_WARN("submit failed: source array component count does not match (got %d, should be 1)", src.components());
					return;
				}
				break;
			case Graphics::LUMINANCE_ALPHA:
				if (src.components() != 2) {
					AL_WARN("submit failed: source array component count does not match (got %d, should be 2)", src.components());
					return;
				}
				break;
			case Graphics::RGB:
				if (src.components() != 3) {
					AL_WARN("submit failed: source array component count does not match (got %d, should be 3)", src.components());
					return;
				}
				break;
			case Graphics::RGBA:
				if (src.components() != 4) {
					AL_WARN("submit failed: source array component count does not match (got %d, should be 4)", src.components());
					return;
				}
				break;
			default:
				break;
		}
	}
	
	submit(src.data.ptr, src.alignment());
}
Example #9
0
void Texture :: allocate(const Array& src, bool reconfigure) {
	
	if (reconfigure) {
		
		//printf("allocating & reconfiguring %p from\n", this); src.print();
		
		// reconfigure texture from array:
		switch (src.dimcount()) {
			case 1: target(TEXTURE_1D); break;
			case 2: target(TEXTURE_2D); break;
			case 3: target(TEXTURE_3D); break;
			default:
				AL_WARN("invalid array dimensions for texture");
				return;
		}
		
		// reconfigure size:
		switch (src.dimcount()) {
			case 3:	depth(src.depth());
			case 2:	height(src.height());
			case 1:	width(src.width()); break;
			default:
				AL_WARN("texture array must have 1, 2 or 3 dimenions");
				return;
		}
		
		// reconfigure components 
		// (only if necessary - no need to lose e.g. 
		// mFormat = DEPTH_COMPONENT if we are only changing size)
		if (Graphics::numComponents(mFormat) != src.components()) {
			switch (src.components()) {
				case 1:	
					format(Graphics::LUMINANCE); break; // alpha or luminance?
				case 2:	
					format(Graphics::LUMINANCE_ALPHA); break;
				case 3:	
					format(Graphics::RGB); break;
				case 4:	
					format(Graphics::RGBA); break;
				default:
					AL_WARN("invalid array component count for texture");
					return;
			}
		}
		
		mArray.format(src);
		
		
		//printf("allocating & reconfigured %p\n", this); mArray.print();
		
		// re-allocate array:
		allocate(src.alignment());
		
		//printf("allocated & reconfigured %p\n", this);
		//mArray.print();
		
	} else {
		
		// TODO: read the source into the dst without changing dst layout
		//printf("allocating without reconfiguring %p\n", this);
		
		// ensure that array matches texture:
		if (!src.isFormat(mArray.header)) {
			AL_WARN("couldn't allocate array, mismatch format");
			mArray.print();
			src.print();
			return;
		}
		
		// re-allocate array:
		allocate();
	}
	
	//src.print();
	//mArray.print();
	
	// copy data:
	memcpy(mArray.data.ptr, src.data.ptr, src.size());
	
	//printf("copied to mArray %p\n", this);
}
	extern "C" void al_main_native_enter(al_sec interval) {
		AL_WARN("Win32 native loop not yet implemented");
	}
	extern "C" void al_main_native_attach(al_sec interval) {
		AL_WARN("Linux native loop not yet implemented");
	}
    virtual bool save(const std::string& filename, const Array& arr, int compressFlags) {

        // check existing image type
        FREE_IMAGE_FORMAT fileType = FreeImage_GetFIFFromFilename(filename.c_str());

        if(fileType == FIF_UNKNOWN) {
            AL_WARN("image format not recognized: %s", filename.c_str());
            return false;
        }
        if(!FreeImage_FIFSupportsWriting(fileType)) {
            AL_WARN("image format not supported: %s", filename.c_str());
            return false;
        }

        Image::Format format = Image::getFormat(arr.components());

        if(FIF_JPEG == fileType && (Image::RGBA == format || Image::LUMALPHA == format)) {
            AL_WARN("cannot save JPEG with alpha channel; use 24-bit RGB or 8-bit grayscale/luminance");
            return false;
        }

        unsigned w = arr.dim(0);
        unsigned h = (arr.dimcount() > 1) ? arr.dim(1) : 1;
        int bpp = arr.stride(0)*8; // bits/pixel

        resize(w,h,bpp);

        if (mImage == NULL) {
            AL_WARN("image could not be understood");
            return false;
        }

        const int rowstride = arr.stride(1);

        //printf("w=%d, h=%d, bpp=%d, stride=%d\n", w,h,bpp,rowstride);

        switch(format) {

        case Image::LUMINANCE:
            switch(arr.type()) {

            case AlloUInt8Ty: {
                char *bp = (char *)(arr.data.ptr);
                for(unsigned j = 0; j < h; ++j) {
                    memcpy(
                        FreeImage_GetScanLine(mImage, j),
                        bp + j*rowstride,
                        w
                    );
                }
            }
            break;

            default:
                AL_WARN("input Array component type not supported");
                return false;
            }
            break;

        // TODO: must save as RGBA
        /*case Image::LUMALPHA:
        	switch(header.type) {

        		case AlloUInt8Ty: {
        			char *bp = (char *)(arr.data.ptr);
        			for(unsigned j = 0; j < h; ++j) {
        				memcpy(
        					FreeImage_GetScanLine(mImage, j),
        					bp + j*rowstride,
        					w*2
        				);
        			}
        		}
        		break;

        		default:
        			AL_WARN("input Array component type not supported");
        			return false;
        	}
        break;*/

        /* According to the FreeImage documentation ("Pixel access functions"):
        "When accessing to individual color components of a 24- or 32-bit
        DIB, you should always use FreeImage macros or RGBTRIPLE / RGBQUAD
        structures in order to write OS independent code.
        */
        case Image::RGB: {
            switch(arr.type()) {

            case AlloUInt8Ty: { //printf("FreeImageImpl: save uint8/RGB\n");
                char *bp = (char *)(arr.data.ptr);

                for(unsigned j = 0; j < h; ++j) {
                    RGBTRIPLE * dst = (RGBTRIPLE *)FreeImage_GetScanLine(mImage, j);
                    const Image::RGBPix<uint8_t> * src = (const Image::RGBPix<uint8_t> *)(bp + j*rowstride);
                    for(unsigned i=0; i < w; ++i) {
                        dst[i].rgbtRed  = src[i].r;
                        dst[i].rgbtGreen= src[i].g;
                        dst[i].rgbtBlue = src[i].b;
                    }
                }
            }
            break;

            default:
                AL_WARN("input Array component type not supported");
                return false;
            }
        }
        break;

        case Image::RGBA: {

            switch(arr.type()) {

            case AlloUInt8Ty: {
                char *bp = (char *)(arr.data.ptr);

                for(unsigned j = 0; j < h; ++j) {
                    RGBQUAD * dst = (RGBQUAD *)FreeImage_GetScanLine(mImage, j);
                    const Image::RGBAPix<uint8_t> * src = (const Image::RGBAPix<uint8_t> *)(bp + j*rowstride);
                    for(unsigned i=0; i < w; ++i) {
                        dst[i].rgbRed     = src[i].r;
                        dst[i].rgbGreen   = src[i].g;
                        dst[i].rgbBlue    = src[i].b;
                        dst[i].rgbReserved= src[i].a;
                    }
                }
            }
            break;

            default:
                AL_WARN("input Array component type not supported");
                return false;
            }
        }
        break;

        default: {
            AL_WARN("input Array component format not supported");
            return false;
        }
        }


        int flags;
        int compressAmt = compressFlags & 127;
        int quality = 100-(compressAmt<=100?compressAmt:100);

        switch(fileType) {

        case FIF_BMP:
            flags = compressAmt >= 50 ? BMP_SAVE_RLE : BMP_DEFAULT;
            break;

        case FIF_EXR:
            flags = compressAmt >= 50 ? EXR_DEFAULT : EXR_NONE;
            break;

        case FIF_JPEG: // default: JPEG_QUALITYGOOD|JPEG_SUBSAMPLING_420
            /*if(compressAmt <= 25) flags = JPEG_QUALITYSUPERB;
            else if(compressAmt <= 50) flags = JPEG_QUALITYGOOD;
            else if(compressAmt <= 75) flags = JPEG_QUALITYAVERAGE;
            else flags = JPEG_QUALITYBAD;*/
            flags = quality;
            break;

        case FIF_J2K:
        case FIF_JP2:
            flags = int(double(quality)*5.11 + 1); // convert [0,100] -> [1,512]
            break;

        case FIF_PNG: // default: PNG_Z_DEFAULT_COMPRESSION (= 6)
            compressAmt = (compressAmt + 5) / 10;
            if(compressAmt != 0) flags = compressAmt>=9 ? 9 : compressAmt;
            else flags = PNG_Z_NO_COMPRESSION;
            break;

        case FIF_TIFF:
            flags = compressAmt >= 50 ? TIFF_DEFAULT : TIFF_NONE;
            break;

        case FIF_TARGA:
            flags = compressAmt >= 50 ? TARGA_SAVE_RLE : TARGA_DEFAULT;
            break;

        default:
            flags = 0;
        }

        return FreeImage_Save(fileType, mImage, filename.c_str(), flags);
    }