#include "ddutil.hpp"
#include "graphicsruntime.hpp"
#include "asmcoder.hpp"
#include "gxcanvas.hpp"
#include "gxruntime.hpp"
#include "gxgraphics.hpp"

//#define FREEIMAGE_LIB
#include <freeimage.h>

extern gxRuntime* gx_runtime;
static AsmCoder   asm_coder;

static void calcShifts(unsigned mask, unsigned char* shr, unsigned char* shl)
{
	if (mask) {
		for (*shl = 0; !(mask & 1); ++*shl, mask >>= 1) {
		}
		for (*shr = 8; mask & 1; --*shr, mask >>= 1) {
		}
	} else
		*shr = *shl = 0;
}

PixelFormat::~PixelFormat()
{
	if (plot_code) {
		VirtualFree(plot_code, 0, MEM_RELEASE);
	}
}

void PixelFormat::setFormat(const DDPIXELFORMAT& pf)
{
	if (plot_code) {
		VirtualFree(plot_code, 0, MEM_RELEASE);
	}

	if (!(pf.dwFlags & DDPF_RGB)) {
		memset(this, 0, sizeof(*this));
		return;
	}

	plot_code  = (char*)VirtualAlloc(0, 128, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
	point_code = plot_code + 64;

	depth    = pf.dwRGBBitCount;
	amask    = pf.dwRGBAlphaBitMask;
	rmask    = pf.dwRBitMask;
	gmask    = pf.dwGBitMask;
	bmask    = pf.dwBBitMask;
	pitch    = depth / 8;
	argbfill = 0;
	if (!amask)
		argbfill |= 0xff000000;
	if (!rmask)
		argbfill |= 0x00ff0000;
	if (!gmask)
		argbfill |= 0x0000ff00;
	if (!bmask)
		argbfill |= 0x000000ff;
	calcShifts(amask, &ashr, &ashl);
	ashr += 24;
	calcShifts(rmask, &rshr, &rshl);
	rshr += 16;
	calcShifts(gmask, &gshr, &gshl);
	gshr += 8;
	calcShifts(bmask, &bshr, &bshl);
	plot  = (Plot)(void*)plot_code;
	point = (Point)(void*)point_code;
	asm_coder.CodePlot(plot_code, depth, amask, rmask, gmask, bmask);
	asm_coder.CodePoint(point_code, depth, amask, rmask, gmask, bmask);
}

static void adjustTexSize(int* width, int* height, IDirect3DDevice7* dir3dDev, bool forcePOT = true)
{
	D3DDEVICEDESC7 ddDesc = {0};
	if (dir3dDev->GetCaps(&ddDesc) < 0) {
		*width = *height = 256;
		return;
	}

	int w = *width, h = *height, min, max;

	if (ddDesc.dpcTriCaps.dwTextureCaps & D3DPTEXTURECAPS_POW2
		|| ddDesc.dpcTriCaps.dwTextureCaps & D3DPTEXTURECAPS_NONPOW2CONDITIONAL || forcePOT) {
		// DirectX Device doesn't support Non Power-Of-Two Textures.

		for (w = 1; w < *width; w <<= 1) {
		}
		for (h = 1; h < *height; h <<= 1) {
		}
	} else if (ddDesc.dpcTriCaps.dwTextureCaps & D3DPTEXTURECAPS_SQUAREONLY) {
		// DirectX Device only supports Square Textures;

		if (w > h)
			h = w;
		else
			w = h;
	}

	//check aspect ratio
	if ((max = ddDesc.dwMaxTextureAspectRatio) && max > 0) {
		int asp = w > h ? w / h : h / w;
		if (asp > max) {
			if (w > h)
				h = w / max;
			else
				w = h / max;
		}
	}
	//clamp size
	if ((min = ddDesc.dwMinTextureWidth) && w < min)
		w = min;
	if ((min = ddDesc.dwMinTextureHeight) && h < min)
		h = min;
	if ((max = ddDesc.dwMaxTextureWidth) && w > max)
		w = max;
	if ((max = ddDesc.dwMaxTextureHeight) && h > max)
		h = max;

	*width  = w;
	*height = h;
}

static IDirectDrawSurface7* createSurface(int width, int height, int pitch, void* bits, IDirectDraw7* dirDraw)
{
	DDSURFACEDESC2 desc         = {sizeof(desc)};
	desc.dwFlags                = DDSD_WIDTH | DDSD_HEIGHT | DDSD_LPSURFACE | DDSD_PITCH | DDSD_PIXELFORMAT | DDSD_CAPS;
	desc.ddsCaps.dwCaps         = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY;
	desc.dwWidth                = width;
	desc.dwHeight               = height;
	desc.lPitch                 = pitch;
	desc.lpSurface              = bits;
	desc.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT);
	desc.ddpfPixelFormat.dwFlags           = DDPF_RGB | DDPF_ALPHAPIXELS;
	desc.ddpfPixelFormat.dwRGBBitCount     = 32;
	desc.ddpfPixelFormat.dwRBitMask        = 0xff0000;
	desc.ddpfPixelFormat.dwGBitMask        = 0x00ff00;
	desc.ddpfPixelFormat.dwBBitMask        = 0x0000ff;
	desc.ddpfPixelFormat.dwRGBAlphaBitMask = 0xff000000;
	IDirectDrawSurface7* surf;
	if (dirDraw->CreateSurface(&desc, &surf, 0) >= 0)
		return surf;
	return 0;
}

static void buildMask(IDirectDrawSurface7* surf)
{
	DDSURFACEDESC2 desc = {sizeof(desc)};
	surf->Lock(0, &desc, DDLOCK_WAIT, 0);
	unsigned char* surf_p = (unsigned char*)desc.lpSurface;
	PixelFormat    fmt(desc.ddpfPixelFormat);

	for (DWORD y = 0; y < desc.dwHeight; ++y) {
		unsigned char* p = surf_p;
		for (DWORD x = 0; x < desc.dwWidth; ++x) {
			unsigned argb = fmt.getPixel(p);
			unsigned rgb  = argb & 0xffffff;
			unsigned a    = rgb ? 0xff000000 : 0;
			fmt.setPixel(p, a | rgb);
			p += fmt.getPitch();
		}
		surf_p += desc.lPitch;
	}
	surf->Unlock(0);
}

static void buildAlpha(IDirectDrawSurface7* surf, bool whiten)
{
	DDSURFACEDESC2 desc = {sizeof(desc)};
	surf->Lock(0, &desc, DDLOCK_WAIT, 0);
	unsigned char* surf_p = (unsigned char*)desc.lpSurface;
	PixelFormat    fmt(desc.ddpfPixelFormat);

	for (DWORD y = 0; y < desc.dwHeight; ++y) {
		unsigned char* p = surf_p;
		for (DWORD x = 0; x < desc.dwWidth; ++x) {
			unsigned argb  = fmt.getPixel(p);
			unsigned alpha = (((argb >> 16) & 0xff) + ((argb >> 8) & 0xff) + (argb & 0xff)) / 3;
			argb           = (alpha << 24) | (argb & 0xffffff);
			if (whiten)
				argb |= 0xffffff;
			fmt.setPixel(p, argb);
			p += fmt.getPitch();
		}
		surf_p += desc.lPitch;
	}
	surf->Unlock(0);
}

void ddUtil::buildMipMaps(IDirectDrawSurface7* surf)
{
	DDSURFACEDESC2 desc = {sizeof(desc)};
	surf->GetSurfaceDesc(&desc);
	if (!(desc.ddsCaps.dwCaps & DDSCAPS_TEXTURE))
		return;
	if (!(desc.ddpfPixelFormat.dwFlags & DDPF_RGB))
		return;

	DDSCAPS2 caps = {0};
	caps.dwCaps   = DDSCAPS_TEXTURE;
	caps.dwCaps2  = DDSCAPS2_MIPMAPSUBLEVEL;

	IDirectDrawSurface7 *src = surf, *dest;

	while (src->GetAttachedSurface(&caps, &dest) >= 0) {
		DDSURFACEDESC2 src_desc = {sizeof(src_desc)};
		if (src->Lock(0, &src_desc, DDLOCK_WAIT, 0) < 0)
			abort();
		unsigned char* src_p = (unsigned char*)src_desc.lpSurface;
		PixelFormat    src_fmt(src_desc.ddpfPixelFormat);

		DDSURFACEDESC2 dest_desc = {sizeof(dest_desc)};
		if (dest->Lock(0, &dest_desc, DDLOCK_WAIT, 0) < 0)
			abort();
		unsigned char* dest_p = (unsigned char*)dest_desc.lpSurface;
		PixelFormat    dest_fmt(dest_desc.ddpfPixelFormat);

		if (src_desc.dwWidth == 1) {
			for (DWORD y = 0; y < dest_desc.dwHeight; ++y) {
				unsigned p1   = src_fmt.getPixel(src_p);
				unsigned p2   = src_fmt.getPixel(src_p + src_desc.lPitch);
				unsigned argb = ((p1 & 0xfefefefe) >> 1) + ((p2 & 0xfefefefe) >> 1);
				argb += (((p1 & 0x01010101) + (p2 & 0x01010101)) >> 1) & 0x01010101;
				dest_fmt.setPixel(dest_p, argb);
				src_p += src_desc.lPitch * 2;
				dest_p += dest_desc.lPitch;
			}
		} else if (src_desc.dwHeight == 1) {
			for (DWORD x = 0; x < dest_desc.dwWidth; ++x) {
				unsigned p1   = src_fmt.getPixel(src_p);
				unsigned p2   = src_fmt.getPixel(src_p + src_fmt.getPitch());
				unsigned argb = ((p1 & 0xfefefefe) >> 1) + ((p2 & 0xfefefefe) >> 1);
				argb += (((p1 & 0x01010101) + (p2 & 0x01010101)) >> 1) & 0x01010101;
				dest_fmt.setPixel(dest_p, argb);
				src_p += src_fmt.getPitch() * 2;
				dest_p += dest_fmt.getPitch();
			}
		} else {
			for (DWORD y = 0; y < dest_desc.dwHeight; ++y) {
				unsigned char* src_t  = src_p;
				unsigned char* dest_t = dest_p;
				for (DWORD x = 0; x < dest_desc.dwWidth; ++x) {
					unsigned p1 = src_fmt.getPixel(src_t);
					unsigned p2 = src_fmt.getPixel(src_t + src_fmt.getPitch());
					unsigned p3 = src_fmt.getPixel(src_t + src_desc.lPitch + src_fmt.getPitch());
					unsigned p4 = src_fmt.getPixel(src_t + src_desc.lPitch);

					unsigned argb = ((p1 & 0xfcfcfcfc) >> 2) + ((p2 & 0xfcfcfcfc) >> 2) + ((p3 & 0xfcfcfcfc) >> 2)
									+ ((p4 & 0xfcfcfcfc) >> 2);
					argb += (((p1 & 0x03030303) + (p2 & 0x03030303) + (p3 & 0x03030303) + (p4 & 0x03030303)) >> 2)
							& 0x03030303;

					dest_fmt.setPixel(dest_t, argb);
					src_t += src_fmt.getPitch() * 2;
					dest_t += dest_fmt.getPitch();
				}
				src_p += src_desc.lPitch * 2;
				dest_p += dest_desc.lPitch;
			}
		}
		src->Unlock(0);
		dest->Unlock(0);
		dest->Release();
		src = dest;
	}
}

void ddUtil::copy(IDirectDrawSurface7* dst_surface, int dx, int dy, int dw, int dh, IDirectDrawSurface7* src_surface,
				  int sx, int sy, int sw, int sh)
{
	DDSURFACEDESC2 src_desc = {sizeof(src_desc)};
	src_surface->Lock(0, &src_desc, DDLOCK_WAIT, 0);
	PixelFormat    src_format(src_desc.ddpfPixelFormat);
	unsigned char* src_memory = (unsigned char*)src_desc.lpSurface;
	src_memory += src_desc.lPitch * sy + src_format.getPitch() * sx;

	DDSURFACEDESC2 dst_desc = {sizeof(dst_desc)};
	dst_surface->Lock(0, &dst_desc, DDLOCK_WAIT, 0);
	PixelFormat    dst_format(dst_desc.ddpfPixelFormat);
	unsigned char* dst_memory = (unsigned char*)dst_desc.lpSurface;
	dst_memory += dst_desc.lPitch * dy + dst_format.getPitch() * dx;

	for (int y = 0; y < dh; ++y) {
		unsigned char* dst = dst_memory;
		unsigned char* src = src_memory + src_desc.lPitch * (y * sh / dh);
		for (int x = 0; x < dw; ++x) {
			dst_format.setPixel(dst, src_format.getPixel(src + src_format.getPitch() * (x * sw / dw)));
			dst += dst_format.getPitch();
		}
		dst_memory += dst_desc.lPitch;
	}

	src_surface->Unlock(0);
	dst_surface->Unlock(0);
}

IDirectDrawSurface7* ddUtil::createSurface(int w, int h, int flags, gxGraphics* gfx)
{
	int            hi   = flags & gxCanvas::CANVAS_TEX_HICOLOR ? 1 : 0;
	DDSURFACEDESC2 desc = {sizeof(desc)};

	// Texture Size
	desc.dwFlags = DDSD_CAPS;
	if (w) {
		desc.dwWidth = w;
		desc.dwFlags |= DDSD_WIDTH;
	}
	if (h) {
		desc.dwHeight = h;
		desc.dwFlags |= DDSD_HEIGHT;
	}

	// Format
	if (flags & gxCanvas::CANVAS_TEXTURE) {
		// Flags guaranteed:
		// - CANVAS_TEX_RGB
		// - CANVAS_TEX_ALPHA
		// - CANVAS_TEXTURE
		desc.dwFlags |= DDSCAPS_3DDEVICE | DDSD_PIXELFORMAT;
		desc.ddsCaps.dwCaps |= DDSCAPS_TEXTURE | DDSCAPS_VIDEOMEMORY;
		if (flags & gxCanvas::CANVAS_TEXTURE) {
			desc.ddpfPixelFormat = gfx->primFmt;
		} else {
			desc.ddpfPixelFormat = gfx->texRGBAlphaFmt[hi];
		}
	} else {
		if (flags & gxCanvas::CANVAS_TEX_MASK) {
			desc.dwFlags |= DDSD_PIXELFORMAT;
			desc.ddpfPixelFormat = gfx->texRGBMaskFmt[hi];
		} else if (flags & gxCanvas::CANVAS_TEX_RGB) {
			desc.dwFlags |= DDSD_PIXELFORMAT;
			desc.ddpfPixelFormat = (flags & gxCanvas::CANVAS_TEX_ALPHA) ? gfx->texRGBAlphaFmt[hi] : gfx->texRGBFmt[hi];
		} else if (flags & gxCanvas::CANVAS_TEX_ALPHA) {
			desc.dwFlags |= DDSD_PIXELFORMAT;
			desc.ddpfPixelFormat = gfx->texAlphaFmt[hi];
		} else if (flags & gxCanvas::CANVAS_TEXTURE) {
			desc.dwFlags |= DDSD_PIXELFORMAT;
			desc.ddpfPixelFormat = gfx->primFmt;
		}
	}

	// Cube Map
	if (flags & (gxCanvas::CANVAS_TEX_CUBE)) {
		desc.ddsCaps.dwCaps |= DDSCAPS_COMPLEX;
		desc.ddsCaps.dwCaps2 |= DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_ALLFACES;
	}

	// Video Memory
	if (!(flags & gxCanvas::CANVAS_TEX_VIDMEM)) {
		desc.ddsCaps.dwCaps2 |= DDSCAPS2_TEXTUREMANAGE;
		if (flags & gxCanvas::CANVAS_TEX_MIPMAP) {
			desc.ddsCaps.dwCaps |= DDSCAPS_MIPMAP | DDSCAPS_COMPLEX;
		}
	}

	// Texture Move
	if ((flags & gxCanvas::CANVAS_TEXTURE) || (flags & gxCanvas::CANVAS_3DRENDER)) {
		desc.ddsCaps.dwCaps |= DDSCAPS_TEXTURE;
		if (!(flags & gxCanvas::CANVAS_TEX_VIDMEM)) {
			desc.ddsCaps.dwCaps2 |= DDSCAPS2_TEXTUREMANAGE;
			if (flags & gxCanvas::CANVAS_TEX_MIPMAP) {
				desc.ddsCaps.dwCaps |= DDSCAPS_MIPMAP | DDSCAPS_COMPLEX;
			}
		}
		if (flags & (gxCanvas::CANVAS_TEX_CUBE)) {
			desc.ddsCaps.dwCaps |= DDSCAPS_COMPLEX;
			desc.ddsCaps.dwCaps2 |= DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_ALLFACES;
		}
		adjustTexSize((int*)&desc.dwWidth, (int*)&desc.dwHeight, gfx->dir3dDev, !(flags & (gxCanvas::CANVAS_TEX_NPOT)));
	} else {
		desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
		if (flags & gxCanvas::CANVAS_HIGHCOLOR) {
			desc.dwFlags |= DDSD_PIXELFORMAT;
			desc.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY;
			desc.ddpfPixelFormat.dwSize            = sizeof(DDPIXELFORMAT);
			desc.ddpfPixelFormat.dwFlags           = DDPF_RGB | DDPF_ALPHAPIXELS;
			desc.ddpfPixelFormat.dwRGBBitCount     = 32;
			desc.ddpfPixelFormat.dwRBitMask        = 0xff0000;
			desc.ddpfPixelFormat.dwGBitMask        = 0x00ff00;
			desc.ddpfPixelFormat.dwBBitMask        = 0x0000ff;
			desc.ddpfPixelFormat.dwRGBAlphaBitMask = 0xff000000;
		} else if (flags & gxCanvas::CANVAS_NONDISPLAY) {
			desc.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY;
		}
	}

	IDirectDrawSurface7* surf;
	if (gfx->dirDraw->CreateSurface(&desc, &surf, 0) >= 0) {
		return surf;
	}
	if (desc.ddsCaps.dwCaps & DDSCAPS_OFFSCREENPLAIN) {
		if (!(desc.ddsCaps.dwCaps & DDSCAPS_SYSTEMMEMORY)) {
			//try again in system memory!
			desc.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY;
			if (gfx->dirDraw->CreateSurface(&desc, &surf, 0) >= 0)
				return surf;
		}
	}

	return 0;
}

//Tom Speed's DXTC loader
//
IDirectDrawSurface7* loadDXTC(const char* filename, gxGraphics* gfx)
{
	HRESULT        hr;
	DDSURFACEDESC2 ddsd;
	DDSURFACEDESC2 fileddsd;
	char           magicID[4];
	FILE*          fp;

	/* try to open the file */
	fp = fopen(filename, "rb");
	if (!fp)
		return NULL;

	/* valid DDS? */
	fread(magicID, 1, 4, fp);
	if (strncmp(magicID, "DDS ", 4) != 0) {
		fclose(fp);
		return NULL;
	}

	/* get the DXTC file surface description */
	fread(&fileddsd, sizeof(DDSURFACEDESC2), 1, fp);

	if (fileddsd.dwSize != sizeof(DDSURFACEDESC2)) {
		fclose(fp);
		return NULL;
	}

	/* copy the fileddsd before we manipulate it so you
	can get neccessary info you want about it later */
	memcpy(&ddsd, &fileddsd, sizeof(DDSURFACEDESC2));

	/* remove unwanted flags if they exist */
	//not sure if this is needed, works without it though
	//ddsd.dwFlags &= ~DDSD_LINEARSIZE;

	int blockSize = 0;
	int chunkSize = 0;

	if (ddsd.ddpfPixelFormat.dwFourCC == FOURCC_DXT1)
		blockSize = 8; // DXT1
	if (ddsd.ddpfPixelFormat.dwFourCC == FOURCC_DXT3)
		blockSize = 16; // DXT3
	if (ddsd.ddpfPixelFormat.dwFourCC == FOURCC_DXT5)
		blockSize = 16; // DXT5

	/* if it isn't a format we support, exit */
	if (blockSize == 0) {
		fclose(fp);
		return NULL;
	}

	/* add texture manage flag */
	ddsd.ddsCaps.dwCaps2 |= DDSCAPS2_TEXTUREMANAGE;

	/* Create the new DXTC surface using the DDSURFACEDESC2
	we read in from the file */
	IDirectDrawSurface7* newSurf = NULL;
	hr                           = gfx->dirDraw->CreateSurface(&ddsd, &newSurf, NULL);
	if (FAILED(hr)) {
		fclose(fp);
		return NULL;
	}

	/* Define what type of child surfaces we may wish
	to access, in this case MipMaps */
	DDSCAPS2 mipmapddsd;
	ZeroMemory(&mipmapddsd, sizeof(DDSCAPS2));
	mipmapddsd.dwCaps = DDSCAPS_TEXTURE | DDSCAPS_MIPMAP | DDSCAPS_COMPLEX;

	/* pointers used when iterating through mipmaps */
	IDirectDrawSurface7* topDDS  = NULL;
	IDirectDrawSurface7* nextDDS = NULL;

	topDDS = newSurf;
	topDDS->AddRef();

	while (TRUE) {
		/* get a description of this surface */
		hr = topDDS->Lock(NULL, &ddsd, DDLOCK_WAIT, NULL);
		if (FAILED(hr)) {
			fclose(fp);
			topDDS->Release();
			newSurf->Release();
			nextDDS->Release();
			return NULL;
		}

		/* how big the raw data is for this surface */
		chunkSize = ((ddsd.dwWidth + 3) / 4) * ((ddsd.dwHeight + 3) / 4) * blockSize;

		/* read in the raw DXTC surface data */
		if (!fread(ddsd.lpSurface, chunkSize, 1, fp)) {
			fclose(fp);
			topDDS->Release();
			newSurf->Release();
			nextDDS->Release();
			return NULL;
		}
		topDDS->Unlock(NULL);

		/* Get next mipmap in chain, or exit the loop if there's no more */
		hr = topDDS->GetAttachedSurface(&mipmapddsd, &nextDDS);
		if (FAILED(hr)) {
			fclose(fp);
			topDDS->Release();
			break;
		}

		topDDS->Release();
		topDDS = nextDDS;
		nextDDS->Release();
	}

	return newSurf;
}

IDirectDrawSurface7* ddUtil::loadSurface(const std::string& f, int flags, gxGraphics* gfx)
{
	int i = f.find(".dds");
	if (i != std::string::npos && i + 4 == f.size()) {
		//dds file!
		IDirectDrawSurface7* surf = loadDXTC(f.c_str(), gfx);
		return surf;
	}

	FreeImage_Initialise();
	FREE_IMAGE_FORMAT fmt = FreeImage_GetFileType(f.c_str(), f.size());
	if (fmt == FIF_UNKNOWN) {
		int n = f.find(".");
		if (n == std::string::npos)
			return 0;
		fmt = FreeImage_GetFileType(f.c_str());
		if (fmt == FIF_UNKNOWN)
			return 0;
	}
	FIBITMAP* t_dib = FreeImage_Load(fmt, f.c_str(), 0);
	if (!t_dib)
		return 0;

	bool trans = FreeImage_GetBPP(t_dib) == 32 || FreeImage_IsTransparent(t_dib);

	FIBITMAP* dib = FreeImage_ConvertTo32Bits(t_dib);

	if (dib)
		FreeImage_Unload(t_dib);
	else
		dib = t_dib;

	int   width  = FreeImage_GetWidth(dib);
	int   height = FreeImage_GetHeight(dib);
	int   pitch  = FreeImage_GetPitch(dib);
	void* bits   = FreeImage_GetBits(dib);

	IDirectDrawSurface7* src = ::createSurface(width, height, pitch, bits, gfx->dirDraw);
	if (!src) {
		FreeImage_Unload(dib);
		return 0;
	}

	if (flags & gxCanvas::CANVAS_TEX_ALPHA) {
		if (flags & gxCanvas::CANVAS_TEX_MASK) {
			buildMask(src);
		} else if (!trans) {
			buildAlpha(src, (flags & gxCanvas::CANVAS_TEX_RGB) ? false : true);
		}
	} else {
		unsigned char* p = (unsigned char*)bits;
		for (int k = 0; k < height; ++k) {
			unsigned char* t = p + 3;
			for (int j = 0; j < width; ++j) {
				*t = 0xff;
				t += 4;
			}
			p += pitch;
		}
	}

	IDirectDrawSurface7* dest = createSurface(width, height, flags, gfx);
	if (!dest) {
		src->Release();
		FreeImage_Unload(dib);
		return 0;
	}

	int t_w = width, t_h = height;
	if (flags & gxCanvas::CANVAS_TEXTURE)
		adjustTexSize(&t_w, &t_h, gfx->dir3dDev, !(flags & (gxCanvas::CANVAS_TEX_NPOT)));
	copy(dest, 0, 0, t_w, t_h, src, 0, height - 1, width, -height);

	src->Release();
	FreeImage_Unload(dib);
	return dest;
}
