Example #1
0
// Create image saved in file
int Level::CreateImage(const STRING & file)
{
	if (cancel)
		return LVL_ERR_CANCEL;
	state = CREATEIMAGE;
	if (chunks.empty())
		return LVL_ERR_NOCHUNKS;
	INT64 minX = MAPCOORD;
	INT64 minY = MAPCOORD;
	INT64 maxX = -MAPCOORD;
	INT64 maxY = -MAPCOORD;

	blocks = chunks.size() << 8;
	bool doRender = !(prefs.flags & CHUNKP_NORENDER);

	// Calculate size
	for (list<Chunk *>::iterator i = chunks.begin(); i != chunks.end(); ++i)
	{
		Chunk * chunk = (*i);
		if (!chunk->isValid())
			continue;
		if (doRender)
		{
			COORDS x = chunk->GetX();
			COORDS y = chunk->GetY();
			if (x < minX)
				minX = x;
			if (y < minY)
				minY = y;
			if (x > maxX)
				maxX = x;
			if (y > maxY)
				maxY = y;
		}

		// And add to amount while we are processing
		amount += chunk->amount;
	}

	if (cancel)
		return LVL_ERR_CANCEL;

	if (doRender)
	{
		// No valid chunks?
		if (minX == MAPCOORD && minY == MAPCOORD &&
			maxX == -MAPCOORD && maxY == -MAPCOORD)
			return LVL_ERR_INVALIDCHUNKS;

		prefs.rotation = abs((prefs.rotation / 90) * 90) % 360;
		float rad = (2 * 3.14159265f * prefs.rotation) / 360;

		UINT
			maxWidth = (UINT)(maxX - minX),
			maxHeight = (UINT)(maxY - minY);

		UINT
			width = (maxWidth + 1) * MAPX, 
			height = (maxHeight + 1) * MAPY;

		// Foolproof
		if (width == 0 || height == 0)
			return LVL_ERR_INVALIDSIZE;

		// Rotate
		{
			float rCos = cos(rad);
			float rSin = sin(rad);
			rCos = (rCos < 0) ? -rCos : rCos;
			rSin = (rSin < 0) ? -rSin : rSin;
			UINT newWidth = UINT(width * rCos + height * rSin);
			UINT newHeight = UINT(height * rCos + width * rSin);
			width = newWidth;
			height = newHeight;
		}

		// Foolproof
		if (width == 0 || height == 0)
			return LVL_ERR_INVALIDSIZE;

		bool useCache = !prefs.cache.empty();
		ImageCache cache(height, width);
		Image * image = 0;

		if (!useCache)
		{
#ifndef min
#define min(a,b) (((a) < (b)) ? (a) : (b))
#endif
			// No more than 1 GiB
			if (width * height * sizeof(Color) > min(1073741824, Computer::GetAvailableMemory()))
				return LVL_ERR_TOOLARGE;
			try
			{
				image = new Image(height, width);
				if (image == 0)
					throw LVL_ERR_TOOLARGE;
			}
			catch (Level_Error error)
			{
				return error;
			}
		}
		else
		{
			STRING cacheFile = prefs.cache;
			cacheFile.append(STR("\\cache.bin"));
			cacheFile = Port::ReplacePath(cacheFile);

			if (!cache.Open(cacheFile))
				return LVL_ERR_CACHE;
		}
		// Sort list
		//chunks.sort(compare_chunks);
		// Found duplicates
		//if (amountDuplicates > 0)
		//	return LVL_ERR_DUPLICATES;

		// Create image
		int n = 0;
		for (list<Chunk *>::iterator i = chunks.begin(); i != chunks.end(); ++i, ++n)
		{
			Chunk * chunk = (*i);
			if (!chunk->isValid())
				continue;

			Image * img = chunk->GetImage();
			// One more try
			if (!img)
			{
				chunk->pref = prefs;
				chunk->CreateImage();
				img = chunk->GetImage();
				if (!img)
					continue;
			}

			UINT x = 0;
			UINT y = 0;
			switch (prefs.rotation)
			{
				case 90:
					x = (width - (UINT(chunk->GetY() - minY) * (MAPY))) - MAPY;
					y = UINT(chunk->GetX() - minX) * MAPX;
					break;
				case 180:
					x = (width - (UINT(chunk->GetX() - minX) * (MAPX))) - MAPX;
					y = (height - (UINT(chunk->GetY() - minY) * (MAPY))) - MAPY;
					break;
				case 270:
					x = UINT(chunk->GetY() - minY) * MAPY;
					y = (height - (UINT(chunk->GetX() - minX) * (MAPX))) - MAPX;
					break;
				case 0:
				default:
					x = UINT(chunk->GetX() - minX) * MAPX;
					y = UINT(chunk->GetY() - minY) * MAPY;
			}

			Color chnk[MAPX][MAPY] = {0};

			// Write pixel for pixel
			for (UINT X = 0; X < MAPX; ++X)
			{
				for (UINT Y = 0; Y < MAPY; ++Y)
				{
					UINT XX = 0;
					UINT YY = 0;
					switch (prefs.rotation)
					{
						case 90:
							XX = Y;
							YY = (MAPX - 1) - X;
							break;
						case 180:
							XX = (MAPX - 1) - X;
							YY = (MAPY - 1) - Y;
							break;
						case 270:
							XX = (MAPY - 1) - Y;
							YY = X;
							break;
						case 0:
						default:
							XX = X;
							YY = Y;
					}
					if (!useCache)
					{
						image->SetPixel((height - (y + Y)) - 1, (x + X), img->GetPixel(YY, XX));
					}
					else
					{
						Color * current = (*chnk) + ((MAPY - (Y+1)) + (MAPY * X));
						Color c = img->GetPixel(YY, XX);
						memcpy(current, &c, sizeof(Color));
					}
				}
			}
			if (useCache)
			{
				for (UINT X = 0; X < MAPX; ++X)
				{
					cache.Write(chnk[X], (height - y) - MAPY, (x + X), MAPY);
				}
			}

			done = ((float)n/(float)total);
		}
		done = 1;

		// No render mode
		if (prefs.flags & CHUNKP_NORENDER)
		{
			saved = true;
			if (!useCache)
			{
				delete image;
			}
			else
			{
				cache.Close();
			}
			state = FINALIZING;
			return LVL_OK;
		}
		// Save image
		state = SAVING;

		// Normal save of image
		if (!useCache)
		{
			if (image->PrepareSave(file))
			{
				for (UINT i = 0; i < width; ++i)
				{
					if (!image->SaveRow(i))
						break;
					done = (float)i/(float)width;
				}
				saved = image->CloseSave();
			}
			done = 1;
			delete image;
		}
		// Save from cache
		else
		{
			ImageSave sav;
			if (sav.Prepare(file, height, width))
			{
				Color * line = new Color[height];
				for (UINT i = 0; i < width; ++i)
				{
					cache.Read(&line, 0, i, height);
					if (!sav.Row(line))
						break;
					done = (float)i/(float)width;
				}
				delete[] line;
				saved = sav.Close();
			}
			cache.Close();
			done = 1;
		}
	}
	else
	{
		saved = true;
	}
	state = FINALIZING;
	return saved ? LVL_OK : LVL_ERR_SAVE;
}