/*
** texture.cpp
** The base texture class
**
**---------------------------------------------------------------------------
** Copyright 2004-2007 Randy Heit
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
**    notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
**    notice, this list of conditions and the following disclaimer in the
**    documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
**    derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
**
*/

#include "doomtype.h"
#include "files.h"
#include "w_wad.h"
#include "templates.h"
#include "i_system.h"
#include "r_data/r_translate.h"
#include "bitmap.h"
#include "colormatcher.h"
#include "c_dispatch.h"
#include "v_video.h"
#include "m_fixed.h"
#include "textures/textures.h"
#include "v_palette.h"

typedef bool (*CheckFunc)(FileReader & file);
typedef FTexture * (*CreateFunc)(FileReader & file, int lumpnum);

struct TexCreateInfo
{
	CreateFunc TryCreate;
	int usetype;
};

BYTE FTexture::GrayMap[256];

void FTexture::InitGrayMap()
{
	for (int i = 0; i < 256; ++i)
	{
		GrayMap[i] = ColorMatcher.Pick (i, i, i);
	}
}

FTexture *IMGZTexture_TryCreate(FileReader &, int lumpnum);
FTexture *PNGTexture_TryCreate(FileReader &, int lumpnum);
FTexture *JPEGTexture_TryCreate(FileReader &, int lumpnum);
FTexture *DDSTexture_TryCreate(FileReader &, int lumpnum);
FTexture *PCXTexture_TryCreate(FileReader &, int lumpnum);
FTexture *TGATexture_TryCreate(FileReader &, int lumpnum);
FTexture *RawPageTexture_TryCreate(FileReader &, int lumpnum);
FTexture *FlatTexture_TryCreate(FileReader &, int lumpnum);
FTexture *PatchTexture_TryCreate(FileReader &, int lumpnum);
FTexture *EmptyTexture_TryCreate(FileReader &, int lumpnum);
FTexture *AutomapTexture_TryCreate(FileReader &, int lumpnum);


// Examines the lump contents to decide what type of texture to create,
// and creates the texture.
FTexture * FTexture::CreateTexture (int lumpnum, int usetype)
{
	static TexCreateInfo CreateInfo[]={
		{ IMGZTexture_TryCreate,		TEX_Any },
		{ PNGTexture_TryCreate,			TEX_Any },
		{ JPEGTexture_TryCreate,		TEX_Any },
		{ DDSTexture_TryCreate,			TEX_Any },
		{ PCXTexture_TryCreate,			TEX_Any },
		{ TGATexture_TryCreate,			TEX_Any },
		{ RawPageTexture_TryCreate,		TEX_MiscPatch },
		{ FlatTexture_TryCreate,		TEX_Flat },
		{ PatchTexture_TryCreate,		TEX_Any },
		{ EmptyTexture_TryCreate,		TEX_Any },
		{ AutomapTexture_TryCreate,		TEX_MiscPatch },
	};

	if (lumpnum == -1) return NULL;

	FWadLump data = Wads.OpenLumpNum (lumpnum);

	for(size_t i = 0; i < countof(CreateInfo); i++)
	{
		if ((CreateInfo[i].usetype == usetype || CreateInfo[i].usetype == TEX_Any))
		{
			FTexture * tex = CreateInfo[i].TryCreate(data, lumpnum);
			if (tex != NULL) 
			{
				tex->UseType = usetype;
				if (usetype == FTexture::TEX_Flat) 
				{
					int w = tex->GetWidth();
					int h = tex->GetHeight();

					// Auto-scale flats with dimensions 128x128 and 256x256.
					// In hindsight, a bad idea, but RandomLag made it sound better than it really is.
					// Now we're stuck with this stupid behaviour.
					if (w==128 && h==128) 
					{
						tex->Scale.X = tex->Scale.Y = 2;
						tex->bWorldPanning = true;
					}
					else if (w==256 && h==256) 
					{
						tex->Scale.X = tex->Scale.Y = 4;
						tex->bWorldPanning = true;
					}
				}
				return tex;
			}
		}
	}
	return NULL;
}

FTexture * FTexture::CreateTexture (const char *name, int lumpnum, int usetype)
{
	FTexture *tex = CreateTexture(lumpnum, usetype);
	if (tex != NULL && name != NULL) {
		tex->Name = name;
		tex->Name.ToUpper();
	}
	return tex;
}


FTexture::FTexture (const char *name, int lumpnum)
: LeftOffset(0), TopOffset(0),
  WidthBits(0), HeightBits(0), Scale(1,1), SourceLump(lumpnum),
  UseType(TEX_Any), bNoDecals(false), bNoRemap0(false), bWorldPanning(false),
  bMasked(true), bAlphaTexture(false), bHasCanvas(false), bWarped(0), bComplex(false), bMultiPatch(false), bKeepAround(false),
  Rotations(0xFFFF), SkyOffset(0), Width(0), Height(0), WidthMask(0), Native(NULL)
{
	id.SetInvalid();
	if (name != NULL)
	{
		Name = name;
		Name.ToUpper();
	}
	else if (lumpnum < 0)
	{
		Name = FString();
	}
	else
	{
		Wads.GetLumpName (Name, lumpnum);
	}
}

FTexture::~FTexture ()
{
	FTexture *link = Wads.GetLinkedTexture(SourceLump);
	if (link == this) Wads.SetLinkedTexture(SourceLump, NULL);
	KillNative();
}

void FTexture::Unload()
{
	PixelsBgra = std::vector<uint32_t>();
}

const uint32_t *FTexture::GetColumnBgra(unsigned int column, const Span **spans_out)
{
	const uint32_t *pixels = GetPixelsBgra();

	column %= Width;

	if (spans_out != nullptr)
		GetColumn(column, spans_out);
	return pixels + column * Height;
}

const uint32_t *FTexture::GetPixelsBgra()
{
	if (PixelsBgra.empty() || CheckModified())
	{
		if (!GetColumn(0, nullptr))
			return nullptr;

		FBitmap bitmap;
		bitmap.Create(GetWidth(), GetHeight());
		CopyTrueColorPixels(&bitmap, 0, 0);
		GenerateBgraFromBitmap(bitmap);
	}
	return PixelsBgra.data();
}

bool FTexture::CheckModified ()
{
	return false;
}

FTextureFormat FTexture::GetFormat()
{
	return TEX_Pal;
}

void FTexture::SetFrontSkyLayer ()
{
	bNoRemap0 = true;
}

void FTexture::CalcBitSize ()
{
	// WidthBits is rounded down, and HeightBits is rounded up
	int i;

	for (i = 0; (1 << i) < Width; ++i)
	{ }

	WidthBits = i;

	// Having WidthBits that would allow for columns past the end of the
	// texture is not allowed, even if it means the entire texture is
	// not drawn.
	if (Width < (1 << WidthBits))
	{
		WidthBits--;
	}
	WidthMask = (1 << WidthBits) - 1;

	// <hr>The minimum height is 2, because we cannot shift right 32 bits.</hr>
	// Scratch that. Somebody actually made a 1x1 texture, so now we have to handle it.
	for (i = 0; (1 << i) < Height; ++i)
	{ }

	HeightBits = i;
}

void FTexture::HackHack (int newheight)
{
}

FTexture::Span **FTexture::CreateSpans (const BYTE *pixels) const
{
	Span **spans, *span;

	if (!bMasked)
	{ // Texture does not have holes, so it can use a simpler span structure
		spans = (Span **)M_Malloc (sizeof(Span*)*Width + sizeof(Span)*2);
		span = (Span *)&spans[Width];
		for (int x = 0; x < Width; ++x)
		{
			spans[x] = span;
		}
		span[0].Length = Height;
		span[0].TopOffset = 0;
		span[1].Length = 0;
		span[1].TopOffset = 0;
	}
	else
	{ // Texture might have holes, so build a complete span structure
		int numcols = Width;
		int numrows = Height;
		int numspans = numcols;	// One span to terminate each column
		const BYTE *data_p;
		bool newspan;
		int x, y;

		data_p = pixels;

		// Count the number of spans in this texture
		for (x = numcols; x > 0; --x)
		{
			newspan = true;
			for (y = numrows; y > 0; --y)
			{
				if (*data_p++ == 0)
				{
					if (!newspan)
					{
						newspan = true;
					}
				}
				else if (newspan)
				{
					newspan = false;
					numspans++;
				}
			}
		}

		// Allocate space for the spans
		spans = (Span **)M_Malloc (sizeof(Span*)*numcols + sizeof(Span)*numspans);

		// Fill in the spans
		for (x = 0, span = (Span *)&spans[numcols], data_p = pixels; x < numcols; ++x)
		{
			newspan = true;
			spans[x] = span;
			for (y = 0; y < numrows; ++y)
			{
				if (*data_p++ == 0)
				{
					if (!newspan)
					{
						newspan = true;
						span++;
					}
				}
				else
				{
					if (newspan)
					{
						newspan = false;
						span->TopOffset = y;
						span->Length = 1;
					}
					else
					{
						span->Length++;
					}
				}
			}
			if (!newspan)
			{
				span++;
			}
			span->TopOffset = 0;
			span->Length = 0;
			span++;
		}
	}
	return spans;
}

void FTexture::FreeSpans (Span **spans) const
{
	M_Free (spans);
}

void FTexture::GenerateBgraFromBitmap(const FBitmap &bitmap)
{
	CreatePixelsBgraWithMipmaps();

	// Transpose
	const uint32_t *src = (const uint32_t *)bitmap.GetPixels();
	uint32_t *dest = PixelsBgra.data();
	for (int x = 0; x < Width; x++)
	{
		for (int y = 0; y < Height; y++)
		{
			dest[y + x * Height] = src[x + y * Width];
		}
	}

	GenerateBgraMipmaps();
}

void FTexture::CreatePixelsBgraWithMipmaps()
{
	int levels = MipmapLevels();
	int buffersize = 0;
	for (int i = 0; i < levels; i++)
	{
		int w = MAX(Width >> i, 1);
		int h = MAX(Height >> i, 1);
		buffersize += w * h;
	}
	PixelsBgra.resize(buffersize, 0xffff0000);
}

int FTexture::MipmapLevels() const
{
	int widthbits = 0;
	while ((Width >> widthbits) != 0) widthbits++;

	int heightbits = 0;
	while ((Height >> heightbits) != 0) heightbits++;

	return MAX(widthbits, heightbits);
}

void FTexture::GenerateBgraMipmaps()
{
	struct Color4f
	{
		float a, r, g, b;
		Color4f operator*(const Color4f &v) const { return Color4f{ a * v.a, r * v.r, g * v.g, b * v.b }; }
		Color4f operator/(const Color4f &v) const { return Color4f{ a / v.a, r / v.r, g / v.g, b / v.b }; }
		Color4f operator+(const Color4f &v) const { return Color4f{ a + v.a, r + v.r, g + v.g, b + v.b }; }
		Color4f operator-(const Color4f &v) const { return Color4f{ a - v.a, r - v.r, g - v.g, b - v.b }; }
		Color4f operator*(float s) const { return Color4f{ a * s, r * s, g * s, b * s }; }
		Color4f operator/(float s) const { return Color4f{ a / s, r / s, g / s, b / s }; }
		Color4f operator+(float s) const { return Color4f{ a + s, r + s, g + s, b + s }; }
		Color4f operator-(float s) const { return Color4f{ a - s, r - s, g - s, b - s }; }
	};

	int levels = MipmapLevels();
	std::vector<Color4f> image(PixelsBgra.size());

	// Convert to normalized linear colorspace
	{
		for (int x = 0; x < Width; x++)
		{
			for (int y = 0; y < Height; y++)
			{
				uint32_t c8 = PixelsBgra[x * Height + y];
				Color4f c;
				c.a = powf(APART(c8) * (1.0f / 255.0f), 2.2f);
				c.r = powf(RPART(c8) * (1.0f / 255.0f), 2.2f);
				c.g = powf(GPART(c8) * (1.0f / 255.0f), 2.2f);
				c.b = powf(BPART(c8) * (1.0f / 255.0f), 2.2f);
				image[x * Height + y] = c;
			}
		}
	}

	// Generate mipmaps
	{
		std::vector<Color4f> smoothed(Width * Height);
		Color4f *src = image.data();
		Color4f *dest = src + Width * Height;
		for (int i = 1; i < levels; i++)
		{
			int srcw = MAX(Width >> (i - 1), 1);
			int srch = MAX(Height >> (i - 1), 1);
			int w = MAX(Width >> i, 1);
			int h = MAX(Height >> i, 1);

			// Downscale
			for (int x = 0; x < w; x++)
			{
				int sx0 = x * 2;
				int sx1 = MIN((x + 1) * 2, srcw - 1);
				for (int y = 0; y < h; y++)
				{
					int sy0 = y * 2;
					int sy1 = MIN((y + 1) * 2, srch - 1);

					Color4f src00 = src[sy0 + sx0 * srch];
					Color4f src01 = src[sy1 + sx0 * srch];
					Color4f src10 = src[sy0 + sx1 * srch];
					Color4f src11 = src[sy1 + sx1 * srch];
					Color4f c = (src00 + src01 + src10 + src11) * 0.25f;

					dest[y + x * h] = c;
				}
			}

			// Sharpen filter with a 3x3 kernel:
			for (int x = 0; x < w; x++)
			{
				for (int y = 0; y < h; y++)
				{
					Color4f c = { 0.0f, 0.0f, 0.0f, 0.0f };
					for (int kx = -1; kx < 2; kx++)
					{
						for (int ky = -1; ky < 2; ky++)
						{
							int a = y + ky;
							int b = x + kx;
							if (a < 0) a = h - 1;
							if (a == h) a = 0;
							if (b < 0) b = w - 1;
							if (b == w) b = 0;
							c = c + dest[a + b * h];
						}
					}
					c = c * (1.0f / 9.0f);
					smoothed[y + x * h] = c;
				}
			}
			float k = 0.08f;
			for (int j = 0; j < w * h; j++)
				dest[j] = dest[j] + (dest[j] - smoothed[j]) * k;

			src = dest;
			dest += w * h;
		}
	}

	// Convert to bgra8 sRGB colorspace
	{
		Color4f *src = image.data() + Width * Height;
		uint32_t *dest = PixelsBgra.data() + Width * Height;
		for (int i = 1; i < levels; i++)
		{
			int w = MAX(Width >> i, 1);
			int h = MAX(Height >> i, 1);
			for (int j = 0; j < w * h; j++)
			{
				uint32_t a = (uint32_t)clamp(powf(MAX(src[j].a, 0.0f), 1.0f / 2.2f) * 255.0f + 0.5f, 0.0f, 255.0f);
				uint32_t r = (uint32_t)clamp(powf(MAX(src[j].r, 0.0f), 1.0f / 2.2f) * 255.0f + 0.5f, 0.0f, 255.0f);
				uint32_t g = (uint32_t)clamp(powf(MAX(src[j].g, 0.0f), 1.0f / 2.2f) * 255.0f + 0.5f, 0.0f, 255.0f);
				uint32_t b = (uint32_t)clamp(powf(MAX(src[j].b, 0.0f), 1.0f / 2.2f) * 255.0f + 0.5f, 0.0f, 255.0f);
				dest[j] = (a << 24) | (r << 16) | (g << 8) | b;
			}
			src += w * h;
			dest += w * h;
		}
	}
}

void FTexture::GenerateBgraMipmapsFast()
{
	uint32_t *src = PixelsBgra.data();
	uint32_t *dest = src + Width * Height;
	int levels = MipmapLevels();
	for (int i = 1; i < levels; i++)
	{
		int srcw = MAX(Width >> (i - 1), 1);
		int srch = MAX(Height >> (i - 1), 1);
		int w = MAX(Width >> i, 1);
		int h = MAX(Height >> i, 1);

		for (int x = 0; x < w; x++)
		{
			int sx0 = x * 2;
			int sx1 = MIN((x + 1) * 2, srcw - 1);

			for (int y = 0; y < h; y++)
			{
				int sy0 = y * 2;
				int sy1 = MIN((y + 1) * 2, srch - 1);

				uint32_t src00 = src[sy0 + sx0 * srch];
				uint32_t src01 = src[sy1 + sx0 * srch];
				uint32_t src10 = src[sy0 + sx1 * srch];
				uint32_t src11 = src[sy1 + sx1 * srch];

				uint32_t alpha = (APART(src00) + APART(src01) + APART(src10) + APART(src11) + 2) / 4;
				uint32_t red = (RPART(src00) + RPART(src01) + RPART(src10) + RPART(src11) + 2) / 4;
				uint32_t green = (GPART(src00) + GPART(src01) + GPART(src10) + GPART(src11) + 2) / 4;
				uint32_t blue = (BPART(src00) + BPART(src01) + BPART(src10) + BPART(src11) + 2) / 4;

				dest[y + x * h] = (alpha << 24) | (red << 16) | (green << 8) | blue;
			}
		}

		src = dest;
		dest += w * h;
	}
}

void FTexture::CopyToBlock (BYTE *dest, int dwidth, int dheight, int xpos, int ypos, int rotate, const BYTE *translation)
{
	const BYTE *pixels = GetPixels();
	int srcwidth = Width;
	int srcheight = Height;
	int step_x = Height;
	int step_y = 1;
	FClipRect cr = {0, 0, dwidth, dheight};

	if (ClipCopyPixelRect(&cr, xpos, ypos, pixels, srcwidth, srcheight, step_x, step_y, rotate))
	{
		dest += ypos + dheight * xpos;
		if (translation == NULL)
		{
			for (int x = 0; x < srcwidth; x++)
			{
				int pos = x * dheight;
				for (int y = 0; y < srcheight; y++, pos++)
				{
					// the optimizer is doing a good enough job here so there's no need to optimize this by hand
					BYTE v = pixels[y * step_y + x * step_x]; 
					if (v != 0) dest[pos] = v;
				}
			}
		}
		else
		{
			for (int x = 0; x < srcwidth; x++)
			{
				int pos = x * dheight;
				for (int y = 0; y < srcheight; y++, pos++)
				{
					BYTE v = pixels[y * step_y + x * step_x]; 
					if (v != 0) dest[pos] = translation[v];
				}
			}
		}
	}
}

// Converts a texture between row-major and column-major format
// by flipping it about the X=Y axis.

void FTexture::FlipSquareBlock (BYTE *block, int x, int y)
{
	int i, j;

	if (x != y) return;

	for (i = 0; i < x; ++i)
	{
		BYTE *corner = block + x*i + i;
		int count = x - i;
		if (count & 1)
		{
			count--;
			swapvalues<BYTE> (corner[count], corner[count*x]);
		}
		for (j = 0; j < count; j += 2)
		{
			swapvalues<BYTE> (corner[j], corner[j*x]);
			swapvalues<BYTE> (corner[j+1], corner[(j+1)*x]);
		}
	}
}

void FTexture::FlipSquareBlockBgra(uint32_t *block, int x, int y)
{
	int i, j;

	if (x != y) return;

	for (i = 0; i < x; ++i)
	{
		uint32_t *corner = block + x*i + i;
		int count = x - i;
		if (count & 1)
		{
			count--;
			swapvalues<uint32_t>(corner[count], corner[count*x]);
		}
		for (j = 0; j < count; j += 2)
		{
			swapvalues<uint32_t>(corner[j], corner[j*x]);
			swapvalues<uint32_t>(corner[j + 1], corner[(j + 1)*x]);
		}
	}
}

void FTexture::FlipSquareBlockRemap (BYTE *block, int x, int y, const BYTE *remap)
{
	int i, j;
	BYTE t;

	if (x != y) return;

	for (i = 0; i < x; ++i)
	{
		BYTE *corner = block + x*i + i;
		int count = x - i;
		if (count & 1)
		{
			count--;
			t = remap[corner[count]];
			corner[count] = remap[corner[count*x]];
			corner[count*x] = t;
		}
		for (j = 0; j < count; j += 2)
		{
			t = remap[corner[j]];
			corner[j] = remap[corner[j*x]];
			corner[j*x] = t;
			t = remap[corner[j+1]];
			corner[j+1] = remap[corner[(j+1)*x]];
			corner[(j+1)*x] = t;
		}
	}
}

void FTexture::FlipNonSquareBlock (BYTE *dst, const BYTE *src, int x, int y, int srcpitch)
{
	int i, j;

	for (i = 0; i < x; ++i)
	{
		for (j = 0; j < y; ++j)
		{
			dst[i*y+j] = src[i+j*srcpitch];
		}
	}
}

void FTexture::FlipNonSquareBlockBgra(uint32_t *dst, const uint32_t *src, int x, int y, int srcpitch)
{
	int i, j;

	for (i = 0; i < x; ++i)
	{
		for (j = 0; j < y; ++j)
		{
			dst[i*y + j] = src[i + j*srcpitch];
		}
	}
}

void FTexture::FlipNonSquareBlockRemap (BYTE *dst, const BYTE *src, int x, int y, int srcpitch, const BYTE *remap)
{
	int i, j;

	for (i = 0; i < x; ++i)
	{
		for (j = 0; j < y; ++j)
		{
			dst[i*y+j] = remap[src[i+j*srcpitch]];
		}
	}
}

FNativeTexture *FTexture::GetNative(bool wrapping)
{
	if (Native != NULL)
	{
		if (!Native->CheckWrapping(wrapping))
		{ // Texture's wrapping mode is not compatible.
		  // Destroy it and get a new one.
			delete Native;
		}
		else
		{
			if (CheckModified())
			{
				Native->Update();
			}
			return Native;
		}
	}
	Native = screen->CreateTexture(this, wrapping);
	return Native;
}

void FTexture::KillNative()
{
	if (Native != NULL)
	{
		delete Native;
		Native = NULL;
	}
}

// For this generic implementation, we just call GetPixels and copy that data
// to the buffer. Texture formats that can do better than paletted images
// should provide their own implementation that may preserve the original
// color data. Note that the buffer expects row-major data, since that's
// generally more convenient for any non-Doom image formats, and it doesn't
// need to be used by any of Doom's column drawing routines.
void FTexture::FillBuffer(BYTE *buff, int pitch, int height, FTextureFormat fmt)
{
	const BYTE *pix;
	int x, y, w, h, stride;

	w = GetWidth();
	h = GetHeight();

	switch (fmt)
	{
	case TEX_Pal:
	case TEX_Gray:
		pix = GetPixels();
		stride = pitch - w;
		for (y = 0; y < h; ++y)
		{
			const BYTE *pix2 = pix;
			for (x = 0; x < w; ++x)
			{
				*buff++ = *pix2;
				pix2 += h;
			}
			pix++;
			buff += stride;
		}
		break;

	case TEX_RGB:
	{
		FCopyInfo inf = {OP_OVERWRITE, BLEND_NONE, {0}, 0, 0};
		FBitmap bmp(buff, pitch, pitch/4, height);
		CopyTrueColorPixels(&bmp, 0, 0, 0, &inf); 
		break;
	}

	default:
		I_Error("FTexture::FillBuffer: Unsupported format %d", fmt);
	}
}

//===========================================================================
//
// FTexture::CopyTrueColorPixels 
//
// this is the generic case that can handle
// any properly implemented texture for software rendering.
// Its drawback is that it is limited to the base palette which is
// why all classes that handle different palettes should subclass this
// method
//
//===========================================================================

int FTexture::CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate, FCopyInfo *inf)
{
	PalEntry *palette = screen->GetPalette();
	for(int i=1;i<256;i++) palette[i].a = 255;	// set proper alpha values
	bmp->CopyPixelData(x, y, GetPixels(), Width, Height, Height, 1, rotate, palette, inf);
	for(int i=1;i<256;i++) palette[i].a = 0;
	return 0;
}

int FTexture::CopyTrueColorTranslated(FBitmap *bmp, int x, int y, int rotate, FRemapTable *remap, FCopyInfo *inf)
{
	PalEntry *palette = remap->Palette;
	bmp->CopyPixelData(x, y, GetPixels(), Width, Height, Height, 1, rotate, palette, inf);
	return 0;
}

bool FTexture::UseBasePalette() 
{ 
	return true; 
}

FTexture *FTexture::GetRedirect(bool wantwarped)
{
	return this;
}

FTexture *FTexture::GetRawTexture()
{
	return this;
}

void FTexture::SetScaledSize(int fitwidth, int fitheight)
{
	Scale.X = double(Width) / fitwidth;
	Scale.Y =double(Height) / fitheight;
	// compensate for roundoff errors
	if (int(Scale.X * fitwidth) != Width) Scale.X += (1 / 65536.);
	if (int(Scale.Y * fitheight) != Height) Scale.Y += (1 / 65536.);
}

//===========================================================================
// 
//	Gets the average color of a texture for use as a sky cap color
//
//===========================================================================

namespace
{
	PalEntry averageColor(const DWORD *data, int size, int maxout)
	{
		int				i;
		unsigned int	r, g, b;

		// First clear them.
		r = g = b = 0;
		if (size == 0)
		{
			return PalEntry(255, 255, 255);
		}
		for (i = 0; i < size; i++)
		{
			b += BPART(data[i]);
			g += GPART(data[i]);
			r += RPART(data[i]);
		}

		r = r / size;
		g = g / size;
		b = b / size;

		int maxv = MAX(MAX(r, g), b);

		if (maxv && maxout)
		{
			r = Scale(r, maxout, maxv);
			g = Scale(g, maxout, maxv);
			b = Scale(b, maxout, maxv);
		}
		return PalEntry(255, r, g, b);
	}
}

PalEntry FTexture::GetSkyCapColor(bool bottom)
{
	PalEntry col;

	if (!bSWSkyColorDone)
	{
		bSWSkyColorDone = true;

		FBitmap bitmap;
		bitmap.Create(GetWidth(), GetHeight());
		CopyTrueColorPixels(&bitmap, 0, 0);
		int w = GetWidth();
		int h = GetHeight();

		const uint32_t *buffer = (const uint32_t *)bitmap.GetPixels();
		if (buffer)
		{
			CeilingSkyColor = averageColor((DWORD *)buffer, w * MIN(30, h), 0);
			if (h>30)
			{
				FloorSkyColor = averageColor(((DWORD *)buffer) + (h - 30)*w, w * 30, 0);
			}
			else FloorSkyColor = CeilingSkyColor;
		}
	}
	return bottom ? FloorSkyColor : CeilingSkyColor;
}


FDummyTexture::FDummyTexture ()
{
	Width = 64;
	Height = 64;
	HeightBits = 6;
	WidthBits = 6;
	WidthMask = 63;
	UseType = TEX_Null;
}

void FDummyTexture::SetSize (int width, int height)
{
	Width = width;
	Height = height;
	CalcBitSize ();
}

// This must never be called
const BYTE *FDummyTexture::GetColumn (unsigned int column, const Span **spans_out)
{
	return NULL;
}

// And this also must never be called
const BYTE *FDummyTexture::GetPixels ()
{
	return NULL;
}

//==========================================================================
//
// Debug stuff
//
//==========================================================================

#ifdef _DEBUG
// Prints the spans generated for a texture. Only needed for debugging.
CCMD (printspans)
{
	if (argv.argc() != 2)
		return;

	FTextureID picnum = TexMan.CheckForTexture (argv[1], FTexture::TEX_Any);
	if (!picnum.Exists())
	{
		Printf ("Unknown texture %s\n", argv[1]);
		return;
	}
	FTexture *tex = TexMan[picnum];
	for (int x = 0; x < tex->GetWidth(); ++x)
	{
		const FTexture::Span *spans;
		Printf ("%4d:", x);
		tex->GetColumn (x, &spans);
		while (spans->Length != 0)
		{
			Printf (" (%4d,%4d)", spans->TopOffset, spans->TopOffset+spans->Length-1);
			spans++;
		}
		Printf ("\n");
	}
}
#endif
